/** * @file SimpleUploads plugin for CKEditor * Version: 4.3.12 * Uploads pasted images and files inside the editor to the server for Firefox and Chrome * Feature introduced in: https://bugzilla.mozilla.org/show_bug.cgi?id=490879 * doesn't include images inside HTML (paste from word). IE11 does. https://bugzilla.mozilla.org/show_bug.cgi?id=665341 https://www.libreoffice.org/bugzilla/show_bug.cgi?id=72083 * Includes Drag&Drop file uploads for all the new browsers. * Two toolbar buttons to perform quick upload of files. * Copyright (C) 2012-14 Alfonso Martínez de Lizarrondo * improvements: allow d&d between 2 editors in Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=454832 */ (function() { "use strict"; // If the selected image is a bmp converts it to a png function convertToBmp(ev) { var data = ev.data, isBmp = /\.bmp$/.test(data.name), editor = ev.editor; if (!isBmp) return; var img = data.image; var canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.height; var ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); data.file = canvas.toDataURL("image/png"); data.name = data.name.replace(/\.bmp$/, ".png"); } // Verifies if the selected image is within the allowed dimensions function checkDimension(ev) { var data = ev.data, editor = ev.editor, config = editor.config, maximum = config.simpleuploads_maximumDimensions; var img = data.image; if (maximum.width && img.width > maximum.width) { alert(editor.lang.simpleuploads.imageTooWide); ev.cancel(); return; } if (maximum.height && img.height > maximum.height) { alert(editor.lang.simpleuploads.imageTooTall); ev.cancel(); return; } } // Custom rule similar to the fake Object to avoid generating anything if the user tries to do something strange while a file is being uploaded var htmlFilterRules = { elements: { $: function( element ) { var attributes = element.attributes, className = attributes && attributes[ "class" ]; // remove our wrappers if ( className == "SimpleUploadsTmpWrapper" ) return false; } } }; // CSS that we add to the editor for our internal styling function getEditorCss( config ) { var imgUpload = 'span.SimpleUploadsTmpWrapper>span { top: 50%; margin-top: -0.5em; width: 100%; text-align: center; color: #fff; ' + 'text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); font-size: 50px; font-family: Calibri,Arial,Sans-serif; pointer-events: none; ' + 'position: absolute; display: inline-block;}'; if (config.simpleuploads_hideImageProgress) imgUpload = 'span.SimpleUploadsTmpWrapper { color:#333; background-color:#fff; padding:4px; border:1px solid #EEE;}'; return '.SimpleUploadsOverEditor { ' + (config.simpleuploads_editorover || 'box-shadow: 0 0 10px 1px #999999 inset !important;') + ' }' + 'a.SimpleUploadsTmpWrapper { color:#333; background-color:#fff; padding:4px; border:1px solid #EEE;}' + '.SimpleUploadsTmpWrapper { display: inline-block; position: relative; pointer-events: none;}' + imgUpload + '.uploadRect {display: inline-block;height: 0.9em;vertical-align: middle;width: 20px;}' + '.uploadRect span {background-color: #999;display: inline-block;height: 100%;vertical-align: top;}' + '.SimpleUploadsTmpWrapper .uploadCancel { background-color: #333333;border-radius: 0.5em;color: #FFFFFF;cursor: pointer !important;' + 'display: inline-block;height: 1em;line-height: 0.8em;margin-left: 4px;padding-left: 0.18em;pointer-events: auto;' + 'position: relative; text-decoration:none; top: -2px;width: 0.7em;}' + '.SimpleUploadsTmpWrapper span uploadCancel { width:1em; padding-left:0}'; } var filePicker, filePickerEditor, filePickerForceLink; var IEUpload_fileName, IEUpload_caller, IEUpload_callback, IEUpload_forImage; function PickAndSendFile(editor, forImage, caller, callback) { if (IEUpload_fileName) { alert("Please, wait to finish the current upload"); return; } filePickerForceLink = !forImage; filePickerEditor = editor; if (typeof FormData == 'undefined') { // old IE var iframe = document.getElementById("simpleUploadsTarget"); if (!iframe) { iframe = document.createElement("iframe"); iframe.style.display = "none"; iframe.id = "simpleUploadsTarget"; document.body.appendChild(iframe); } IEUpload_caller = caller; IEUpload_callback = callback; IEUpload_forImage = forImage; var fnNumber = editor._.simpleuploadsFormUploadFn; var fnInitPicker = editor._.simpleuploadsFormInitFn; if (!fnNumber) { editor._.simpleuploadsFormUploadFn = fnNumber = CKEDITOR.tools.addFunction( setUrl, editor ); editor._.simpleuploadsFormInitFn = fnInitPicker = CKEDITOR.tools.addFunction( function() { window.setTimeout(function() { var picker = document.getElementById("simpleUploadsTarget").contentWindow.document.getElementById( getUploadInputName(editor) ); picker.onchange=function() { var evdata = { name: this.value, url: this.form.action, context : IEUpload_caller, id: 'IEUpload', requiresImage: IEUpload_forImage }; // , mode : {type : 'selectedFileIE'} // Remove C:\FakePath\ var m = evdata.name.match(/\\([^\\]*)$/); if (m) evdata.name = m[1]; var result = filePickerEditor.fire("simpleuploads.startUpload", evdata ); // in v3 cancel() returns true and in v4 returns false // if not canceled it's the evdata object if ( typeof result == "boolean" ) return; if (evdata.requiresImage && !CKEDITOR.plugins.simpleuploads.isImageExtension(filePickerEditor, evdata.name)) { alert(filePickerEditor.lang.simpleuploads.nonImageExtension); return; } if (IEUpload_callback && IEUpload_callback.start) IEUpload_callback.start( evdata ); IEUpload_fileName = this.value; this.form.action = evdata.url; // Add extra fields if provided if (evdata.extraFields) { var obj = evdata.extraFields; var doc = this.ownerDocument; for (var prop in obj) { if( obj.hasOwnProperty( prop ) ) { var field = doc.createElement("input"); field.type="hidden"; field.name = prop; field.value = obj[prop]; this.form.appendChild(field); } } } this.form.submit(); }; picker.click(); }, 100); }, editor); editor.on( "destroy", function () { CKEDITOR.tools.removeFunction( this._.simpleuploadsFormUploadFn ); CKEDITOR.tools.removeFunction( this._.simpleuploadsFormInitFn ); } ); } var inputName = getUploadInputName(editor); var form = "
" + "
"; var src= 'document.open(); document.write("' + form + '");document.close();' + 'window.parent.CKEDITOR.tools.callFunction(' + fnInitPicker + ');'; iframe.src = 'javascript:void(function(){' + encodeURIComponent( src ) + '}())'; // Detect when the file upload ends to check for errors iframe.onreadystatechange = function() { if (iframe.readyState == "complete") { window.setTimeout(function() { if (IEUpload_fileName) { alert("The file upload has failed"); IEUpload_fileName=null; } }, 100); } }; filePicker = null; return; } if (!filePicker) { filePicker=document.createElement("input"); filePicker.type="file"; filePicker.style.overflow="hidden"; filePicker.style.width="1px"; filePicker.style.height="1px"; filePicker.style.opacity=0.1; filePicker.multiple = "multiple"; // to trick jQueryUI filePicker.position = "absolute"; filePicker.zIndex = 1000; document.body.appendChild(filePicker); filePicker.addEventListener("change", function () { var count = filePicker.files.length; if (!count) return; // Create Undo image filePickerEditor.fire( "saveSnapshot" ); for(var i=0; i 0 ; i-- ) throbberBlocks[i].className = throbberBlocks[i-1].className ; // For the first one, copy the last class (rotation). throbberBlocks[0].className = lastClass ; }, create: function( dialog ) { if (this.throbberCover) return; var cover = CKEDITOR.dom.element.createFromHtml( '
'); dialog.parts.close.setStyle("z-index", 101); // IE8 if (CKEDITOR.env.ie && CKEDITOR.env.version<9) { cover.setStyle("zoom", 1); cover.setStyle("filter", "progid:DXImageTransform.Microsoft.gradient(startColorstr=#EEFFFFFF,endColorstr=#EEFFFFFF)"); } cover.appendTo(dialog.parts.dialog); this.throbberCover = cover; //dialog.throbberCover = cover; var mainThrobber = new CKEDITOR.dom.element("div"); this.mainThrobber = mainThrobber; var throbberParent = new CKEDITOR.dom.element("div"); this.throbberParent = throbberParent; var throbberTitle = new CKEDITOR.dom.element("div"); this.throbberTitle = throbberTitle; cover.append(mainThrobber).addClass("cke_throbberMain"); mainThrobber.append(throbberTitle).addClass("cke_throbberTitle"); mainThrobber.append(throbberParent).addClass("cke_throbber"); // Create the throbber blocks. var classIds = [ 1,2,3,4,5,4,3,2 ] ; while ( classIds.length > 0 ) throbberParent.append( new CKEDITOR.dom.element("div") ).addClass('cke_throbber_' + classIds.shift()) ; this.center(); // Protection if the dialog is closed without removing the throbber dialog.on("hide", this.hide, this); }, center : function() { var mainThrobber = this.mainThrobber, cover = this.throbberCover; // Center the throbber var x = ( cover.$.offsetWidth - mainThrobber.$.offsetWidth ) / 2, y = ( cover.$.offsetHeight - mainThrobber.$.offsetHeight ) / 2; mainThrobber.setStyle( "left", x.toFixed() + "px" ); mainThrobber.setStyle( "top", y.toFixed() + "px" ); }, show : function() { this.create(CKEDITOR.dialog.getCurrent()); this.throbberCover.setStyle("visibility", ""); // Setup the animation interval. this.timer = setInterval( CKEDITOR.tools.bind(this.update, this), 100 ) ; }, hide : function() { if ( this.timer ) { clearInterval( this.timer ) ; this.timer = null ; } if (!this.throbberCover) return; this.throbberCover.setStyle("visibility", "hidden"); } }; } this.throbber.show(); }; // Add a listener to check file size and valid extensions editor.on( "simpleuploads.startUpload" , function(ev) { var editor = ev.editor, config = editor.config, file = ev.data && ev.data.file; if (config.simpleuploads_maxFileSize && file && file.size && file.size > config.simpleuploads_maxFileSize ) { alert( editor.lang.simpleuploads.fileTooBig ); ev.cancel(); } var name = ev.data.name; if (config.simpleuploads_invalidExtensions) { var reInvalid = new RegExp( "\.(?:" + config.simpleuploads_invalidExtensions + ")$", "i"); if ( reInvalid.test( name ) ) { alert( editor.lang.simpleuploads.invalidExtension ); ev.cancel(); } } if (config.simpleuploads_acceptedExtensions) { var reAccepted = new RegExp( "\.(?:" + config.simpleuploads_acceptedExtensions + ")$", "i"); if ( !reAccepted.test( name ) ) { alert( editor.lang.simpleuploads.nonAcceptedExtension.replace("%0", config.simpleuploads_acceptedExtensions) ); ev.cancel(); } } }); // Special listener that captures uploads of images and if there's some listener set for "simpleuploads.localImageReady" // event, prepare an image with the local data (to check dimensions, convert between formats, resize...) editor.on( 'simpleuploads.startUpload' , function(ev) { var data = ev.data, editor = ev.editor; // If this function has already pre-processed the file, exit. if (data.image) return; // Handle here only images if (data.forceLink || !CKEDITOR.plugins.simpleuploads.isImageExtension(editor, data.name)) return; // If the mode hasn't been set (picked files in IE8), don't process the data if (!data.mode || !data.mode.type) return; // As this forces an asynchronous callback use it only when there's a listener set. if (!editor.hasListeners( 'simpleuploads.localImageReady' )) return; // Cancel the default processing ev.cancel(); if (data.mode.type=="base64paste") { // to handle multiple images in IE11, insert a marker for each one. // we add our class so it won't remain if it's rejected in another step var idTmp = CKEDITOR.plugins.simpleuploads.getTimeStampId(); data.result = ""; data.mode.id = idTmp; } var img = new Image; img.onload = function() { var evData = CKEDITOR.tools.extend({}, data); evData.image = img; var result = editor.fire('simpleuploads.localImageReady', evData); // in v3 cancel() returns true and in v4 returns false // if not canceled it's the evdata object if ( typeof result == "boolean" ) return; CKEDITOR.plugins.simpleuploads.insertProcessedFile(ev.editor, evData); }; if (typeof data.file == "string") img.src = data.file; // base64 encoded else img.src = nativeURL.createObjectURL( data.file ); // FileReader }); // Setup listeners if the config specifies that they should be used if (config.simpleuploads_convertBmp) editor.on( 'simpleuploads.localImageReady', convertToBmp); if (config.simpleuploads_maximumDimensions) editor.on( 'simpleuploads.localImageReady', checkDimension); // workaround for image2 support editor.on( 'simpleuploads.finishedUpload' , function(ev) { if (editor.widgets && editor.plugins.image2) { var element = ev.data.element; if (element.getName()=="img") { var widget = editor.widgets.getByElement(element); if (widget) { widget.data.src = element.data( 'cke-saved-src' ); widget.data.width = element.$.width; widget.data.height = element.$.height; } else { // They have renamed the widget after the initial release :-( // Let's try with both, one of them will work editor.widgets.initOn(element, "image2"); editor.widgets.initOn(element, "image"); } } } }); // Paste from clipboard: editor.on( "paste", function(e) { var pasteData = e.data, html = (pasteData.html || ( pasteData.type && pasteData.type=='html' && pasteData.dataValue)); if (!html) return; // strip out webkit-fake-url as they are useless: if (CKEDITOR.env.webkit && (html.indexOf("webkit-fake-url")>0) ) { alert("Sorry, the images pasted with Safari aren't usable"); window.open("https://bugs.webkit.org/show_bug.cgi?id=49141"); html = html.replace( //g, ""); } // Handles image pasting in Firefox // Replace data: images in Firefox and upload them. // No longer required with Firefox 22 html = html.replace( //g, function( img ) { if (!editor.config.filebrowserImageUploadUrl) return ""; var match = img.match(/"(data:image\/(.{3,4});base64,.*?)"/), imgData = match[1], type = match[2].toLowerCase(), id = CKEDITOR.plugins.simpleuploads.getTimeStampId(); // If it's too small then leave it as is. if (imgData.length<128) return img; if (type=="jpeg") type="jpg"; var fileName = id + '.' + type, uploadData = { context : "pastedimage", name : fileName, id : id, forceLink : false, file : imgData, mode : { type: "base64paste"} }; if (!uploadFile(editor, uploadData)) return uploadData.result; var animation = uploadData.element, content = animation.$.innerHTML; animation.$.innerHTML = " "; // only once editor.on( "afterPaste" , function( ev ) { ev.removeListener(); var span = editor.document.$.getElementById(id); if (!span) return; // fight against ACF in v4.1 and IE11, insert svg afterwards span.innerHTML = content; setupCancelButton( editor, uploadData ); } ); return animation.getOuterHtml(); }); if (e.data.html) e.data.html = html; else e.data.dataValue = html; }); var avoidBadUndo = function(e) { if (editor.mode != "wysiwyg") return; var root = editor.document; if (editor.editable) root = editor.editable(); // detect now if the contents include our tmp node if (root.$.querySelector( ".SimpleUploadsTmpWrapper") ) { var move = e.name.substr(5).toLowerCase(); // If the user tried to redo but there are no more saved images forward and this is a bad image, move back instead. if ( move=="redo" && editor.getCommand(move).state == CKEDITOR.TRISTATE_DISABLED ) move = "undo"; // Move one extra step back/forward editor.execCommand( move ); } }; // on dev mode plugins might not load in the right order with empty cache var cmd = editor.getCommand('undo'); cmd && cmd.on('afterUndo', avoidBadUndo ); cmd = editor.getCommand('redo'); cmd && editor.getCommand('redo').on('afterRedo', avoidBadUndo ); // http://dev.ckeditor.com/ticket/10101 editor.on('afterUndo', avoidBadUndo ); editor.on('afterRedo', avoidBadUndo ); // Buttons to launch the file picker easily // Files editor.addCommand( 'addFile', { exec: function( editor ) { PickAndSendFile(editor, false, this); } }); editor.ui.addButton( 'addFile', { label: editor.lang.simpleuploads.addFile, command: 'addFile', icon : this.path + 'icons/addfile.png', // %REMOVE_LINE_CORE% toolbar: 'insert', allowedContent : 'a[!href];span[id](SimpleUploadsTmpWrapper);', requiredContent : 'a[href]' }); // Images editor.addCommand( 'addImage', { exec: function( editor ) { PickAndSendFile(editor, true, this); } }); editor.ui.addButton( 'addImage', { label: editor.lang.simpleuploads.addImage, command: 'addImage', icon : this.path + 'icons/addimage.png', // %REMOVE_LINE_CORE% toolbar: 'insert', allowedContent : 'img[!src,width,height];span[id](SimpleUploadsTmpWrapper);', requiredContent : 'img[src]' }); if (typeof FormData == 'undefined') return; var root, visibleRoot, pasteRoot; var minX=-1, minY, maxX, maxY; // Hint in the main document var mainMinX=-1, mainMinY, mainMaxX, mainMaxY; var removeBaseHighlight = function() { var dialog = CKEDITOR.dialog.getCurrent(); if ( dialog ) { var div = dialog.parts.title.getParent(); div.removeClass( 'SimpleUploadsOverCover' ); } else { editor.container.removeClass( 'SimpleUploadsOverContainer' ); } }; editor.on( 'destroy', function() { CKEDITOR.removeListener( 'simpleuploads.droppedFile', removeBaseHighlight); CKEDITOR.document.removeListener( 'dragenter', CKEDITORdragenter); CKEDITOR.document.removeListener( 'dragleave', CKEDITORdragleave); domUnload(); }); var domUnload = function() { if (!root || !root.removeListener) return; pasteRoot.removeListener( 'paste', pasteListener); root.removeListener( 'dragenter', rootDragEnter); root.removeListener( 'dragleave', rootDragLeave); root.removeListener( 'dragover', rootDragOver); root.removeListener( 'drop', rootDropListener); pasteRoot = null; root = null; visibleRoot = null; }; CKEDITOR.on( 'simpleuploads.droppedFile', removeBaseHighlight); var CKEDITORdragenter = function(e) { if (mainMinX == -1) { if (!hasFiles(e)) return; var dialog = CKEDITOR.dialog.getCurrent(); if ( dialog ) { if (!dialog.handleFileDrop) return; var div = dialog.parts.title.getParent(); div.addClass( 'SimpleUploadsOverCover' ); } else { if (!editor.readOnly) editor.container.addClass( 'SimpleUploadsOverContainer' ); } mainMinX=0; mainMinY=0; mainMaxX=CKEDITOR.document.$.body.parentNode.clientWidth; mainMaxY=CKEDITOR.document.$.body.parentNode.clientHeight; } }; var CKEDITORdragleave = function(e) { if ( mainMinX == -1 ) return; var ev = e.data.$; if ((ev.clientX<=mainMinX) || (ev.clientY<=mainMinY) || (ev.clientX>=mainMaxX) || (ev.clientY>=mainMaxY)) { removeBaseHighlight(); mainMinX = -1; } }; CKEDITOR.document.on( 'dragenter', CKEDITORdragenter); CKEDITOR.document.on( 'dragleave', CKEDITORdragleave); var rootDropListener = function(e) { // editor visibleRoot.removeClass( "SimpleUploadsOverEditor" ); minX = -1; //container // We fire an event on CKEDITOR so all the instances get notified and remove their class // This is an "internal" event to the plugin CKEDITOR.fire( 'simpleuploads.droppedFile' ); mainMinX = -1; if (editor.readOnly) { e.data.preventDefault(); return false; } var ev = e.data.$, data = ev.dataTransfer; if ( data && data.files && data.files.length>0 ) { // Create Undo image editor.fire( "saveSnapshot" ); // Prevent default insertion e.data.preventDefault(); var dropLocation = { ev : ev, range : false, count : data.files.length, rangeParent : ev.rangeParent, rangeOffset : ev.rangeOffset }; // store the location for IE if (!dropLocation.rangeParent && !document.caretRangeFromPoint) { if (ev.target.nodeName.toLowerCase()!="img") { var doc = editor.document.$; if ( doc.body.createTextRange ) { var textRange = doc.body.createTextRange(); try { textRange.moveToPoint( ev.clientX, ev.clientY ); dropLocation.range = textRange; } catch (ex) { } } } } for( var i=0; i=maxX) || (ev.clientY>=maxY)) { visibleRoot.removeClass( "SimpleUploadsOverEditor" ); minX = -1; } }; var rootDragOver = function(e) { if (minX != -1 ) { if (editor.readOnly) { e.data.$.dataTransfer.dropEffect = "none"; e.data.preventDefault(); return false; } // Show Copy instead of Move. Works for Chrome // Firefox and IE10 don't respect this change (Firefox by default doesn't enter here) // https://bugzilla.mozilla.org/show_bug.cgi?id=484511 e.data.$.dataTransfer.dropEffect = 'copy'; // IE always requires this // Chrome almost fixed the requirement, but it's required if the body is a single line and the user drops below it. if (!CKEDITOR.env.gecko) e.data.preventDefault(); } }; // drag & drop, paste editor.on( 'contentDom', function(ev) { root = editor.document; visibleRoot = root.getBody().getParent(); // v4 inline editing // ELEMENT_MODE_INLINE if (editor.elementMode == 3 ) { root = editor.editable(); visibleRoot = root; } // v4 divArea if ( editor.elementMode == 1 && 'divarea' in editor.plugins ) { root = editor.editable(); visibleRoot = root; } pasteRoot = editor.editable ? editor.editable() : root; // Special case for IE in forcePasteAsPlainText: // CKEditor uses the beforepaste event to move the target, but we can't use that to check for files, // so in that case, set a listener on the document on each paste if (CKEDITOR.env.ie && CKEDITOR.env.version>=11 && editor.config.forcePasteAsPlainText && editor.editable().isInline()) { // Is an editable instance, so let's use attachListener here pasteRoot.attachListener(pasteRoot, "beforepaste", function( bpEv ) { // Only once, so we can check always which editor the paste belongs to editor.document.on( "paste", function( pEv ) { pEv.removeListener(); // redirect the original data to our paste listener pasteListener(pEv); }, null, {editor : editor }); }); } else { // For everyone else, use the normal paste event pasteRoot.on( "paste", pasteListener, null, {editor : editor }, 8); } root.on( 'dragenter', rootDragEnter); root.on( 'dragleave', rootDragLeave); // https://bugs.webkit.org/show_bug.cgi?id=57185 if ( !CKEDITOR.env.gecko ) { root.on( 'dragover', rootDragOver); } // Must use CKEditor 3.6.3 for IE 10 root.on( 'drop', rootDropListener); }); editor.on( 'contentDomUnload', domUnload); editor.plugins.fileDropHandler = { addTarget : function( target, callback ) { target.on( 'dragenter', function(e) { if (minX == -1) { if (!hasFiles(e)) return; target.addClass( 'SimpleUploadsOverDialog' ); var rect = target.$.getBoundingClientRect(); minX=rect.left; minY=rect.top; maxX=minX + target.$.clientWidth; maxY=minY + target.$.clientHeight; } }); target.on( 'dragleave', function(e) { if ( minX == -1 ) return; var ev = e.data.$; if ((ev.clientX<=minX) || (ev.clientY<=minY) || (ev.clientX>=maxX) || (ev.clientY>=maxY)) { target.removeClass( 'SimpleUploadsOverDialog' ); minX = -1; } }); target.on( 'dragover', function(e) { if (minX != -1 ) { // Show Copy instead of Move. Works for Chrome // Firefox and IE10 don't respect this change (Firefox by default doesn't enter here) // https://bugzilla.mozilla.org/show_bug.cgi?id=484511 e.data.$.dataTransfer.dropEffect = 'copy'; e.data.preventDefault(); } }); target.on( 'drop', function(e) { target.removeClass( 'SimpleUploadsOverDialog' ); minX = -1; //container // We fire an event on CKEDITOR so all the instances get notified and remove their class // This is an "internal" event to the plugin CKEDITOR.fire( 'simpleuploads.droppedFile' ); mainMinX = -1; var ev = e.data.$, data = ev.dataTransfer; if ( data && data.files && data.files.length>0 ) { // Prevent default insertion e.data.preventDefault(); // only one for( var i=0; i<1; i++) { var file = data.files[ i ]; var evData = { context : ev, name : file.name, file : file, id : CKEDITOR.tools.getNextId(), forceLink : false, callback : callback, mode : { type: "callback" } }; CKEDITOR.plugins.simpleuploads.processFileWithCallback( editor, evData ); } } }); } }; }, //Init afterInit: function( editor ) { var dataProcessor = editor.dataProcessor, htmlFilter = dataProcessor && dataProcessor.htmlFilter; if ( htmlFilter ) htmlFilter.addRules( htmlFilterRules , {applyToAll :true} ); } } ); // API CKEDITOR.plugins.simpleuploads = { getTimeStampId : (function() { var counter = 0; return function() { counter++; return (new Date()).toISOString().replace(/\..*/, "").replace(/\D/g, "_") + counter; }; })(), isImageExtension: function(editor, filename) { if (!editor.config.simpleuploads_imageExtensions) return false; var imageRegexp = new RegExp( "\.(?:" + editor.config.simpleuploads_imageExtensions + ")$", "i"); return imageRegexp.test( filename ); }, // Main entry point for callbacks insertProcessedFile: function(editor, evData) { evData.element = null; evData.id = this.getTimeStampId(); // new id var that = this; switch (evData.mode.type) { case 'selectedFile': window.setTimeout( function() { that.insertSelectedFile( editor, evData ); }, 50); break; case 'pastedFile': this.insertPastedFile( editor, evData ); break; case 'callback': window.setTimeout( function() { that.processFileWithCallback( editor, evData ); }, 50); break; case 'droppedFile': this.insertDroppedFile( editor, evData ); break; case 'base64paste': this.insertBase64File( editor, evData ); break; default: alert("Error, no valid type", evData.mode); break; } }, // Insert a file from the toolbar buttons insertSelectedFile: function(editor, evData) { var mode = evData.mode, i = mode.i, count = mode.count; // Upload the file if (!uploadFile( editor, evData )) return; var element = evData.element; if (!element) return; if (count == 1) { var selection = editor.getSelection(), selected = selection.getSelectedElement(), originalNode; // If it's just one image and the user has another one selected, replace it if (selected && selected.getName() == "img" && element.getName() == "span") { originalNode = selected.$; } // Image2 widget if (editor.widgets) { var focused = editor.widgets.focused; if (focused && focused.wrapper.equals(selected)) { originalNode = selected.$.querySelector("img"); } } // a link if (element.getName() == "a") { var parent = selected, ranges = selection.getRanges(), range = ranges && ranges[0]; if (!parent) { if (ranges && ranges.length == 1) { parent = range.startContainer.$; if (parent.nodeType == document.TEXT_NODE) parent = parent.parentNode; } } while ( parent && (parent.nodeType == document.ELEMENT_NODE) && (parent.nodeName.toLowerCase() != "a") ) parent = parent.parentNode; if ( parent && parent.nodeName && parent.nodeName.toLowerCase() == "a" ) { originalNode = parent; } // there was no link, check the best way to create one: // create a link if (!originalNode && range && (selected || !range.collapsed) ) { var style = new CKEDITOR.style({ element: 'a', attributes: {href:'#'} } ); style.type = CKEDITOR.STYLE_INLINE; // need to override... dunno why. style.applyToRange( range ); parent = range.startContainer.$; if (parent.nodeType == document.TEXT_NODE) parent = parent.parentNode; originalNode = parent; } } if (originalNode) { originalNode.parentNode.replaceChild(element.$, originalNode); evData.originalNode = originalNode; editor.fire( "saveSnapshot" ); return; } } // insert a space between links if (i>0 && element.getName()=="a") editor.insertHtml(" "); editor.insertElement(element); setupCancelButton( editor, evData ); }, // Insert a file that has been pasted into the content (as File) insertPastedFile: function(editor, evData) { // Upload the file if (!uploadFile( editor, evData )) return; var element = evData.element; var dialog = evData.mode.dialog; if (dialog) { editor.fire( "updateSnapshot" ); editor.insertElement(element); editor.fire( "updateSnapshot" ); } else { // Insert in the correct position after the pastebin has been removed var processElement = function() { // Check if there's a valid selection or if it's the pastebin var ranges = editor.getSelection().getRanges(); if (!ranges.length) { // Put back in the queue window.setTimeout(processElement, 0); return; } // verify that it has really been removed if (editor.editable && editor.editable().$.querySelector("#cke_pastebin")) { // Put back in the queue window.setTimeout(processElement, 0); return; } editor.fire( "updateSnapshot" ); editor.insertElement(element); editor.fire( "updateSnapshot" ); setupCancelButton( editor, evData ); }; window.setTimeout(processElement, 0); } }, // The evData includes a callback that takes care of everything (a file dropped in a dialog) processFileWithCallback: function(editor, evData) { uploadFile( editor, evData ); }, insertDroppedFile: function(editor, evData) { if (!uploadFile( editor, evData )) return; var element = evData.element; var dropLocation = evData.mode.dropLocation, range = dropLocation.range, ev = dropLocation.ev, count = dropLocation.count; // if we're adding several links, add a space between them if ( range && element.getName()=="a" ) { if ( range.pasteHTML ) range.pasteHTML( " " ); // simple space doesn't work else range.insertNode( editor.document.$.createTextNode( ' ' ) ); } var target = ev.target; if (!range) { var doc = editor.document.$; // Move to insertion point /* standard way: only implemented in Firefox 20 if (document.caretPositionFromPoint) { var caret = document.caretPositionFromPoint(ev.pageX, ev.pageY), textNode = caret.offsetNode, offset = caret.offset; } */ // Firefox, custom properties in event. if ( dropLocation.rangeParent ) { // it seems that they aren't preserved in the ev after resending back the info var node = dropLocation.rangeParent, offset = dropLocation.rangeOffset; range = doc.createRange(); range.setStart( node, offset ); range.collapse( true ); } else { // Webkit, old documentView API if ( document.caretRangeFromPoint ) { range = doc.caretRangeFromPoint( ev.clientX, ev.clientY ); } else { // IE if (target.nodeName.toLowerCase()=="img") { range = doc.createRange(); range.selectNode(target); } else if ( document.body.createTextRange ) { var textRange = doc.body.createTextRange(); try { textRange.moveToPoint( ev.clientX, ev.clientY ); /* // Convert to W3C range: var node = textRange.parentElement(); var start = Math.abs( textRange.duplicate().moveStart('character', -1000000) ); var r = textRange.duplicate(); r.moveToElementText( node ); r.collapse(); var start2 = Math.abs( r.moveStart('character', -1000000) ); range = doc.createRange(); range.setStart( node.firstChild, start - start2 ); range.collapse( true ); */ range = textRange; } catch (ex) { range = doc.createRange(); range.setStartAfter( doc.body.lastChild ); range.collapse( true ); } } } } dropLocation.range = range; } var elementName = element.getName(), handled = false; if ( count==1 ) { if (target.nodeName.toLowerCase() == "img" && elementName == "span" ) { target.parentNode.replaceChild(element.$, target); evData.originalNode = target; handled = true; } if ( elementName == "a" ) { var start; if (range.startContainer) { start = range.startContainer; if (start.nodeType == document.TEXT_NODE) start = start.parentNode; else { if (range.startOffset < start.childNodes.length) start = start.childNodes[ range.startOffset ]; } } else start = range.parentElement(); if (!start || target.nodeName.toLowerCase() == "img") start = target; var parent = start; while ( parent && (parent.nodeType == document.ELEMENT_NODE) && (parent.nodeName.toLowerCase() != "a") ) parent = parent.parentNode; if ( parent && parent.nodeName && parent.nodeName.toLowerCase() == "a" ) { parent.parentNode.replaceChild(element.$, parent); evData.originalNode = parent; handled = true; } // dropping on an image without a parent link if ( !handled && start.nodeName.toLowerCase() == "img" ) { parent = start.ownerDocument.createElement('a'); parent.href = '#'; start.parentNode.replaceChild(parent, start); parent.appendChild(start); parent.parentNode.replaceChild(element.$, parent); evData.originalNode = parent; handled = true; } } } if (!handled) { if (range) { if ( range.pasteHTML ) range.pasteHTML( element.$.outerHTML ); else range.insertNode( element.$ ); } else editor.insertElement( element ); } setupCancelButton( editor, evData ); editor.fire( "saveSnapshot" ); }, insertBase64File: function(editor, evData) { delete evData.result; var id = evData.mode.id; var tmp = editor.document.getById(id); if (!uploadFile(editor, evData)) { tmp.remove(); if (evData.result) editor.insertHTML(evData.result); return; } editor.getSelection().selectElement(tmp); editor.insertElement(evData.element); setupCancelButton( editor, evData ); } }; // Creates the element, but doesn't insert it function createPreview(editor, data) { var isImage = CKEDITOR.plugins.simpleuploads.isImageExtension( editor, data.name ), showImageProgress = !editor.config.simpleuploads_hideImageProgress, element; // Create and insert our element if ( !data.forceLink && isImage && showImageProgress) { element = createSVGAnimation(data.file, data.id, editor); } else { if ( isImage && !data.forceLink ) element = new CKEDITOR.dom.element( "span", editor.document ); else element = new CKEDITOR.dom.element( "a", editor.document ); element.setAttribute( "id", data.id ); element.setAttribute( "class", "SimpleUploadsTmpWrapper"); //element.setText( data.name ); var html = "" + data.name + "" + " " + " x"; element.setHtml(html); } // Prevent selection handles in IE element.setAttribute( 'contentEditable', false ); data.element=element; } function errorListener(e) { e.removeListener(); alert("Failed to load the image with the provided URL: '" + e.sender.data( 'cke-saved-src') + "'"); e.listenerData.remove(); } function checkLoadedImage(img, editor, el, name) { if (img.$.naturalWidth === 0) { // when replacing an image, IE might fire the load event, but it still uses the old data window.setTimeout( function() {checkLoadedImage(img, editor, el, name);}, 50); return; } img.replace( el ); img.setAttribute("width", img.$.width); img.setAttribute("height", img.$.height); editor.fire('simpleuploads.finishedUpload', { name: name, element: img } ); // Correct the Undo image editor.fire( "updateSnapshot" ); } // Sets up a XHR object to handle the upload function createXHRupload(editor, data) { var isImage = CKEDITOR.plugins.simpleuploads.isImageExtension( editor, data.name ), attribute = "href", forImage = false; if ( !data.forceLink && isImage ) { attribute = "src"; forImage=true; } if (data.callback) data.callback.setup(data); if (!data.url) data.url = getUploadUrl(editor, 2, forImage); if (data.requiresImage && !isImage) { alert(editor.lang.simpleuploads.nonImageExtension); return null; } var result = editor.fire("simpleuploads.startUpload", data); // in v3 cancel() returns true and in v4 returns false // if not canceled it's the data object, so let's use that. if ( typeof result == "boolean" ) return null; // instead of uploading, use base64 encoded data if (data.url == "base64") { if (typeof data.file == "string") { setTimeout( function() { var fileUrl = data.file, id = data.id, el = editor.document.getById( id ); receivedUrl(fileUrl, data, editor, el, attribute); }, 100); return {}; } else { var reader = new FileReader(); reader.onload = function() { setTimeout( function() { var fileUrl = reader.result, id = data.id, el = editor.document.getById( id ); receivedUrl(fileUrl, data, editor, el, attribute); }, 100); }; reader.readAsDataURL( data.file ); } return {}; } var xhr = new XMLHttpRequest(), target = xhr.upload; // nice progress effect. Opera used to lack xhr.upload if ( target ) { target.onprogress = function( evt ) { updateProgress(editor, data.id, evt); }; } data.xhr = xhr; // Upload the file xhr.open("POST", data.url ); xhr.onload = function() { var id = data.id, el = editor.document.getById( id ), fileUrl, msg; // final update updateProgress(editor, id, null); // Correct the Undo image editor.fire( "updateSnapshot" ); var evtData = { xhr: xhr, data: data, element:el }; var result = editor.fire("simpleuploads.serverResponse", evtData); // in v3 cancel() returns true and in v4 returns false // if not canceled it's the evdata object if ( typeof result == "boolean" ) return; // if the listener has Cancelled the event, exit and we suppose that it took care of everything by itself. // Check if the event has been listened and performed its own parsing if (typeof evtData.url == "undefined") { // Upon finish, get the url and update the file //var parts = xhr.responseText.match(/2,\s*("|')(.*?[^\\]?)\1(?:,\s*\1(.*?[^\\]?)\1)?\s*\)/), //var parts = xhr.responseText.match(/\((?:"|')?\d+(?:"|')?,\s*("|')(.*?[^\\]?)\1(?:,\s*(.*?))?\s*\)\s*;?\s*<\/script>/), var parts = xhr.responseText.match(/\((?:"|')?\d+(?:"|')?,\s*("|')(.*?[^\\]?)\1(?:,\s*(.*?))?\s*\)\s*;?/); fileUrl = parts && parts[2]; msg = parts && parts[3]; // The server response usually is automatically parsed by the js engine, but in this case we get the "raw content" // and must take care of un-escaping it. // So far I haven't been able to find a single function that does it correctly in all the cases if (fileUrl) { fileUrl = fileUrl.replace(/\\'/g, "'"); // Try to handle URLs with escaped chars like 51-Body/\u00E6\u00F8\u00E5.jpg try { var o = JSON.parse('{"url":"' + fileUrl + '"}'); if (o && o.url) fileUrl = o.url; } catch (ex) { } } if (msg) { // find out if it was a function or a string message: var matchFunction = msg.match(/function\(\)\s*\{(.*)\}/); if (matchFunction) msg = new Function( matchFunction[1] ); else { var first=msg.substring(0,1); if (first=="'" || first=='"') msg = msg.substring( 1, msg.length-1 ); } } if (!parts) { msg = 'Error posting the file to ' + data.url + '\r\nInvalid data returned (check console)'; if (window.console) console.log(xhr.responseText); } } else { fileUrl = evtData.url; msg = ""; } editor.fire('simpleuploads.endUpload', { name: data.name, ok: (!!fileUrl), xhr : xhr, data : data } ); if (xhr.status!=200) { if (xhr.status == 413) alert( editor.lang.simpleuploads.fileTooBig ); else alert('Error posting the file to ' + data.url + '\r\nResponse status: ' + xhr.status); if (window.console) console.log(xhr); } if (data.callback) { if (!fileUrl && msg) alert( msg ); data.callback.upload(fileUrl, msg, data); return; } // If the element doesn't exists it means that the user has deleted it or pressed undo while uploading // so let's get out if (!el) return; if ( fileUrl ) { receivedUrl(fileUrl, data, editor, el, attribute); } else { if (data.originalNode) el.$.parentNode.replaceChild(data.originalNode, el.$); else el.remove(); if (msg) alert( msg ); } // Correct undo image editor.fire( "updateSnapshot" ); }; xhr.onerror = function(e) { alert('Error posting the file to ' + data.url ); if (window.console) console.log(e); var el = editor.document.getById( data.id ); if (el) { if (data.originalNode) el.$.parentNode.replaceChild(data.originalNode, el.$); else el.remove(); } // Correct undo image editor.fire( "updateSnapshot" ); }; xhr.onabort = function(e) { if (data.callback) { data.callback.upload(null); return; } var el = editor.document.getById( data.id ); if (el) { if (data.originalNode) el.$.parentNode.replaceChild(data.originalNode, el.$); else el.remove(); } // Correct undo image editor.fire( "updateSnapshot" ); }; // CORS https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS xhr.withCredentials = true; return xhr; } // Takes care of uploading the file using XHR function uploadFile(editor, data) { if (!data.callback) createPreview(editor, data); var xhr = createXHRupload(editor, data); if (!xhr) { data.result = data.result || ""; return false; } // FileReader if (!xhr.send) return true; if (data.callback && data.callback.start) data.callback.start(data); var inputName = data.inputName|| getUploadInputName(editor); if (typeof data.file == "string") sendBase64File( data, xhr, inputName); else sendBlobFile( data, xhr, inputName); return true; } function sendBlobFile(data, xhr, inputName) { var formdata = new FormData(); formdata.append( inputName, data.file, data.name ); // Add extra fields if provided if (data.extraFields) { var obj = data.extraFields; for (var prop in obj) { if( obj.hasOwnProperty( prop ) ) formdata.append( prop, obj[prop] ); } } if (data.extraHeaders) { var headers = data.extraHeaders; for (var header in headers){ if( headers.hasOwnProperty( header )){ xhr.setRequestHeader(header, headers[header]); } } } xhr.send( formdata ); } function sendBase64File(data, xhr, inputName) { // Create the multipart data upload. var BOUNDARY = "---------------------------1966284435497298061834782736", rn = "\r\n", req = "--" + BOUNDARY, type = data.name.match(/\.(\w+)$/)[1]; req += rn + 'Content-Disposition: form-data; name="' + inputName + '"; filename="' + data.name + '"'; req += rn + "Content-type: image/" + type; req += rn + rn + window.atob( data.file.split(',')[1] ); req += rn + "--" + BOUNDARY; // Add extra fields if provided if (data.extraFields) { var obj = data.extraFields; for (var prop in obj) { req += rn + "Content-Disposition: form-data; name=\"" + unescape(encodeURIComponent( prop )).replace(/=/g, "\\=") + "\""; req += rn + rn + unescape(encodeURIComponent( obj[prop] )) ; req += rn + "--" + BOUNDARY; } } req += "--"; xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); /* if (xhr.sendAsBinary) xhr.sendAsBinary(req); else { // emulate sendAsBinary for IE11 & Chrome */ var bufferData = new ArrayBuffer(req.length); var ui8a = new Uint8Array(bufferData, 0); for (var i = 0; i < req.length; i++) ui8a[i] = (req.charCodeAt(i) & 0xff); xhr.send(ui8a); /* } */ } function updateProgress(editor, id, evt) { if (!editor.document || !editor.document.$) return; var dialog = CKEDITOR.dialog.getCurrent(); var doc = (dialog ? CKEDITOR : editor ).document.$, rect = doc.getElementById("rect" + id), text = doc.getElementById("text" + id), value, textValue; if ( evt ) { if ( !evt.lengthComputable ) return; value = (100*evt.loaded/evt.total).toFixed(2) + "%"; textValue = (100*evt.loaded/evt.total).toFixed() + "%"; } else { textValue = editor.lang.simpleuploads.processing; value = "100%"; /* if (text) { text.parentNode.removeChild(text); text = null; } */ } if (rect) { rect.setAttribute("width", value); rect.style.width = value; if (!evt) { var parent = rect.parentNode; if (parent && parent.className=="uploadRect") parent.parentNode.removeChild(parent); } } if (text) { text.firstChild.nodeValue = textValue; if (!evt) { // Remove cancel button var sibling = text.nextSibling; if (sibling && sibling.nodeName.toLowerCase()=="a") sibling.parentNode.removeChild(sibling); } } } // Show a grayscale version of the image that animates toward the full color version function createSVGAnimation( file, id, editor ) { var element = new CKEDITOR.dom.element( "span", editor.document ), div = element.$, useURL, doc = editor.document.$, span = doc.createElement("span"); element.setAttribute( "id", id ); element.setAttribute( "class", "SimpleUploadsTmpWrapper"); var rectSpan = doc.createElement("span"); rectSpan.setAttribute( "id" , "text" + id); rectSpan.appendChild( doc.createTextNode("0 %")); div.appendChild(span); span.appendChild(rectSpan); var cancelSpan = doc.createElement("span"); cancelSpan.appendChild( doc.createTextNode('x')); span.appendChild(cancelSpan); if (typeof file != "string") { if ( !nativeURL || !nativeURL.revokeObjectURL) return element; useURL = true; } var svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.setAttribute( "id" , "svg" + id); // just to find out the image dimensions as they are needed for the svg block var img = doc.createElement( "img" ); if (useURL) { img.onload = function(e) { if (this.onload) { nativeURL.revokeObjectURL( this.src ); this.onload = null; } // in IE it's inserted with the HTML, so we can't reuse the svg object var svg = doc.getElementById("svg" + id); if (svg) { svg.setAttribute("width", this.width + "px"); svg.setAttribute("height", this.height + "px"); } // Chrome var preview = doc.getElementById(id); if (preview) preview.style.width = this.width + "px"; }; img.src = nativeURL.createObjectURL( file ); } else { // base64 data, dimensions are available right now in Firefox img.src = file; // extra protection img.onload = function(e) { this.onload = null; // we're pasting so it's inserted with the HTML, so we can't reuse the svg object var svg = doc.getElementById("svg" + id); if (svg) { svg.setAttribute("width", this.width + "px"); svg.setAttribute("height", this.height + "px"); } }; svg.setAttribute("width", img.width + "px"); svg.setAttribute("height", img.height + "px"); } div.appendChild(svg); var filter = doc.createElementNS("http://www.w3.org/2000/svg", "filter"); filter.setAttribute("id", "SVGdesaturate"); svg.appendChild(filter); var feColorMatrix = doc.createElementNS("http://www.w3.org/2000/svg", "feColorMatrix"); feColorMatrix.setAttribute("type", "saturate"); feColorMatrix.setAttribute("values", "0"); filter.appendChild(feColorMatrix); var clipPath = doc.createElementNS("http://www.w3.org/2000/svg", "clipPath"); clipPath.setAttribute("id", "SVGprogress" + id); svg.appendChild(clipPath); var rect = doc.createElementNS("http://www.w3.org/2000/svg", "rect"); rect.setAttribute("id", "rect" + id); rect.setAttribute("width", "0"); rect.setAttribute("height", "100%"); clipPath.appendChild(rect); var image = doc.createElementNS("http://www.w3.org/2000/svg", "image"); image.setAttribute("width", "100%"); image.setAttribute("height", "100%"); if (useURL) { image.setAttributeNS('http://www.w3.org/1999/xlink',"href", nativeURL.createObjectURL( file )); var loaded = function( e ) { nativeURL.revokeObjectURL( image.getAttributeNS('http://www.w3.org/1999/xlink',"href") ); image.removeEventListener( "load", loaded, false); }; image.addEventListener( "load", loaded, false); } else image.setAttributeNS('http://www.w3.org/1999/xlink',"href", file ); var image2 = image.cloneNode(true); image.setAttribute("filter", "url(#SVGdesaturate)"); image.style.opacity="0.5"; svg.appendChild(image); image2.setAttribute("clip-path", "url(#SVGprogress" + id + ")"); svg.appendChild(image2); return element; } // Compatibility between CKEditor 3 and 4 if (CKEDITOR.skins) { CKEDITOR.plugins.setLang = CKEDITOR.tools.override( CKEDITOR.plugins.setLang , function( originalFunction ) { return function( plugin, lang, obj ) { if (plugin != "devtools" && typeof obj[plugin] != "object") { var newObj = {}; newObj[ plugin ] = obj; obj = newObj; } originalFunction.call(this, plugin, lang, obj); }; }); } function createSimpleUpload( editor, dialogName, definition, element ) { if (element.type=="file") return; var forImage = (dialogName.substr(0,5)=="image" || element.requiresImage); var targetField = element.filebrowser.target.split(":"); var callback = { setup : function(data) { if (!definition.uploadUrl) return; if (forImage) data.requiresImage = true; var params = {}; params.CKEditor = editor.name; params.CKEditorFuncNum = 2; params.langCode = editor.langCode; data.url = addQueryString( definition.uploadUrl, params ); }, start : function(data) { var dialog = CKEDITOR.dialog.getCurrent(); dialog.showThrobber(); var throbber = dialog.throbber; if (data.xhr) { var html = "" + data.name + "" + " " + " x"; throbber.throbberTitle.setHtml(html); var cover = throbber.throbberCover; var xhr = data.xhr; if ( cover.timer ) { clearInterval( cover.timer ) ; cover.timer = null ; } throbber.throbberParent.setStyle("display", "none"); throbber.throbberTitle.getLast().on('click', function() { xhr.abort(); }); // protection to check that the upload isn't pending when forcing to close the dialog dialog.on('hide', function() { if (xhr.readyState == 1) xhr.abort(); }); } throbber.center(); }, upload : function(url, msg, data) { var dialog = CKEDITOR.dialog.getCurrent(); dialog.throbber.hide(); if ( typeof msg == 'function' && msg.call( data.context.sender ) === false ) return; if (definition.onFileSelect) { if ( definition.onFileSelect.call( data.context.sender, url, msg ) === false ) return; } if (!url) return; dialog.getContentElement( targetField[ 0 ], targetField[ 1 ] ).setValue( url ); dialog.selectPage( targetField[ 0 ] ); } }; if (element.filebrowser.action == "QuickUpload") { definition.hasQuickUpload = true; definition.onFileSelect = null; if (!editor.config.simpleuploads_respectDialogUploads) { element.label = ( forImage ? editor.lang.simpleuploads.addImage : editor.lang.simpleuploads.addFile); element.onClick = function( evt ) { // "element" here means the definition object, so we need to find the correct // button to scope the event call //var sender = evt.sender; PickAndSendFile(editor, forImage, evt, callback); return false; }; var picker = definition.getContents( element[ "for" ][ 0 ] ).get( element[ "for" ][ 1 ] ); picker.hidden = true; } } else { // if the dialog has already been configured with quickUpload there's no need to use the file browser config if (definition.hasQuickUpload) return; if (element.filebrowser.onSelect) definition.onFileSelect = element.filebrowser.onSelect; } if (!editor.plugins.fileDropHandler) return; if (element.filebrowser.action == "QuickUpload") definition.uploadUrl = element.filebrowser.url; var original = definition.onShow || (function(){}); definition.onShow = CKEDITOR.tools.override( original, function( original ) { return function() { if ( typeof original == 'function' ) original.call( this ); if (element.filebrowser.action != "QuickUpload" && definition.hasQuickUpload) return; var dialog = this; if (dialog.handleFileDrop) return; dialog.handleFileDrop = true; dialog.getParentEditor().plugins.fileDropHandler.addTarget( dialog.parts.contents, callback ); }; } ); } // Searches for elements in the dialog definition where we can apply our enhancements function applySimpleUpload( editor, dialogName, definition, elements ) { for ( var i in elements ) { var element = elements[ i ]; // If due to some customization or external library the object isn't valid, skip it. if (!element) continue; if ( element.type == 'hbox' || element.type == 'vbox' || element.type == 'fieldset' ) applySimpleUpload( editor, dialogName, definition, element.children ); if ( element.filebrowser && element.filebrowser.url ) createSimpleUpload( editor, dialogName, definition, element ); } } function setupCancelButton( editor, data ) { var element = editor.document.getById( data.id ); if (!element) return; var links = element.$.getElementsByTagName("a"); if (!links || !links.length) { links = element.$.getElementsByTagName("span"); if (!links || !links.length) return; } for(var i=0; i