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