| 1 | /* |
| 2 | Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. |
| 3 | For licensing, see LICENSE.html or http://ckeditor.com/license |
| 4 | */ |
| 5 | |
| 6 | /** |
| 7 | * @fileOverview The "filebrowser" plugin, it adds support for file uploads and browsing. |
| 8 | * |
| 9 | * When file is selected inside of the file browser or uploaded, its url is inserted automatically to |
| 10 | * a field, which is described in the 'filebrowser' attribute. |
| 11 | * To specify field that should be updated, pass the tab id and element id, separated with a colon. |
| 12 | * |
| 13 | * Example 1: (Browse) |
| 14 | * { |
| 15 | * type : 'button', |
| 16 | * id : 'browse', |
| 17 | * filebrowser : 'tabId:elementId', |
| 18 | * label : editor.lang.common.browseServer |
| 19 | * } |
| 20 | * |
| 21 | * If you set the 'filebrowser' attribute on any element other than 'fileButton', the 'Browse' |
| 22 | * action will be triggered. |
| 23 | * |
| 24 | * Example 2: (Quick Upload) |
| 25 | * { |
| 26 | * type : 'fileButton', |
| 27 | * id : 'uploadButton', |
| 28 | * filebrowser : 'tabId:elementId', |
| 29 | * label : editor.lang.common.uploadSubmit, |
| 30 | * 'for' : [ 'upload', 'upload' ] |
| 31 | * } |
| 32 | * |
| 33 | * If you set the 'filebrowser' attribute on a fileButton element, the 'QuickUpload' |
| 34 | * action will be executed. |
| 35 | * |
| 36 | * Filebrowser plugin also supports more advanced configuration (through javascript object). |
| 37 | * |
| 38 | * The following settings are supported: |
| 39 | * [action] - Browse or QuickUpload |
| 40 | * [target] - field to update, tabId:elementId |
| 41 | * [params] - additional arguments to be passed to the server connector (optional) |
| 42 | * [selectFunction] - name of the javascript function called by the server connector when file is |
| 43 | * selected/uploaded, default value is 'SetUrl'. |
| 44 | * [onSelect] - function to execute when file is selected/uploaded (optional) |
| 45 | * [url] - the URL to be called (optional) |
| 46 | * |
| 47 | * Example 3: (Quick Upload) |
| 48 | * { |
| 49 | * type : 'fileButton', |
| 50 | * label : editor.lang.common.uploadSubmit, |
| 51 | * id : 'buttonId', |
| 52 | * filebrowser : |
| 53 | * { |
| 54 | * action : 'QuickUpload', //required |
| 55 | * target : 'tab1:elementId', //required |
| 56 | * params : { type : 'Files', currentFolder : '/folder/' }, //optional |
| 57 | * onSelect : function( fileUrl, errorMessage ) //optional |
| 58 | * { |
| 59 | * // Do not call the built-in selectFuntion |
| 60 | * // return false; |
| 61 | * } |
| 62 | * }, |
| 63 | * 'for' : ['tab1', 'myFile'] |
| 64 | * } |
| 65 | * |
| 66 | * Suppose we have a file element with id 'myFile', text field with id 'elementId' and a fileButton. |
| 67 | * If filebowser.url is not specified explicitly, form action will be set to |
| 68 | * 'filebrowser[DialogName]UploadUrl' or, if not specified, to 'filebrowserUploadUrl'. |
| 69 | * Additional parameters from 'params' object will be added to the query string. |
| 70 | * It is possible to create your own uploadHandler and cancel the built-in updateTargetElement command. |
| 71 | * |
| 72 | * Example 4: (Browse) |
| 73 | * { |
| 74 | * type : 'button', |
| 75 | * id : 'buttonId', |
| 76 | * label : editor.lang.common.browseServer, |
| 77 | * filebrowser : |
| 78 | * { |
| 79 | * action : 'Browse', |
| 80 | * url : '/ckfinder/ckfinder.html?action=js&func=SetUrl&thumbFunc=SetUrl&type=Images', |
| 81 | * target : 'tab1:elementId' |
| 82 | * } |
| 83 | * } |
| 84 | * |
| 85 | * In this example, after pressing a button, file browser will be opened in a popup. |
| 86 | * If we don't specify filebrowser.url attribute, 'filebrowser[DialogName]BrowseUrl' or |
| 87 | * 'filebrowserBrowseUrl' will be used. |
| 88 | * After selecting a file in a file browser, an element with id 'elementId' will be updated. |
| 89 | */ |
| 90 | CKEDITOR.plugins.add( 'filebrowser', |
| 91 | { |
| 92 | /** |
| 93 | * Reference to the last opened dialog. Required by 'selectFunction'. |
| 94 | * @type {CKEDITOR.dialog} |
| 95 | */ |
| 96 | _dialog : null, |
| 97 | /** |
| 98 | * The "action" attribute that will be passed to the {@see CKEDITOR.ui.dialog.file#reset} function. |
| 99 | * The "action" attribute is generated automatically in some cases. |
| 100 | * @type String |
| 101 | */ |
| 102 | _formAction : null, |
| 103 | /** |
| 104 | * Holds the reference to the file input element to reset after file is uploaded. |
| 105 | * Syntax: [ pageId, elementId ] |
| 106 | * @type Array |
| 107 | */ |
| 108 | _targetInput : null, |
| 109 | /** |
| 110 | * Holds the reference to the UI element that will be updated when file is selected/uploaded. |
| 111 | * Syntax: pageId:elementId |
| 112 | * @type Array |
| 113 | */ |
| 114 | _targetElement : null, |
| 115 | /** |
| 116 | * The user defined function (if defined) that will be called when file is selected/uploaded. |
| 117 | * @type Function |
| 118 | */ |
| 119 | _onSelect : null, |
| 120 | |
| 121 | init : function( editor, pluginPath ) |
| 122 | { |
| 123 | /** |
| 124 | * Updates the target element with the url of uploaded/selected file. |
| 125 | * @param {String} url The url of a file. |
| 126 | */ |
| 127 | var updateTargetElement = function( url ) |
| 128 | { |
| 129 | url = url.replace( /#/g, '%23' ); |
| 130 | var plugin = editor.plugins.filebrowser; |
| 131 | var targetElement = plugin._targetElement; |
| 132 | |
| 133 | // If there is a reference to targetElement, update it. |
| 134 | if ( targetElement ) |
| 135 | { |
| 136 | if ( targetElement === true ) |
| 137 | targetElement = 'url'; |
| 138 | |
| 139 | if ( targetElement.indexOf( ':' ) == -1 ) |
| 140 | { |
| 141 | plugin._dialog.foreach( function ( e ) { |
| 142 | if ( e.id == plugin._targetElement ) |
| 143 | { |
| 144 | e.setValue( url ); |
| 145 | plugin._dialog.selectPage( e.getParentTab() ); |
| 146 | } |
| 147 | }); |
| 148 | } |
| 149 | else |
| 150 | { |
| 151 | var target = plugin._targetElement.split( ':' ); |
| 152 | var element = plugin._dialog.getContentElement( target[0], target[1] ); |
| 153 | if ( element ) |
| 154 | { |
| 155 | element.setValue( url ); |
| 156 | plugin._dialog.selectPage( target[0] ); |
| 157 | } |
| 158 | } |
| 159 | } |
| 160 | }; |
| 161 | |
| 162 | /** |
| 163 | * Make a string's first character uppercase. |
| 164 | * @param {String} str String. |
| 165 | */ |
| 166 | var ucFirst = function( str ) |
| 167 | { |
| 168 | str += ''; |
| 169 | var f = str.charAt( 0 ).toUpperCase(); |
| 170 | return f + str.substr( 1 ); |
| 171 | }; |
| 172 | |
| 173 | /** |
| 174 | * The onlick function assigned to the 'Browse Server' button. |
| 175 | * Opens the file browser and updates target field when file is selected. |
| 176 | * @param {CKEDITOR.event} evt The event object. |
| 177 | */ |
| 178 | var browseServer = function( evt ) |
| 179 | { |
| 180 | var dialog = this.getDialog(); |
| 181 | var editor = dialog.getParentEditor(); |
| 182 | var plugin = editor.plugins.filebrowser; |
| 183 | var params = this.filebrowser.params || {}; |
| 184 | |
| 185 | // Save references to fields required by the 'selectFunction'. |
| 186 | plugin._dialog = dialog; |
| 187 | plugin._targetElement = this.filebrowser.target || null; |
| 188 | plugin._onSelect = this.filebrowser.onSelect; |
| 189 | |
| 190 | // Function that is called when the user selects a file in external file browser. |
| 191 | var selectFunction = this.filebrowser.selectFunction || editor.config['filebrowserSelectFunction'] || 'SetUrl'; |
| 192 | |
| 193 | this.getElement().getWindow().$[selectFunction] = function( fileUrl, data ) |
| 194 | { |
| 195 | if ( plugin._onSelect && plugin._onSelect( fileUrl, data ) === false ) |
| 196 | return; |
| 197 | |
| 198 | updateTargetElement( fileUrl ); |
| 199 | }; |
| 200 | |
| 201 | var width = editor.config['filebrowser' + ucFirst( dialog.getName() ) + 'WindowWidth'] || editor.config.filebrowserWindowWidth || '80%'; |
| 202 | var height = editor.config['filebrowser' + ucFirst( dialog.getName() ) + 'WindowHeight'] || editor.config.filebrowserWindowHeight || '70%'; |
| 203 | var url = this.filebrowser.url || editor.config['filebrowser' + ucFirst( dialog.getName() ) + 'BrowseUrl'] || editor.config.filebrowserBrowseUrl; |
| 204 | |
| 205 | if ( !url ) |
| 206 | { |
| 207 | alert( editor.lang.filebrowser.browserNotConfigured ); |
| 208 | return; |
| 209 | } |
| 210 | |
| 211 | if ( !params.langCode ) |
| 212 | params.langCode = editor.langCode; |
| 213 | |
| 214 | params.CKEditor = 1; |
| 215 | url = addQueryString( url, params ); |
| 216 | |
| 217 | editor.popup( url, width, height ); |
| 218 | }; |
| 219 | |
| 220 | /** |
| 221 | * The onlick function assigned to the 'Upload' button. |
| 222 | * Makes the final decision whether form is really submitted and updates target field when file is uploaded. |
| 223 | * @param {CKEDITOR.event} evt The event object. |
| 224 | */ |
| 225 | var uploadFile = function( evt ) |
| 226 | { |
| 227 | var dialog = this.getDialog(); |
| 228 | var editor = dialog.getParentEditor(); |
| 229 | var plugin = editor.plugins.filebrowser; |
| 230 | |
| 231 | // Update variables that will be used by the 'selectFunction' function. |
| 232 | plugin._onSelect = this.filebrowser.onSelect; |
| 233 | plugin._dialog = dialog; |
| 234 | plugin._targetInput = this['for']; |
| 235 | plugin._formAction = dialog.getContentElement( this['for'][0], this['for'][1] ).getAction(); |
| 236 | |
| 237 | // If user didn't select the file, stop the upload. |
| 238 | if ( !dialog.getContentElement( this['for'][0], this['for'][1] ).getInputElement().$.value ) |
| 239 | { |
| 240 | alert( editor.lang.filebrowser.fileNotSelected ); |
| 241 | return false; |
| 242 | } |
| 243 | |
| 244 | if ( !plugin._formAction ) |
| 245 | { |
| 246 | alert( editor.lang.filebrowser.uploaderNotConfigured ); |
| 247 | return false; |
| 248 | } |
| 249 | |
| 250 | // targetElement will be updated with the url of uploaded file. |
| 251 | plugin._targetElement = this.filebrowser.target || null; |
| 252 | |
| 253 | // Function to be called by the server connector after file is uploaded. |
| 254 | var selectFunction = this.filebrowser.selectFunction || editor.config['filebrowserSelectFunction'] || 'SetUrl'; |
| 255 | |
| 256 | this.getElement().getWindow().$[selectFunction] = function( fileUrl, errorMessage ) |
| 257 | { |
| 258 | plugin._dialog.getContentElement( plugin._targetInput[0], plugin._targetInput[1] ).reset( plugin._formAction ); |
| 259 | |
| 260 | if ( plugin._onSelect && plugin._onSelect( fileUrl, errorMessage ) === false ) |
| 261 | return; |
| 262 | |
| 263 | if ( errorMessage ) |
| 264 | alert( errorMessage ); |
| 265 | |
| 266 | if ( fileUrl ) |
| 267 | updateTargetElement( fileUrl ); |
| 268 | }; |
| 269 | |
| 270 | return true; |
| 271 | }; |
| 272 | |
| 273 | /** |
| 274 | * Adds (additional) arguments to given url. |
| 275 | * @param {String} url The url. |
| 276 | * @param {Object} params Additional parameters. |
| 277 | */ |
| 278 | var addQueryString = function( url, params ) |
| 279 | { |
| 280 | var queryString = []; |
| 281 | |
| 282 | if ( !params ) |
| 283 | return url; |
| 284 | else |
| 285 | { |
| 286 | for ( var i in params ) |
| 287 | queryString.push( i + "=" + encodeURIComponent( params[i] ) ); |
| 288 | } |
| 289 | |
| 290 | return url + ( ( url.indexOf( "?" ) != -1 ) ? "&" : "?" ) + queryString.join( "&" ); |
| 291 | }; |
| 292 | |
| 293 | /** |
| 294 | * Setups the file element. |
| 295 | * @param {CKEDITOR.ui.dialog.file} fileInput The file element used during file upload. |
| 296 | * @param {Object} filebrowser Object containing filebrowser settings assigned to the fileButton |
| 297 | * associated with this file element. |
| 298 | */ |
| 299 | var setupFileElement = function( dialogName, fileInput, filebrowser ) |
| 300 | { |
| 301 | var params = filebrowser.params || {}; |
| 302 | |
| 303 | params.CKEditor = 1; |
| 304 | if ( !params.langCode ) |
| 305 | params.langCode = editor.langCode; |
| 306 | |
| 307 | var url = filebrowser.url || editor.config['filebrowser' + ucFirst( dialogName ) + 'UploadUrl'] || editor.config.filebrowserUploadUrl; |
| 308 | |
| 309 | fileInput.action = url ? addQueryString( url, params ) : ''; |
| 310 | fileInput.filebrowser = filebrowser; |
| 311 | }; |
| 312 | |
| 313 | /** |
| 314 | * Traverse through the content definition and attach filebrowser |
| 315 | * to elements with 'filebrowser' attribute. |
| 316 | * @param {CKEDITOR.dialog.dialogDefinitionObject} definition |
| 317 | * @param {Array} elements Array of {@link CKEDITOR.dialog.contentDefinition} objects. |
| 318 | */ |
| 319 | var attachFileBrowser = function( dialogName, definition, elements ) |
| 320 | { |
| 321 | var element, fileInput; |
| 322 | |
| 323 | for ( var i in elements ) |
| 324 | { |
| 325 | element = elements[i]; |
| 326 | |
| 327 | if ( element.type == 'hbox' || element.type == 'vbox' ) |
| 328 | attachFileBrowser( dialogName, definition, element.children ); |
| 329 | |
| 330 | if ( !element.filebrowser ) |
| 331 | continue; |
| 332 | |
| 333 | if ( typeof element.filebrowser == 'string' ) |
| 334 | { |
| 335 | var fb = { |
| 336 | action : ( element.type == 'fileButton' ) ? 'QuickUpload' : 'Browse', |
| 337 | target : element.filebrowser |
| 338 | }; |
| 339 | element.filebrowser = fb; |
| 340 | } |
| 341 | |
| 342 | if ( element.filebrowser.action == 'Browse' ) |
| 343 | { |
| 344 | element.onClick = browseServer; |
| 345 | } |
| 346 | else if ( element.filebrowser.action == 'QuickUpload' && element['for'] ) |
| 347 | { |
| 348 | setupFileElement( dialogName, definition.getContents( element['for'][0] ).get( element['for'][1] ), element.filebrowser ); |
| 349 | element.onClick = uploadFile; |
| 350 | } |
| 351 | } |
| 352 | }; |
| 353 | |
| 354 | CKEDITOR.on( 'dialogDefinition', function( evt ) |
| 355 | { |
| 356 | var i, browseButton, fileButton; |
| 357 | |
| 358 | // Associate filebrowser to elements with 'filebrowser' attribute. |
| 359 | for ( i in evt.data.definition.contents ) |
| 360 | attachFileBrowser( evt.data.name, evt.data.definition, evt.data.definition.contents[i].elements ); |
| 361 | }); |
| 362 | } |
| 363 | } ); |