JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
9af9714eff8f19b85df1328d38779b61e24e764f
[ckeditor.git] / _source / plugins / filebrowser / plugin.js
1 /*\r
2 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.\r
3 For licensing, see LICENSE.html or http://ckeditor.com/license\r
4 */\r
5 \r
6 /**\r
7  * @fileOverview The "filebrowser" plugin, it adds support for file uploads and\r
8  *               browsing.\r
9  *\r
10  * When file is selected inside of the file browser or uploaded, its url is\r
11  * inserted automatically to a field, which is described in the 'filebrowser'\r
12  * attribute. To specify field that should be updated, pass the tab id and\r
13  * element id, separated with a colon.\r
14  *\r
15  * Example 1: (Browse)\r
16  *\r
17  * <pre>\r
18  * {\r
19  *      type : 'button',\r
20  *      id : 'browse',\r
21  *      filebrowser : 'tabId:elementId',\r
22  *      label : editor.lang.common.browseServer\r
23  * }\r
24  * </pre>\r
25  *\r
26  * If you set the 'filebrowser' attribute on any element other than\r
27  * 'fileButton', the 'Browse' action will be triggered.\r
28  *\r
29  * Example 2: (Quick Upload)\r
30  *\r
31  * <pre>\r
32  * {\r
33  *      type : 'fileButton',\r
34  *      id : 'uploadButton',\r
35  *      filebrowser : 'tabId:elementId',\r
36  *      label : editor.lang.common.uploadSubmit,\r
37  *      'for' : [ 'upload', 'upload' ]\r
38  * }\r
39  * </pre>\r
40  *\r
41  * If you set the 'filebrowser' attribute on a fileButton element, the\r
42  * 'QuickUpload' action will be executed.\r
43  *\r
44  * Filebrowser plugin also supports more advanced configuration (through\r
45  * javascript object).\r
46  *\r
47  * The following settings are supported:\r
48  *\r
49  * <pre>\r
50  *  [action] - Browse or QuickUpload\r
51  *  [target] - field to update, tabId:elementId\r
52  *  [params] - additional arguments to be passed to the server connector (optional)\r
53  *  [onSelect] - function to execute when file is selected/uploaded (optional)\r
54  *  [url] - the URL to be called (optional)\r
55  * </pre>\r
56  *\r
57  * Example 3: (Quick Upload)\r
58  *\r
59  * <pre>\r
60  * {\r
61  *      type : 'fileButton',\r
62  *      label : editor.lang.common.uploadSubmit,\r
63  *      id : 'buttonId',\r
64  *      filebrowser :\r
65  *      {\r
66  *              action : 'QuickUpload', //required\r
67  *              target : 'tab1:elementId', //required\r
68  *              params : //optional\r
69  *              {\r
70  *                      type : 'Files',\r
71  *                      currentFolder : '/folder/'\r
72  *              },\r
73  *              onSelect : function( fileUrl, errorMessage ) //optional\r
74  *              {\r
75  *                      // Do not call the built-in selectFuntion\r
76  *                      // return false;\r
77  *              }\r
78  *      },\r
79  *      'for' : [ 'tab1', 'myFile' ]\r
80  * }\r
81  * </pre>\r
82  *\r
83  * Suppose we have a file element with id 'myFile', text field with id\r
84  * 'elementId' and a fileButton. If filebowser.url is not specified explicitly,\r
85  * form action will be set to 'filebrowser[DialogName]UploadUrl' or, if not\r
86  * specified, to 'filebrowserUploadUrl'. Additional parameters from 'params'\r
87  * object will be added to the query string. It is possible to create your own\r
88  * uploadHandler and cancel the built-in updateTargetElement command.\r
89  *\r
90  * Example 4: (Browse)\r
91  *\r
92  * <pre>\r
93  * {\r
94  *      type : 'button',\r
95  *      id : 'buttonId',\r
96  *      label : editor.lang.common.browseServer,\r
97  *      filebrowser :\r
98  *      {\r
99  *              action : 'Browse',\r
100  *              url : '/ckfinder/ckfinder.html&amp;type=Images',\r
101  *              target : 'tab1:elementId'\r
102  *      }\r
103  * }\r
104  * </pre>\r
105  *\r
106  * In this example, after pressing a button, file browser will be opened in a\r
107  * popup. If we don't specify filebrowser.url attribute,\r
108  * 'filebrowser[DialogName]BrowseUrl' or 'filebrowserBrowseUrl' will be used.\r
109  * After selecting a file in a file browser, an element with id 'elementId' will\r
110  * be updated. Just like in the third example, a custom 'onSelect' function may be\r
111  * defined.\r
112  */\r
113 ( function()\r
114 {\r
115         /**\r
116          * Adds (additional) arguments to given url.\r
117          *\r
118          * @param {String}\r
119          *            url The url.\r
120          * @param {Object}\r
121          *            params Additional parameters.\r
122          */\r
123         function addQueryString( url, params )\r
124         {\r
125                 var queryString = [];\r
126 \r
127                 if ( !params )\r
128                         return url;\r
129                 else\r
130                 {\r
131                         for ( var i in params )\r
132                                 queryString.push( i + "=" + encodeURIComponent( params[ i ] ) );\r
133                 }\r
134 \r
135                 return url + ( ( url.indexOf( "?" ) != -1 ) ? "&" : "?" ) + queryString.join( "&" );\r
136         }\r
137 \r
138         /**\r
139          * Make a string's first character uppercase.\r
140          *\r
141          * @param {String}\r
142          *            str String.\r
143          */\r
144         function ucFirst( str )\r
145         {\r
146                 str += '';\r
147                 var f = str.charAt( 0 ).toUpperCase();\r
148                 return f + str.substr( 1 );\r
149         }\r
150 \r
151         /**\r
152          * The onlick function assigned to the 'Browse Server' button. Opens the\r
153          * file browser and updates target field when file is selected.\r
154          *\r
155          * @param {CKEDITOR.event}\r
156          *            evt The event object.\r
157          */\r
158         function browseServer( evt )\r
159         {\r
160                 var dialog = this.getDialog();\r
161                 var editor = dialog.getParentEditor();\r
162 \r
163                 editor._.filebrowserSe = this;\r
164 \r
165                 var width = editor.config[ 'filebrowser' + ucFirst( dialog.getName() ) + 'WindowWidth' ]\r
166                                 || editor.config.filebrowserWindowWidth || '80%';\r
167                 var height = editor.config[ 'filebrowser' + ucFirst( dialog.getName() ) + 'WindowHeight' ]\r
168                                 || editor.config.filebrowserWindowHeight || '70%';\r
169 \r
170                 var params = this.filebrowser.params || {};\r
171                 params.CKEditor = editor.name;\r
172                 params.CKEditorFuncNum = editor._.filebrowserFn;\r
173                 if ( !params.langCode )\r
174                         params.langCode = editor.langCode;\r
175 \r
176                 var url = addQueryString( this.filebrowser.url, params );\r
177                 editor.popup( url, width, height, editor.config.fileBrowserWindowFeatures );\r
178         }\r
179 \r
180         /**\r
181          * The onlick function assigned to the 'Upload' button. Makes the final\r
182          * decision whether form is really submitted and updates target field when\r
183          * file is uploaded.\r
184          *\r
185          * @param {CKEDITOR.event}\r
186          *            evt The event object.\r
187          */\r
188         function uploadFile( evt )\r
189         {\r
190                 var dialog = this.getDialog();\r
191                 var editor = dialog.getParentEditor();\r
192 \r
193                 editor._.filebrowserSe = this;\r
194 \r
195                 // If user didn't select the file, stop the upload.\r
196                 if ( !dialog.getContentElement( this[ 'for' ][ 0 ], this[ 'for' ][ 1 ] ).getInputElement().$.value )\r
197                         return false;\r
198 \r
199                 if ( !dialog.getContentElement( this[ 'for' ][ 0 ], this[ 'for' ][ 1 ] ).getAction() )\r
200                         return false;\r
201 \r
202                 return true;\r
203         }\r
204 \r
205         /**\r
206          * Setups the file element.\r
207          *\r
208          * @param {CKEDITOR.ui.dialog.file}\r
209          *            fileInput The file element used during file upload.\r
210          * @param {Object}\r
211          *            filebrowser Object containing filebrowser settings assigned to\r
212          *            the fileButton associated with this file element.\r
213          */\r
214         function setupFileElement( editor, fileInput, filebrowser )\r
215         {\r
216                 var params = filebrowser.params || {};\r
217                 params.CKEditor = editor.name;\r
218                 params.CKEditorFuncNum = editor._.filebrowserFn;\r
219                 if ( !params.langCode )\r
220                         params.langCode = editor.langCode;\r
221 \r
222                 fileInput.action = addQueryString( filebrowser.url, params );\r
223                 fileInput.filebrowser = filebrowser;\r
224         }\r
225 \r
226         /**\r
227          * Traverse through the content definition and attach filebrowser to\r
228          * elements with 'filebrowser' attribute.\r
229          *\r
230          * @param String\r
231          *            dialogName Dialog name.\r
232          * @param {CKEDITOR.dialog.dialogDefinitionObject}\r
233          *            definition Dialog definition.\r
234          * @param {Array}\r
235          *            elements Array of {@link CKEDITOR.dialog.contentDefinition}\r
236          *            objects.\r
237          */\r
238         function attachFileBrowser( editor, dialogName, definition, elements )\r
239         {\r
240                 var element, fileInput;\r
241 \r
242                 for ( var i in elements )\r
243                 {\r
244                         element = elements[ i ];\r
245 \r
246                         if ( element.type == 'hbox' || element.type == 'vbox' )\r
247                                 attachFileBrowser( editor, dialogName, definition, element.children );\r
248 \r
249                         if ( !element.filebrowser )\r
250                                 continue;\r
251 \r
252                         if ( typeof element.filebrowser == 'string' )\r
253                         {\r
254                                 var fb =\r
255                                 {\r
256                                         action : ( element.type == 'fileButton' ) ? 'QuickUpload' : 'Browse',\r
257                                         target : element.filebrowser\r
258                                 };\r
259                                 element.filebrowser = fb;\r
260                         }\r
261 \r
262                         if ( element.filebrowser.action == 'Browse' )\r
263                         {\r
264                                 var url = element.filebrowser.url;\r
265                                 if ( url === undefined )\r
266                                 {\r
267                                         url = editor.config[ 'filebrowser' + ucFirst( dialogName ) + 'BrowseUrl' ];\r
268                                         if ( url === undefined )\r
269                                                 url = editor.config.filebrowserBrowseUrl;\r
270                                 }\r
271 \r
272                                 if ( url )\r
273                                 {\r
274                                         element.onClick = browseServer;\r
275                                         element.filebrowser.url = url;\r
276                                         element.hidden = false;\r
277                                 }\r
278                         }\r
279                         else if ( element.filebrowser.action == 'QuickUpload' && element[ 'for' ] )\r
280                         {\r
281                                 url = element.filebrowser.url;\r
282                                 if ( url === undefined )\r
283                                 {\r
284                                         url = editor.config[ 'filebrowser' + ucFirst( dialogName ) + 'UploadUrl' ];\r
285                                         if ( url === undefined )\r
286                                                 url = editor.config.filebrowserUploadUrl;\r
287                                 }\r
288 \r
289                                 if ( url )\r
290                                 {\r
291                                         var onClick = element.onClick;\r
292                                         element.onClick = function( evt )\r
293                                         {\r
294                                                 // "element" here means the definition object, so we need to find the correct\r
295                                                 // button to scope the event call\r
296                                                 var sender = evt.sender;\r
297                                                 if ( onClick && onClick.call( sender, evt ) === false )\r
298                                                         return false;\r
299 \r
300                                                 return uploadFile.call( sender, evt );\r
301                                         };\r
302 \r
303                                         element.filebrowser.url = url;\r
304                                         element.hidden = false;\r
305                                         setupFileElement( editor, definition.getContents( element[ 'for' ][ 0 ] ).get( element[ 'for' ][ 1 ] ), element.filebrowser );\r
306                                 }\r
307                         }\r
308                 }\r
309         }\r
310 \r
311         /**\r
312          * Updates the target element with the url of uploaded/selected file.\r
313          *\r
314          * @param {String}\r
315          *            url The url of a file.\r
316          */\r
317         function updateTargetElement( url, sourceElement )\r
318         {\r
319                 var dialog = sourceElement.getDialog();\r
320                 var targetElement = sourceElement.filebrowser.target || null;\r
321                 url = url.replace( /#/g, '%23' );\r
322 \r
323                 // If there is a reference to targetElement, update it.\r
324                 if ( targetElement )\r
325                 {\r
326                         var target = targetElement.split( ':' );\r
327                         var element = dialog.getContentElement( target[ 0 ], target[ 1 ] );\r
328                         if ( element )\r
329                         {\r
330                                 element.setValue( url );\r
331                                 dialog.selectPage( target[ 0 ] );\r
332                         }\r
333                 }\r
334         }\r
335 \r
336         /**\r
337          * Returns true if filebrowser is configured in one of the elements.\r
338          *\r
339          * @param {CKEDITOR.dialog.dialogDefinitionObject}\r
340          *            definition Dialog definition.\r
341          * @param String\r
342          *            tabId The tab id where element(s) can be found.\r
343          * @param String\r
344          *            elementId The element id (or ids, separated with a semicolon) to check.\r
345          */\r
346         function isConfigured( definition, tabId, elementId )\r
347         {\r
348                 if ( elementId.indexOf( ";" ) !== -1 )\r
349                 {\r
350                         var ids = elementId.split( ";" );\r
351                         for ( var i = 0 ; i < ids.length ; i++ )\r
352                         {\r
353                                 if ( isConfigured( definition, tabId, ids[i] ) )\r
354                                         return true;\r
355                         }\r
356                         return false;\r
357                 }\r
358 \r
359                 var elementFileBrowser = definition.getContents( tabId ).get( elementId ).filebrowser;\r
360                 return ( elementFileBrowser && elementFileBrowser.url );\r
361         }\r
362 \r
363         function setUrl( fileUrl, data )\r
364         {\r
365                 var dialog = this._.filebrowserSe.getDialog(),\r
366                         targetInput = this._.filebrowserSe[ 'for' ],\r
367                         onSelect = this._.filebrowserSe.filebrowser.onSelect;\r
368 \r
369                 if ( targetInput )\r
370                         dialog.getContentElement( targetInput[ 0 ], targetInput[ 1 ] ).reset();\r
371 \r
372                 if ( typeof data == 'function' && data.call( this._.filebrowserSe ) === false )\r
373                         return;\r
374 \r
375                 if ( onSelect && onSelect.call( this._.filebrowserSe, fileUrl, data ) === false )\r
376                         return;\r
377 \r
378                 // The "data" argument may be used to pass the error message to the editor.\r
379                 if ( typeof data == 'string' && data )\r
380                         alert( data );\r
381 \r
382                 if ( fileUrl )\r
383                         updateTargetElement( fileUrl, this._.filebrowserSe );\r
384         }\r
385 \r
386         CKEDITOR.plugins.add( 'filebrowser',\r
387         {\r
388                 init : function( editor, pluginPath )\r
389                 {\r
390                         editor._.filebrowserFn = CKEDITOR.tools.addFunction( setUrl, editor );\r
391                         editor.on( 'destroy', function () { CKEDITOR.tools.removeFunction( this._.filebrowserFn ); } );\r
392                 }\r
393         } );\r
394 \r
395         CKEDITOR.on( 'dialogDefinition', function( evt )\r
396         {\r
397                 var definition = evt.data.definition,\r
398                         element;\r
399                 // Associate filebrowser to elements with 'filebrowser' attribute.\r
400                 for ( var i in definition.contents )\r
401                 {\r
402                         if ( ( element = definition.contents[ i ] ) )\r
403                         {\r
404                                 attachFileBrowser( evt.editor, evt.data.name, definition, element.elements );\r
405                                 if ( element.hidden && element.filebrowser )\r
406                                 {\r
407                                         element.hidden = !isConfigured( definition, element[ 'id' ], element.filebrowser );\r
408                                 }\r
409                         }\r
410                 }\r
411         } );\r
412 \r
413 } )();\r
414 \r
415 /**\r
416  * The location of an external file browser, that should be launched when "Browse Server" button is pressed.\r
417  * If configured, the "Browse Server" button will appear in Link, Image and Flash dialogs.\r
418  * @see The <a href="http://docs.cksource.com/CKEditor_3.x/Developers_Guide/File_Browser_(Uploader)">File Browser/Uploader</a> documentation.\r
419  * @name CKEDITOR.config.filebrowserBrowseUrl\r
420  * @since 3.0\r
421  * @type String\r
422  * @default '' (empty string = disabled)\r
423  * @example\r
424  * config.filebrowserBrowseUrl = '/browser/browse.php';\r
425  */\r
426 \r
427 /**\r
428  * The location of a script that handles file uploads.\r
429  * If set, the "Upload" tab will appear in "Link", "Image" and "Flash" dialogs.\r
430  * @name CKEDITOR.config.filebrowserUploadUrl\r
431  * @see The <a href="http://docs.cksource.com/CKEditor_3.x/Developers_Guide/File_Browser_(Uploader)">File Browser/Uploader</a> documentation.\r
432  * @since 3.0\r
433  * @type String\r
434  * @default '' (empty string = disabled)\r
435  * @example\r
436  * config.filebrowserUploadUrl = '/uploader/upload.php';\r
437  */\r
438 \r
439 /**\r
440  * The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Image dialog.\r
441  * If not set, CKEditor will use {@link CKEDITOR.config.filebrowserBrowseUrl}.\r
442  * @name CKEDITOR.config.filebrowserImageBrowseUrl\r
443  * @since 3.0\r
444  * @type String\r
445  * @default '' (empty string = disabled)\r
446  * @example\r
447  * config.filebrowserImageBrowseUrl = '/browser/browse.php?type=Images';\r
448  */\r
449 \r
450 /**\r
451  * The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Flash dialog.\r
452  * If not set, CKEditor will use {@link CKEDITOR.config.filebrowserBrowseUrl}.\r
453  * @name CKEDITOR.config.filebrowserFlashBrowseUrl\r
454  * @since 3.0\r
455  * @type String\r
456  * @default '' (empty string = disabled)\r
457  * @example\r
458  * config.filebrowserFlashBrowseUrl = '/browser/browse.php?type=Flash';\r
459  */\r
460 \r
461 /**\r
462  * The location of a script that handles file uploads in the Image dialog.\r
463  * If not set, CKEditor will use {@link CKEDITOR.config.filebrowserUploadUrl}.\r
464  * @name CKEDITOR.config.filebrowserImageUploadUrl\r
465  * @since 3.0\r
466  * @type String\r
467  * @default '' (empty string = disabled)\r
468  * @example\r
469  * config.filebrowserImageUploadUrl = '/uploader/upload.php?type=Images';\r
470  */\r
471 \r
472 /**\r
473  * The location of a script that handles file uploads in the Flash dialog.\r
474  * If not set, CKEditor will use {@link CKEDITOR.config.filebrowserUploadUrl}.\r
475  * @name CKEDITOR.config.filebrowserFlashUploadUrl\r
476  * @since 3.0\r
477  * @type String\r
478  * @default '' (empty string = disabled)\r
479  * @example\r
480  * config.filebrowserFlashUploadUrl = '/uploader/upload.php?type=Flash';\r
481  */\r
482 \r
483 /**\r
484  * The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Link tab of Image dialog.\r
485  * If not set, CKEditor will use {@link CKEDITOR.config.filebrowserBrowseUrl}.\r
486  * @name CKEDITOR.config.filebrowserImageBrowseLinkUrl\r
487  * @since 3.2\r
488  * @type String\r
489  * @default '' (empty string = disabled)\r
490  * @example\r
491  * config.filebrowserImageBrowseLinkUrl = '/browser/browse.php';\r
492  */\r
493 \r
494 /**\r
495  * The "features" to use in the file browser popup window.\r
496  * @name CKEDITOR.config.filebrowserWindowFeatures\r
497  * @since 3.4.1\r
498  * @type String\r
499  * @default 'location=no,menubar=no,toolbar=no,dependent=yes,minimizable=no,modal=yes,alwaysRaised=yes,resizable=yes,scrollbars=yes'\r
500  * @example\r
501  * config.filebrowserWindowFeatures = 'resizable=yes,scrollbars=no';\r
502  */\r