/**
 * @file SimpleUploads plugin for CKEditor
 *	Version: 4.3.11
 *	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 = "<form method='post' enctype='multipart/form-data' action='" + getUploadUrl(editor, fnNumber, forImage) + "'>" +
		"<input type='file' name='" + inputName + "' id='" + inputName + "'></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<count; i++)
			{
				var file = filePicker.files[i],
					evData = CKEDITOR.tools.extend({}, filePicker.simpleUploadData);

				evData.file = file;
				evData.name = file.name;
				evData.id = CKEDITOR.plugins.simpleuploads.getTimeStampId();
				evData.forceLink = filePickerForceLink;
				evData.mode = {
					type : 'selectedFile',
					i : i,
					count : count
				};
				CKEDITOR.plugins.simpleuploads.insertSelectedFile(filePickerEditor, evData);
			}
		});
	}

	filePicker.value='';
	filePicker.simpleUploadData = {
		context : caller,
		callback : callback,
		requiresImage : forImage
	};

	// Keep focus on the editor instance.
	if (CKEDITOR.env.webkit)
	{
		var manager = editor.focusManager;
		if (manager && manager.lock)
		{
			manager.lock();
			setTimeout( function() { manager.unlock(); }, 500 );
		}
	}
	filePicker.click();
}

	function getUploadUrl(editor, functionNumber, forImage) {
		var url = forImage ? editor.config.filebrowserImageUploadUrl : editor.config.filebrowserUploadUrl;
		if (url=="base64") return url;

		var params = {};
		params.CKEditor = editor.name;
		params.CKEditorFuncNum = functionNumber;
		params.langCode = editor.langCode;
		return addQueryString( url, params );
	}

	//	Returns the name that should be used for the file upload input
	function getUploadInputName(editor) {
		return editor.config.simpleuploads_inputname || "upload";
	}

	function setUrl( fileUrl, msg )
	{
		// The "msg" argument may be used to pass the error message to the editor.
		if ( typeof msg == "string" && msg && !fileUrl)
			alert( msg );

		var editor = filePickerEditor;
		editor.fire('simpleuploads.endUpload', { name: IEUpload_fileName, ok: (!!fileUrl) } );

		if (IEUpload_callback)
		{
			IEUpload_callback.upload( fileUrl, msg, {context: IEUpload_caller} );
			IEUpload_fileName = null;
			IEUpload_callback = null;
			IEUpload_caller = null;
			return;
		}

		if ( fileUrl ) {
			var element,
				attribute;
			if (filePickerForceLink)
			{
				element = new CKEDITOR.dom.element( "a", editor.document );
				element.setText( fileUrl.match(/\/([^\/]+)$/)[1] );
				attribute = "href";
			}
			else
			{
				element = new CKEDITOR.dom.element( "img", editor.document );
				attribute = "src";

				element.on("load", function(e) {
					e.removeListener();
					element.removeListener("error", errorListener);

					element.setAttribute("width", element.$.width);
					element.setAttribute("height", element.$.height);

					editor.fire('simpleuploads.finishedUpload', { name: IEUpload_fileName, element: element } );
				});

				element.on("error", errorListener, null, element);
			}
			element.setAttribute(attribute, fileUrl);
			element.data( "cke-saved-" + attribute, fileUrl);
			editor.insertElement(element);

			if (filePickerForceLink)
				filePickerEditor.fire("simpleuploads.finishedUpload", { name: IEUpload_fileName, element: element } );
		}

		IEUpload_fileName = null;
		IEUpload_caller = null;
	}

	/**
	* Adds (additional) arguments to given url.
	*
	* @param {String}
	*	url The url.
	* @param {Object}
	*	params Additional parameters.
	*/
	function addQueryString( url, params )
	{
		var queryString = [];

		if ( !params )
			return url;
		else
		{
			for ( var i in params )
				queryString.push( i + "=" + encodeURIComponent( params[ i ] ) );
		}

		return url + ( ( url.indexOf( "?" ) != -1 ) ? "&" : "?" ) + queryString.join( "&" );
	}

function hasFiles(e)
{
	var ev = e.data.$,
		data = ev.dataTransfer;

	if (!data || !data.types)
		return false;

	if (data.types.contains && data.types.contains("Files") && (!data.types.contains("text/html")) ) return true;
	if (data.types.indexOf && data.types.indexOf( "Files" )!=-1) return true;
	return false;
}

function receivedUrl(fileUrl, data, editor, el, attribute)
{
	if (el.$.nodeName.toLowerCase() == "span")
	{
		// create the final img, getting rid of the fake div
		var img;
		if (data.originalNode)
		{
			img = data.originalNode.cloneNode( true );
			// reset size
			img.removeAttribute("width");
			img.removeAttribute("height");
			img.style.width = "";
			img.style.height = "";
			img = new CKEDITOR.dom.element( img );
		}
		else
		{
			img = new CKEDITOR.dom.element( "img", editor.document );
		}

		img.data( 'cke-saved-src', fileUrl);
		img.setAttribute( 'src', fileUrl);

		// wait to replace until the image is loaded to prevent flickering
		img.on("load", function(e) {
			e.removeListener();
			img.removeListener( "error", errorListener);

			checkLoadedImage(img, editor, el, data.name);
		});

		img.on("error", errorListener, null, el);

		// in case the user tries to get the html right now, a little protection
		el.data('cke-real-element-type', "img");
		el.data('cke-realelement', encodeURIComponent( img.getOuterHtml() ));
		el.data('cke-real-node-type', CKEDITOR.NODE_ELEMENT);

		return;
	}
	if (data.originalNode)
	{
		var newEl = data.originalNode.cloneNode( true );
		el.$.parentNode.replaceChild(newEl, el.$);
		el = new CKEDITOR.dom.element( newEl );
	}
	else
	{
		el.removeAttribute( "id" );
		el.removeAttribute( "class" );
		el.removeAttribute( 'contentEditable' );
		el.setHtml( el.getFirst().getHtml() );
	}

	el.data( 'cke-saved-' + attribute, fileUrl);
	el.setAttribute( attribute, fileUrl);
	editor.fire('simpleuploads.finishedUpload', { name: data.name, element: el } );
}

// store a reference of the native URL object as the CodeCog latex editor overwrites it
// http://www.codecogs.com/pages/forums/pagegen.php?id=2803
var nativeURL = window.URL || window.webkitURL;

CKEDITOR.plugins.add( "simpleuploads",
{
	//lang : 'en,es',		v4 style for builder not compatible with v3
	lang : ['en','ar','cs','de','es','fr','he','hu','it','ja','ko','nl','pl','pt-br','ru','tr','zh-cn'], // "en" the first one to use it as default
	icons: 'addfile,addimage', // %REMOVE_LINE_CORE%

	onLoad : function()	{
		// v4
		// In v4 this setting is global for all instances:
		if (CKEDITOR.addCss)
			CKEDITOR.addCss( getEditorCss(CKEDITOR.config) );

		// CSS for container
		var node = CKEDITOR.document.getHead().append( 'style' );
		node.setAttribute( 'type', 'text/css' );
		var content = '.SimpleUploadsOverContainer {' + (CKEDITOR.config.simpleuploads_containerover || 'box-shadow: 0 0 10px 1px #99DD99 !important;') + '} ' +
					'.SimpleUploadsOverDialog {' + (CKEDITOR.config.simpleuploads_dialogover || 'box-shadow: 0 0 10px 4px #999999 inset !important;') + '} ' +
					'.SimpleUploadsOverCover {' + (CKEDITOR.config.simpleuploads_coverover || 'box-shadow: 0 0 10px 4px #99DD99 !important;') + '} ';

		// Inject the throbber styles in the page:
		// If this were part of the official code it should be placed in the dialog.css skin
		// We must specify the .cke_throbber for the inner divs or the reset css won't allow to use the background-color
		content += [".cke_throbber {margin: 0 auto; width: 100px;}",
					".cke_throbber div {float: left; width: 8px; height: 9px; margin-left: 2px; margin-right: 2px; font-size: 1px;}",
					".cke_throbber .cke_throbber_1 {background-color: #737357;}",
					".cke_throbber .cke_throbber_2 {background-color: #8f8f73;}",
					".cke_throbber .cke_throbber_3 {background-color: #abab8f;}",
					".cke_throbber .cke_throbber_4 {background-color: #c7c7ab;}",
					".cke_throbber .cke_throbber_5 {background-color: #e3e3c7;}",
					".uploadRect {display: inline-block;height: 11px;vertical-align: middle;width: 50px;}",
					".uploadRect span {background-color: #999;display: inline-block;height: 100%;vertical-align: top;}",
					".uploadName {display: inline-block;max-width: 180px;overflow: hidden;text-overflow: ellipsis;vertical-align: top;white-space: pre;}",
					".uploadText {font-size:80%;}",
					".cke_throbberMain a {cursor: pointer; font-size: 14px; font-weight:bold; padding: 4px 5px;position: absolute;right:0; text-decoration:none; top: -2px;}",
					".cke_throbberMain {background-color: #FFF; border:1px solid #e5e5e5; padding:4px 14px 4px 4px; min-width:250px; position:absolute;}"]
					.join(' ');

		if ( CKEDITOR.env.ie && CKEDITOR.env.version < 11)
			node.$.styleSheet.cssText = content;
		else
			node.$.innerHTML = content;
	},

	init : function( editor ) {
		var config = editor.config;

		if (typeof config.simpleuploads_imageExtensions == "undefined")
			config.simpleuploads_imageExtensions = "jpe?g|gif|png";

		// v3
		if (editor.addCss)
			editor.addCss( getEditorCss(config) );

		// if not defined specifically for images, reuse the default file upload url
		if (!config.filebrowserImageUploadUrl)
			config.filebrowserImageUploadUrl = config.filebrowserUploadUrl;

		if (!config.filebrowserUploadUrl && !config.filebrowserImageUploadUrl)
		{
			if (window.console && console.log)
			{
				console.log("The editor is missing the 'config.filebrowserUploadUrl' entry to know the URL that will handle uploaded files.\r\n" +
					"It should handle the posted file as shown in Example 3: http://docs.ckeditor.com/#!/guide/dev_file_browser_api-section-example-3 \r\n" +
					"More info: http://alfonsoml.blogspot.com/2009/12/using-your-own-uploader-in-ckeditor.html");
				console[ console.warn ? "warn" : "log" ]("The 'SimpleUploads' plugin now is disabled.");
			}
			return;
		}

		// if upload URL is set to base64 data urls then exit if the browser doesn't support the file reader api
		if (config.filebrowserImageUploadUrl=="base64" && (typeof FormData == 'undefined'))
			return;

		// v 4.1 filters
		if (editor.addFeature)
		{
			editor.addFeature( {
				allowedContent: 'img[!src,width,height];a[!href];span[id](SimpleUploadsTmpWrapper);'
			} );
		}

		// Manages the throbber animation that appears to show a lengthy operation
		CKEDITOR.dialog.prototype.showThrobber = function () {
			if (!this.throbber)
			{
				this.throbber = {
					update : function()
					{
						var throbberParent = this.throbberParent.$,
							throbberBlocks = throbberParent.childNodes,
							lastClass = throbberParent.lastChild.className;

						// From the last to the second one, copy the class from the previous one.
						for ( var i = throbberBlocks.length - 1 ; 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( '<div style="background-color:rgba(255,255,255,0.95);width:100%;height:100%;top:0;left:0; position:absolute; visibility:none;z-index:100;"></div>');
						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 = "<span id='" + idTmp + "' class='SimpleUploadsTmpWrapper' style='display:none'>&nbsp;</span>";
				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( /<img src="webkit-fake-url:.*?">/g, "");
			}

			// Handles image pasting in Firefox
			// Replace data: images in Firefox and upload them.
			// No longer required with Firefox 22
			html = html.replace( /<img(.*?) src="data:image\/.{3,4};base64,.*?"(.*?)>/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 = "&nbsp;";

					// 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<data.files.length; i++)
				{
					var file = data.files[ i ],
						id = CKEDITOR.tools.getNextId(),
						fileName = file.name,
						evData = {
							context : ev,
							name : fileName,
							file : file,
							forceLink : ev.shiftKey, // If shift is pressed, create a link even if the drop is an image
							id : id,
							mode : {
								type : 'droppedFile',
								dropLocation: dropLocation
							}
						};

					CKEDITOR.plugins.simpleuploads.insertDroppedFile(editor, evData);
				}
			}
		};

		var rootDragEnter = function(e) {
			if (minX == -1)
			{
				if (!hasFiles(e))
					return;

				if (!editor.readOnly)
					visibleRoot.addClass( "SimpleUploadsOverEditor" );

				var rect = visibleRoot.$.getBoundingClientRect();
				minX=rect.left;
				minY=rect.top;
				maxX=minX + visibleRoot.$.clientWidth;
				maxY=minY + visibleRoot.$.clientHeight;
			}
		};

		var rootDragLeave = function(e) {
			if ( minX == -1 )
				return;

			var ev = e.data.$;

			if ((ev.clientX<=minX) || (ev.clientY<=minY) || (ev.clientX>=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.$;
			}

			// 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("&nbsp;");

		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( "&nbsp;" ); // 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 = "<span class='uploadName'>" + data.name + "</span>" +
			" <span class='uploadRect'><span id='rect" + data.id + "'></span></span>" +
			" <span id='text" + data.id + "' class='uploadText'> </span><span class='uploadCancel'>x</span>";
		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 = "<span class='uploadName'>" + data.name + "</span>" +
						" <span class='uploadRect'><span id='rect" + data.id + "'></span></span>" +
						" <span id='text" + data.id + "' class='uploadText'> </span><a>x</a>";

					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<links.length; i++) {
			var link = links[ i ];
			if (link.innerHTML == "x")
			{
				link.className = "uploadCancel";
				link.onclick = function() {
					if (data.xhr)
						data.xhr.abort();
				};
			}
		}
	}

	function pasteListener(e) {
		var editor = e.listenerData.editor,
			dialog = e.listenerData.dialog,
			i,
			item;

		// We want IE11 here to embed images as base64 (at least for the moment)
		// later use them as blob if we aren't in a dialog

		// In IE11 we use the images at this point only if forcePasteAsPlainText has been set
		// It doesn't work due to https://connect.microsoft.com/IE/feedback/details/813618/calling-xhr-open-in-a-paste-event-throws-an-access-denied-error
		var data = (e.data && e.data.$.clipboardData) || (editor.config.forcePasteAsPlainText && window.clipboardData);
		if (!data)
			return;


		// If forcePasteAsPlainText is set, try to detect if we're with Firefox and the clipboard content is only an image
		if (CKEDITOR.env.gecko && editor.config.forcePasteAsPlainText)
		{
			if (data.types.length===0)
			{
				// only once:
				editor.on( 'beforePaste', function( evt ) {
					evt.removeListener();

					// Force html mode :-)
					evt.data.type = 'html';
				} );
				return;
			}
		}

		// Chrome has clipboardData.items. Other browsers don't provide this info at the moment.
		// Firefox implements clipboardData.files in 22
		var items = data.items || data.files;
		if (!items || !items.length)
			return;

		// Check first if there is a text/html or text/plain version, and leave the browser use that:
		// otherwise, pasting from MS Word to Chrome in Mac will always generate a black rectangle.
		if (items[0].kind)
		{
			for (i=0; i< items.length; i++)
			{
				item = items[i];
				if ( item.kind=="string" && (item.type=="text/html" || item.type=="text/plain") )
					return;
			}
		}

		// We're safe, stupid Office-Mac combination won't disturb us.
		for (i=0; i< items.length; i++)
		{
			item = items[i];
			if ( item.kind && item.kind != "file" )
				continue;

			e.data.preventDefault();

			var file = (item.getAsFile ? item.getAsFile() : item);

			if (CKEDITOR.env.ie || editor.config.forcePasteAsPlainText)
			{
				setTimeout( function() {
					processPastedFile(file, e);
				}, 100);
			}
			else
				processPastedFile(file, e);
		}

		// autoclose the dialog
		if (dialog && e.data.$.defaultPrevented)
			dialog.hide();
	}

	function processPastedFile(file, e)
	{
		var editor = e.listenerData.editor,
			dialog = e.listenerData.dialog;

		var id = CKEDITOR.plugins.simpleuploads.getTimeStampId(),
			fileName = file.name || (id + ".png"),
			evData = {
				context : e.data.$,
				name : fileName,
				file : file,
				forceLink : false,
				id : id,
				mode : {
					type : 'pastedFile',
					dialog : dialog
				}
			};

		CKEDITOR.plugins.simpleuploads.insertPastedFile(editor, evData);
	}

	function setupPasteListener(iframe)
	{
		var doc = iframe.getFrameDocument(),
			body = doc.getBody();
		if (!body || !body.$ || (body.$.contentEditable != "true" && doc.$.designMode != "on"))
		{
			setTimeout(function() { setupPasteListener(iframe);}, 100);
			return;
		}
		var dialog = CKEDITOR.dialog.getCurrent();

		doc.on("paste", pasteListener, null, {
				dialog : dialog,
				editor : dialog.getParentEditor()
			});
	}

	CKEDITOR.on( "dialogDefinition", function( evt )
	{
		if (!evt.editor.plugins.simpleuploads)
			return;

		var definition = evt.data.definition;

		// Associate filebrowser to elements with 'filebrowser' attribute.
		for ( var i in definition.contents )
		{
			var contents = definition.contents[ i ];
			if ( contents )
				applySimpleUpload(evt.editor, evt.data.name, definition, contents.elements);
		}

		// Detect the Paste dialog
		if (evt.data.name == "paste")
		{
			definition.onShow = CKEDITOR.tools.override( definition.onShow, function( original )
			{
				return function()
				{
					if ( typeof original == "function" )
						original.call( this );

					setupPasteListener( this.getContentElement( "general", "editing_area" ).getInputElement() );
				};
			} );

		}
	}, null, null, 30 );

})();


/**
 * Fired when file starts being uploaded by the "simpleuploads" plugin
 *
 * @since 3.1
 * @name CKEDITOR.editor#simpleuploads.startUpload
 * @event
 * @param {String} [name] The file name.
 * @param {String} [url] The url that will be used for the upload. It can be modified to your needs on each upload.
 * @param {String|Object} [context] Context that caused the upload (a string if it's a pasted image, a DOM event for drag&drop and copied files, the toolbar button for those cases)
 * @param {Object} [file] The file itself (if available).
 * @param {Object} [extraFields] Since 3.4.1 the event listener can add this property to indicate extra data to send in the upload as POST data
 */

/**
 * Fired when the server sends the response of an upload.
 *
 * @since 4.3.6
 * @name CKEDITOR.editor#simpleuploads.serverResponse
 * @event
 * @param {Object} [xhr] The XHR with the request.
 * @param {Object} [data] The original data object of this upload.
 * Upon processing this event, a listener can set a "url" property on the event.data object and that will tell to the SimpleUploads plugin
 * that your code has processed the response.
 * If url is an empty string it means that the upload has failed and that the upload placeholder must be removed silently
 * Otherwise it will be treated as the response from the server
 * This way you can use different responses from your server that doesn't follow the QuickUpload pattern, as well as hook any additional processing that
 * you might need.
 * Please, note that this is fired only for uploads using XHR, old IEs are excluded and they need the default response from the server.
 */

/**
 * Fired when file upload ends on the "simpleuploads" plugin
 *
 * @since 3.1
 * @name CKEDITOR.editor#simpleuploads.endUpload
 * @event
 * @param {String} [name] The file name.
 * @param {Boolean} [ok] Whether the file has been correctly uploaded or not
 * @param {Object} [xhr] The XHR with the request. Since 4.3
 * @param {Object} [data] The original data object of this upload. Since 4.3
 */

/**
 * Fired when the final element has been inserted by the "simpleuploads" plugin (after it has been uploaded)
 *
 * @since 3.3.4
 * @name CKEDITOR.editor#simpleuploads.finishedUpload
 * @event
 * @param {String} [name] The file name.
 * @param {CKEDITOR.dom.element} [element] The element node that has been inserted
 */


/**
 * Fired when an image has been selected, before it's uploaded. It provides a reference to an img element
 * that contains the selected file. Extends the data provided in the simpleuploads.startUpload event
 * @since 4.2
 * @name CKEDITOR.editor#simpleuploads.localImageReady
 * @event
 * @param {Image} [image] The element node that has been inserted
 */

/**
 * Class to apply to the editor container (ie: the border outside the editor) when a file is dragged on the page
 *
 *		CKEDITOR.config.simpleuploads_containerover='border:1px solid red !important;';
 *
 * @since 2.7
 * @cfg {String} [simpleuploads_containerover='box-shadow: 0 0 10px 1px #99DD99 !important;']
 * @member CKEDITOR.config
 */

/**
 * Class to apply to the editor when a file is dragged over it
 *
 *		CKEDITOR.config.simpleuploads_editorover='background-color:yellow !important;';
 *
 * @since 2.7
 * @cfg {String} [simpleuploads_editorover='box-shadow: 0 0 10px 1px #999999 inset !important;']
 * @member CKEDITOR.config
 */

/**
 * Class to apply to the dialog border/cover when a file is dragged on the page
 *
 *		CKEDITOR.config.simpleuploads_coverover='border:1px solid red !important;';
 *
 * @since 4.0
 * @cfg {String} [simpleuploads_coverover='box-shadow: 0 0 10px 4px #999999 inset !important;']
 * @member CKEDITOR.config
 */

/**
 * Class to apply to the dialog content when a file is dragged over it
 *
 *		CKEDITOR.config.simpleuploads_dialogover='border:1px solid red !important;';
 *
 * @since 4.0
 * @cfg {String} [simpleuploads_dialogover='box-shadow: 0 0 10px 4px #999999 inset !important;']
 * @member CKEDITOR.config
 */


/**
 * List of extensions that should be recognized as belonging to image files (ie: a dropped file will be inserted as img instead of link)
 *
 *		config.simpleuploads_imageExtensions='jpe?g';
 *
 * @since 3.3.3
 * @cfg {String} [simpleuploads_imageExtensions='jpe?g|gif|png']
 * @member CKEDITOR.config
 */

/**
 * Maximum file size to allow the upload. By default there are no restrictions
 *
 *		config.simpleuploads_maxFileSize=10*1024*1024; // 10 Mb
 *
 * @since 3.3.3
 * @cfg {Number} [simpleuploads_maxFileSize=null]
 * @member CKEDITOR.config
 */


/**
 * List of extensions that aren't allowed (blacklist)
 *
 *		config.simpleuploads_invalidExtensions='exe|php';
 *
 * @since 3.3.3
 * @cfg {String} [simpleuploads_invalidExtensions=null]
 * @member CKEDITOR.config
 */

/**
 * List of extensions that are accepted (whitelist). If the file doesn't have this extension it will be rejected
 *
 *		config.simpleuploads_acceptedExtensions='jpg|png|pdf|zip';
 *
 * @since 3.3.3
 * @cfg {String} [simpleuploads_acceptedExtensions=null]
 * @member CKEDITOR.config
 */


/**
 * If it's set to true, the plugin won't modify the "Quick Upload" button in the dialogs
 * (just in case you are doing some special processing that isn't possible at the moment with the modified system)
 * (if you have such situation, please, contact me so that I can improve the plugin and allow you to use the plugin for everything)
 *
 *		config.simpleuploads_respectDialogUploads=true;
 *
 * @since 4.0
 * @cfg {Boolean} [simpleuploads_respectDialogUploads=null]
 * @member CKEDITOR.config
 */

/**
 * If it's set to true, images will be uploaded without the preview progress, using the plain text upload
 *
 *		CKEDITOR.config.simpleuploads_hideImageProgress=true;
 *
 * @since 4.1
 * @cfg {Boolean} [simpleuploads_hideImageProgress=null]
 * @member CKEDITOR.config
 */

/**
 * If it's set to true, bmp images will be converted to png before uploading (you must add the "bmp" to the list of allowed extensions)
 *
 *		CKEDITOR.config.simpleuploads_convertBmp=true;
 *
 * @since 4.2
 * @cfg {Boolean} [simpleuploads_convertBmp=null]
 * @member CKEDITOR.config
 */

/**
 * It's an object that can contain two members: width and height specifying the maximum dimensions (in pixels) allowed for images.
 * if the image is bigger in any of those dimensions, the upload will be rejected.
 *
 *		CKEDITOR.config.simpleuploads_maximumDimensions={width:500, height:400};
 *
 * @since 4.2
 * @cfg {Object} [simpleuploads_maximumDimensions=null]
 * @member CKEDITOR.config
 */

/**
 * Name of the input sent to the server with the file data
 *
 *		CKEDITOR.config.simpleuploads_inputname="file";
 *
 * @since 4.3.9
 * @cfg {String} [simpleuploads_inputname="upload"]
 * @member CKEDITOR.config
 */
