2 * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
7 * @fileOverview The "filebrowser" plugin that adds support for file uploads and
10 * When a file is uploaded or selected inside the file browser, its URL is
11 * inserted automatically into a field defined in the <code>filebrowser</code>
12 * attribute. In order to specify a field that should be updated, pass the tab ID and
13 * the element ID, separated with a colon.<br /><br />
15 * <strong>Example 1: (Browse)</strong>
21 * filebrowser : 'tabId:elementId',
22 * label : editor.lang.common.browseServer
26 * If you set the <code>filebrowser</code> attribute for an element other than
27 * the <code>fileButton</code>, the <code>Browse</code> action will be triggered.<br /><br />
29 * <strong>Example 2: (Quick Upload)</strong>
33 * type : 'fileButton',
34 * id : 'uploadButton',
35 * filebrowser : 'tabId:elementId',
36 * label : editor.lang.common.uploadSubmit,
37 * 'for' : [ 'upload', 'upload' ]
41 * If you set the <code>filebrowser</code> attribute for a <code>fileButton</code>
42 * element, the <code>QuickUpload</code> action will be executed.<br /><br />
44 * The filebrowser plugin also supports more advanced configuration performed through
45 * a JavaScript object.
47 * The following settings are supported:
50 * <li><code>action</code> – <code>Browse</code> or <code>QuickUpload</code>.</li>
51 * <li><code>target</code> – the field to update in the <code><em>tabId:elementId</em></code> format.</li>
52 * <li><code>params</code> – additional arguments to be passed to the server connector (optional).</li>
53 * <li><code>onSelect</code> – a function to execute when the file is selected/uploaded (optional).</li>
54 * <li><code>url</code> – the URL to be called (optional).</li>
57 * <strong>Example 3: (Quick Upload)</strong>
61 * type : 'fileButton',
62 * label : editor.lang.common.uploadSubmit,
66 * action : 'QuickUpload', // required
67 * target : 'tab1:elementId', // required
68 * params : // optional
71 * currentFolder : '/folder/'
73 * onSelect : function( fileUrl, errorMessage ) // optional
75 * // Do not call the built-in selectFuntion.
79 * 'for' : [ 'tab1', 'myFile' ]
83 * Suppose you have a file element with an ID of <code>myFile</code>, a text
84 * field with an ID of <code>elementId</code> and a <code>fileButton</code>.
85 * If the <code>filebowser.url</code> attribute is not specified explicitly,
86 * the form action will be set to <code>filebrowser[<em>DialogWindowName</em>]UploadUrl</code>
87 * or, if not specified, to <code>filebrowserUploadUrl</code>. Additional parameters
88 * from the <code>params</code> object will be added to the query string. It is
89 * possible to create your own <code>uploadHandler</code> and cancel the built-in
90 * <code>updateTargetElement</code> command.<br /><br />
92 * <strong>Example 4: (Browse)</strong>
98 * label : editor.lang.common.browseServer,
102 * url : '/ckfinder/ckfinder.html&type=Images',
103 * target : 'tab1:elementId'
108 * In this example, when the button is pressed, the file browser will be opened in a
109 * popup window. If you do not specify the <code>filebrowser.url</code> attribute,
110 * <code>filebrowser[<em>DialogName</em>]BrowseUrl</code> or
111 * <code>filebrowserBrowseUrl</code> will be used. After selecting a file in the file
112 * browser, an element with an ID of <code>elementId</code> will be updated. Just
113 * like in the third example, a custom <code>onSelect</code> function may be defined.
117 // Default input element name for CSRF protection token.
118 var TOKEN_INPUT_NAME
= 'ckCsrfToken';
120 // Adds (additional) arguments to given url.
125 // params Additional parameters.
126 function addQueryString( url
, params
) {
127 var queryString
= [];
132 for ( var i
in params
)
133 queryString
.push( i
+ '=' + encodeURIComponent( params
[ i
] ) );
136 return url
+ ( ( url
.indexOf( '?' ) != -1 ) ? '&' : '?' ) + queryString
.join( '&' );
139 // Make a string's first character uppercase.
143 function ucFirst( str
) {
145 var f
= str
.charAt( 0 ).toUpperCase();
146 return f
+ str
.substr( 1 );
149 // The onlick function assigned to the 'Browse Server' button. Opens the
150 // file browser and updates target field when file is selected.
152 // @param {CKEDITOR.event}
153 // evt The event object.
154 function browseServer() {
155 var dialog
= this.getDialog();
156 var editor
= dialog
.getParentEditor();
158 editor
._
.filebrowserSe
= this;
160 var width
= editor
.config
[ 'filebrowser' + ucFirst( dialog
.getName() ) + 'WindowWidth' ] || editor
.config
.filebrowserWindowWidth
|| '80%';
161 var height
= editor
.config
[ 'filebrowser' + ucFirst( dialog
.getName() ) + 'WindowHeight' ] || editor
.config
.filebrowserWindowHeight
|| '70%';
163 var params
= this.filebrowser
.params
|| {};
164 params
.CKEditor
= editor
.name
;
165 params
.CKEditorFuncNum
= editor
._
.filebrowserFn
;
166 if ( !params
.langCode
)
167 params
.langCode
= editor
.langCode
;
169 var url
= addQueryString( this.filebrowser
.url
, params
);
170 // TODO: V4: Remove backward compatibility (#8163).
171 editor
.popup( url
, width
, height
, editor
.config
.filebrowserWindowFeatures
|| editor
.config
.fileBrowserWindowFeatures
);
174 // Appends token preventing CSRF attacks to the form of provided file input.
177 // @param {CKEDITOR.dom.element} fileInput
178 function appendToken( fileInput
) {
180 var form
= new CKEDITOR
.dom
.element( fileInput
.$.form
);
183 // Check if token input element already exists.
184 tokenElement
= form
.$.elements
[ TOKEN_INPUT_NAME
];
186 // Create new if needed.
187 if ( !tokenElement
) {
188 tokenElement
= new CKEDITOR
.dom
.element( 'input' );
189 tokenElement
.setAttributes( {
190 name: TOKEN_INPUT_NAME
,
194 form
.append( tokenElement
);
196 tokenElement
= new CKEDITOR
.dom
.element( tokenElement
);
199 tokenElement
.setAttribute( 'value', CKEDITOR
.tools
.getCsrfToken() );
203 // The onlick function assigned to the 'Upload' button. Makes the final
204 // decision whether form is really submitted and updates target field when
207 // @param {CKEDITOR.event}
208 // evt The event object.
209 function uploadFile() {
210 var dialog
= this.getDialog();
211 var editor
= dialog
.getParentEditor();
213 editor
._
.filebrowserSe
= this;
215 // If user didn't select the file, stop the upload.
216 if ( !dialog
.getContentElement( this[ 'for' ][ 0 ], this[ 'for' ][ 1 ] ).getInputElement().$.value
)
219 if ( !dialog
.getContentElement( this[ 'for' ][ 0 ], this[ 'for' ][ 1 ] ).getAction() )
225 // Setups the file element.
227 // @param {CKEDITOR.ui.dialog.file}
228 // fileInput The file element used during file upload.
230 // filebrowser Object containing filebrowser settings assigned to
231 // the fileButton associated with this file element.
232 function setupFileElement( editor
, fileInput
, filebrowser
) {
233 var params
= filebrowser
.params
|| {};
234 params
.CKEditor
= editor
.name
;
235 params
.CKEditorFuncNum
= editor
._
.filebrowserFn
;
236 if ( !params
.langCode
)
237 params
.langCode
= editor
.langCode
;
239 fileInput
.action
= addQueryString( filebrowser
.url
, params
);
240 fileInput
.filebrowser
= filebrowser
;
243 // Traverse through the content definition and attach filebrowser to
244 // elements with 'filebrowser' attribute.
247 // dialogName Dialog name.
248 // @param {CKEDITOR.dialog.definitionObject}
249 // definition Dialog definition.
251 // elements Array of {@link CKEDITOR.dialog.definition.content}
253 function attachFileBrowser( editor
, dialogName
, definition
, elements
) {
254 if ( !elements
|| !elements
.length
)
259 for ( var i
= elements
.length
; i
--; ) {
260 element
= elements
[ i
];
262 if ( element
.type
== 'hbox' || element
.type
== 'vbox' || element
.type
== 'fieldset' )
263 attachFileBrowser( editor
, dialogName
, definition
, element
.children
);
265 if ( !element
.filebrowser
)
268 if ( typeof element
.filebrowser
== 'string' ) {
270 action: ( element
.type
== 'fileButton' ) ? 'QuickUpload' : 'Browse',
271 target: element
.filebrowser
273 element
.filebrowser
= fb
;
276 if ( element
.filebrowser
.action
== 'Browse' ) {
277 var url
= element
.filebrowser
.url
;
278 if ( url
=== undefined ) {
279 url
= editor
.config
[ 'filebrowser' + ucFirst( dialogName
) + 'BrowseUrl' ];
280 if ( url
=== undefined )
281 url
= editor
.config
.filebrowserBrowseUrl
;
285 element
.onClick
= browseServer
;
286 element
.filebrowser
.url
= url
;
287 element
.hidden
= false;
289 } else if ( element
.filebrowser
.action
== 'QuickUpload' && element
[ 'for' ] ) {
290 url
= element
.filebrowser
.url
;
291 if ( url
=== undefined ) {
292 url
= editor
.config
[ 'filebrowser' + ucFirst( dialogName
) + 'UploadUrl' ];
293 if ( url
=== undefined )
294 url
= editor
.config
.filebrowserUploadUrl
;
298 var onClick
= element
.onClick
;
299 element
.onClick = function( evt
) {
300 // "element" here means the definition object, so we need to find the correct
301 // button to scope the event call
302 var sender
= evt
.sender
;
303 if ( onClick
&& onClick
.call( sender
, evt
) === false )
306 if ( uploadFile
.call( sender
, evt
) ) {
307 var fileInput
= sender
.getDialog().getContentElement( this[ 'for' ][ 0 ], this[ 'for' ][ 1 ] ).getInputElement();
309 // Append token preventing CSRF attacks.
310 appendToken( fileInput
);
318 element
.filebrowser
.url
= url
;
319 element
.hidden
= false;
320 setupFileElement( editor
, definition
.getContents( element
[ 'for' ][ 0 ] ).get( element
[ 'for' ][ 1 ] ), element
.filebrowser
);
326 // Updates the target element with the url of uploaded/selected file.
329 // url The url of a file.
330 function updateTargetElement( url
, sourceElement
) {
331 var dialog
= sourceElement
.getDialog();
332 var targetElement
= sourceElement
.filebrowser
.target
|| null;
334 // If there is a reference to targetElement, update it.
335 if ( targetElement
) {
336 var target
= targetElement
.split( ':' );
337 var element
= dialog
.getContentElement( target
[ 0 ], target
[ 1 ] );
339 element
.setValue( url
);
340 dialog
.selectPage( target
[ 0 ] );
345 // Returns true if filebrowser is configured in one of the elements.
347 // @param {CKEDITOR.dialog.definitionObject}
348 // definition Dialog definition.
350 // tabId The tab id where element(s) can be found.
352 // elementId The element id (or ids, separated with a semicolon) to check.
353 function isConfigured( definition
, tabId
, elementId
) {
354 if ( elementId
.indexOf( ';' ) !== -1 ) {
355 var ids
= elementId
.split( ';' );
356 for ( var i
= 0; i
< ids
.length
; i
++ ) {
357 if ( isConfigured( definition
, tabId
, ids
[ i
] ) )
363 var elementFileBrowser
= definition
.getContents( tabId
).get( elementId
).filebrowser
;
364 return ( elementFileBrowser
&& elementFileBrowser
.url
);
367 function setUrl( fileUrl
, data
) {
368 var dialog
= this._
.filebrowserSe
.getDialog(),
369 targetInput
= this._
.filebrowserSe
[ 'for' ],
370 onSelect
= this._
.filebrowserSe
.filebrowser
.onSelect
;
373 dialog
.getContentElement( targetInput
[ 0 ], targetInput
[ 1 ] ).reset();
375 if ( typeof data
== 'function' && data
.call( this._
.filebrowserSe
) === false )
378 if ( onSelect
&& onSelect
.call( this._
.filebrowserSe
, fileUrl
, data
) === false )
381 // The "data" argument may be used to pass the error message to the editor.
382 if ( typeof data
== 'string' && data
)
383 alert( data
); // jshint ignore:line
386 updateTargetElement( fileUrl
, this._
.filebrowserSe
);
389 CKEDITOR
.plugins
.add( 'filebrowser', {
391 init: function( editor
) {
392 editor
._
.filebrowserFn
= CKEDITOR
.tools
.addFunction( setUrl
, editor
);
393 editor
.on( 'destroy', function() {
394 CKEDITOR
.tools
.removeFunction( this._
.filebrowserFn
);
399 CKEDITOR
.on( 'dialogDefinition', function( evt
) {
400 // We require filebrowser plugin to be loaded.
401 if ( !evt
.editor
.plugins
.filebrowser
)
404 var definition
= evt
.data
.definition
,
406 // Associate filebrowser to elements with 'filebrowser' attribute.
407 for ( var i
= 0; i
< definition
.contents
.length
; ++i
) {
408 if ( ( element
= definition
.contents
[ i
] ) ) {
409 attachFileBrowser( evt
.editor
, evt
.data
.name
, definition
, element
.elements
);
410 if ( element
.hidden
&& element
.filebrowser
)
411 element
.hidden
= !isConfigured( definition
, element
.id
, element
.filebrowser
);
420 * The location of an external file manager that should be launched when the **Browse Server**
421 * button is pressed. If configured, the **Browse Server** button will appear in the
422 * **Link**, **Image**, and **Flash** dialog windows.
424 * Read more in the [documentation](#!/guide/dev_file_browse_upload)
425 * and see the [SDK sample](http://sdk.ckeditor.com/samples/fileupload.html).
427 * config.filebrowserBrowseUrl = '/browser/browse.php';
430 * @cfg {String} [filebrowserBrowseUrl='' (empty string = disabled)]
431 * @member CKEDITOR.config
435 * The location of the script that handles file uploads.
436 * If set, the **Upload** tab will appear in the **Link**, **Image**,
437 * and **Flash** dialog windows.
439 * Read more in the [documentation](#!/guide/dev_file_browse_upload)
440 * and see the [SDK sample](http://sdk.ckeditor.com/samples/fileupload.html).
442 * config.filebrowserUploadUrl = '/uploader/upload.php';
444 * **Note:** This is a configuration setting for a [file browser/uploader](#!/guide/dev_file_browse_upload).
445 * To configure [uploading dropped or pasted files](#!/guide/dev_file_upload) use the {@link CKEDITOR.config#uploadUrl}
446 * configuration option.
449 * @cfg {String} [filebrowserUploadUrl='' (empty string = disabled)]
450 * @member CKEDITOR.config
454 * The location of an external file manager that should be launched when the **Browse Server**
455 * button is pressed in the **Image** dialog window.
457 * If not set, CKEditor will use {@link CKEDITOR.config#filebrowserBrowseUrl}.
459 * Read more in the [documentation](#!/guide/dev_file_manager_configuration-section-adding-file-manager-scripts-for-selected-dialog-windows)
460 * and see the [SDK sample](http://sdk.ckeditor.com/samples/fileupload.html).
462 * config.filebrowserImageBrowseUrl = '/browser/browse.php?type=Images';
465 * @cfg {String} [filebrowserImageBrowseUrl='' (empty string = disabled)]
466 * @member CKEDITOR.config
470 * The location of an external file browser that should be launched when the **Browse Server**
471 * button is pressed in the **Flash** dialog window.
473 * If not set, CKEditor will use {@link CKEDITOR.config#filebrowserBrowseUrl}.
475 * Read more in the [documentation](#!/guide/dev_file_manager_configuration-section-adding-file-manager-scripts-for-selected-dialog-windows)
476 * and see the [SDK sample](http://sdk.ckeditor.com/samples/fileupload.html).
478 * config.filebrowserFlashBrowseUrl = '/browser/browse.php?type=Flash';
481 * @cfg {String} [filebrowserFlashBrowseUrl='' (empty string = disabled)]
482 * @member CKEDITOR.config
486 * The location of the script that handles file uploads in the **Image** dialog window.
488 * If not set, CKEditor will use {@link CKEDITOR.config#filebrowserUploadUrl}.
490 * Read more in the [documentation](#!/guide/dev_file_manager_configuration-section-adding-file-manager-scripts-for-selected-dialog-windows)
491 * and see the [SDK sample](http://sdk.ckeditor.com/samples/fileupload.html).
493 * config.filebrowserImageUploadUrl = '/uploader/upload.php?type=Images';
495 * **Note:** This is a configuration setting for a [file browser/uploader](#!/guide/dev_file_browse_upload).
496 * To configure [uploading dropped or pasted files](#!/guide/dev_file_upload) use the {@link CKEDITOR.config#uploadUrl}
497 * or {@link CKEDITOR.config#imageUploadUrl} configuration option.
500 * @cfg {String} [filebrowserImageUploadUrl='' (empty string = disabled)]
501 * @member CKEDITOR.config
505 * The location of the script that handles file uploads in the **Flash** dialog window.
507 * If not set, CKEditor will use {@link CKEDITOR.config#filebrowserUploadUrl}.
509 * Read more in the [documentation](#!/guide/dev_file_manager_configuration-section-adding-file-manager-scripts-for-selected-dialog-windows)
510 * and see the [SDK sample](http://sdk.ckeditor.com/samples/fileupload.html).
512 * config.filebrowserFlashUploadUrl = '/uploader/upload.php?type=Flash';
515 * @cfg {String} filebrowserFlashUploadUrl='' (empty string = disabled)]
516 * @member CKEDITOR.config
520 * The location of an external file manager that should be launched when the **Browse Server**
521 * button is pressed in the **Link** tab of the **Image** dialog window.
523 * If not set, CKEditor will use {@link CKEDITOR.config#filebrowserBrowseUrl}.
525 * Read more in the [documentation](#!/guide/dev_file_manager_configuration-section-adding-file-manager-scripts-for-selected-dialog-windows)
526 * and see the [SDK sample](http://sdk.ckeditor.com/samples/fileupload.html).
528 * config.filebrowserImageBrowseLinkUrl = '/browser/browse.php';
531 * @cfg {String} [filebrowserImageBrowseLinkUrl='' (empty string = disabled)]
532 * @member CKEDITOR.config
536 * The features to use in the file manager popup window.
538 * config.filebrowserWindowFeatures = 'resizable=yes,scrollbars=no';
541 * @cfg {String} [filebrowserWindowFeatures='location=no,menubar=no,toolbar=no,dependent=yes,minimizable=no,modal=yes,alwaysRaised=yes,resizable=yes,scrollbars=yes']
542 * @member CKEDITOR.config
546 * The width of the file manager popup window. It can be a number denoting a value in
547 * pixels or a percent string.
549 * Read more in the [documentation](#!/guide/dev_file_manager_configuration-section-file-manager-window-size)
550 * and see the [SDK sample](http://sdk.ckeditor.com/samples/fileupload.html).
552 * config.filebrowserWindowWidth = 750;
554 * config.filebrowserWindowWidth = '50%';
556 * @cfg {Number/String} [filebrowserWindowWidth='80%']
557 * @member CKEDITOR.config
561 * The height of the file manager popup window. It can be a number denoting a value in
562 * pixels or a percent string.
564 * Read more in the [documentation](#!/guide/dev_file_manager_configuration-section-file-manager-window-size)
565 * and see the [SDK sample](http://sdk.ckeditor.com/samples/fileupload.html).
567 * config.filebrowserWindowHeight = 580;
569 * config.filebrowserWindowHeight = '50%';
571 * @cfg {Number/String} [filebrowserWindowHeight='70%']
572 * @member CKEDITOR.config