JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
458f29614ad96a0db0b3828b8d74bedb0b267ab6
[ckeditor.git] / _source / plugins / filebrowser / plugin.js
1 /*\r
2 Copyright (c) 2003-2009, 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 );\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 || editor.config[ 'filebrowser' + ucFirst( dialogName ) + 'BrowseUrl' ]\r
265                                                         || editor.config.filebrowserBrowseUrl;\r
266 \r
267                                 if ( url )\r
268                                 {\r
269                                         element.onClick = browseServer;\r
270                                         element.filebrowser.url = url;\r
271                                         element.hidden = false;\r
272                                 }\r
273                         }\r
274                         else if ( element.filebrowser.action == 'QuickUpload' && element[ 'for' ] )\r
275                         {\r
276                                 url =  element.filebrowser.url || editor.config[ 'filebrowser' + ucFirst( dialogName ) + 'UploadUrl' ]\r
277                                                         || editor.config.filebrowserUploadUrl;\r
278 \r
279                                 if ( url )\r
280                                 {\r
281                                         element.onClick = uploadFile;\r
282                                         element.filebrowser.url = url;\r
283                                         element.hidden = false;\r
284                                         setupFileElement( editor, definition.getContents( element[ 'for' ][ 0 ] ).get( element[ 'for' ][ 1 ] ), element.filebrowser );\r
285                                 }\r
286                         }\r
287                 }\r
288         }\r
289 \r
290         /**\r
291          * Updates the target element with the url of uploaded/selected file.\r
292          *\r
293          * @param {String}\r
294          *            url The url of a file.\r
295          */\r
296         function updateTargetElement( url, sourceElement )\r
297         {\r
298                 var dialog = sourceElement.getDialog();\r
299                 var targetElement = sourceElement.filebrowser.target || null;\r
300                 url = url.replace( /#/g, '%23' );\r
301 \r
302                 // If there is a reference to targetElement, update it.\r
303                 if ( targetElement )\r
304                 {\r
305                         var target = targetElement.split( ':' );\r
306                         var element = dialog.getContentElement( target[ 0 ], target[ 1 ] );\r
307                         if ( element )\r
308                         {\r
309                                 element.setValue( url );\r
310                                 dialog.selectPage( target[ 0 ] );\r
311                         }\r
312                 }\r
313         }\r
314 \r
315         /**\r
316          * Returns true if filebrowser is configured in one of the elements.\r
317          *\r
318          * @param {CKEDITOR.dialog.dialogDefinitionObject}\r
319          *            definition Dialog definition.\r
320          * @param String\r
321          *            tabId The tab id where element(s) can be found.\r
322          * @param String\r
323          *            elementId The element id (or ids, separated with a semicolon) to check.\r
324          */\r
325         function isConfigured( definition, tabId, elementId )\r
326         {\r
327                 if ( elementId.indexOf( ";" ) !== -1 )\r
328                 {\r
329                         var ids = elementId.split( ";" );\r
330                         for ( var i = 0 ; i < ids.length ; i++ )\r
331                         {\r
332                                 if ( isConfigured( definition, tabId, ids[i]) )\r
333                                         return true;\r
334                         }\r
335                         return false;\r
336                 }\r
337 \r
338                 return ( definition.getContents( tabId ).get( elementId ).filebrowser && definition.getContents( tabId ).get( elementId ).filebrowser.url );\r
339         }\r
340 \r
341         function setUrl( fileUrl, data )\r
342         {\r
343                 var dialog = this._.filebrowserSe.getDialog(),\r
344                         targetInput = this._.filebrowserSe[ 'for' ],\r
345                         onSelect = this._.filebrowserSe.filebrowser.onSelect;\r
346 \r
347                 if ( targetInput )\r
348                         dialog.getContentElement( targetInput[ 0 ], targetInput[ 1 ] ).reset();\r
349 \r
350                 if ( onSelect && onSelect.call( this._.filebrowserSe, fileUrl, data ) === false )\r
351                         return;\r
352 \r
353                 // The "data" argument may be used to pass the error message to the editor.\r
354                 if ( typeof data == 'string' && data )\r
355                         alert( data );\r
356 \r
357                 if ( fileUrl )\r
358                         updateTargetElement( fileUrl, this._.filebrowserSe );\r
359         }\r
360 \r
361         CKEDITOR.plugins.add( 'filebrowser',\r
362         {\r
363                 init : function( editor, pluginPath )\r
364                 {\r
365                         editor._.filebrowserFn = CKEDITOR.tools.addFunction( setUrl, editor );\r
366 \r
367                         CKEDITOR.on( 'dialogDefinition', function( evt )\r
368                         {\r
369                                 // Associate filebrowser to elements with 'filebrowser' attribute.\r
370                                 for ( var i in evt.data.definition.contents )\r
371                                 {\r
372                                         attachFileBrowser( evt.editor, evt.data.name, evt.data.definition, evt.data.definition.contents[ i ].elements );\r
373                                         if ( evt.data.definition.contents[ i ].hidden && evt.data.definition.contents[ i ].filebrowser )\r
374                                         {\r
375                                                 evt.data.definition.contents[ i ].hidden =\r
376                                                         !isConfigured( evt.data.definition, evt.data.definition.contents[ i ][ 'id' ], evt.data.definition.contents[ i ].filebrowser );\r
377                                         }\r
378                                 }\r
379                         } );\r
380                 }\r
381         } );\r
382 \r
383 } )();\r