src/myams/resources/js/ext/tinymce/dev/plugins/paste/plugin.js
changeset 0 f05d7aea098a
equal deleted inserted replaced
-1:000000000000 0:f05d7aea098a
       
     1 /**
       
     2  * Compiled inline version. (Library mode)
       
     3  */
       
     4 
       
     5 /*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
       
     6 /*globals $code */
       
     7 
       
     8 (function(exports, undefined) {
       
     9 	"use strict";
       
    10 
       
    11 	var modules = {};
       
    12 
       
    13 	function require(ids, callback) {
       
    14 		var module, defs = [];
       
    15 
       
    16 		for (var i = 0; i < ids.length; ++i) {
       
    17 			module = modules[ids[i]] || resolve(ids[i]);
       
    18 			if (!module) {
       
    19 				throw 'module definition dependecy not found: ' + ids[i];
       
    20 			}
       
    21 
       
    22 			defs.push(module);
       
    23 		}
       
    24 
       
    25 		callback.apply(null, defs);
       
    26 	}
       
    27 
       
    28 	function define(id, dependencies, definition) {
       
    29 		if (typeof id !== 'string') {
       
    30 			throw 'invalid module definition, module id must be defined and be a string';
       
    31 		}
       
    32 
       
    33 		if (dependencies === undefined) {
       
    34 			throw 'invalid module definition, dependencies must be specified';
       
    35 		}
       
    36 
       
    37 		if (definition === undefined) {
       
    38 			throw 'invalid module definition, definition function must be specified';
       
    39 		}
       
    40 
       
    41 		require(dependencies, function() {
       
    42 			modules[id] = definition.apply(null, arguments);
       
    43 		});
       
    44 	}
       
    45 
       
    46 	function defined(id) {
       
    47 		return !!modules[id];
       
    48 	}
       
    49 
       
    50 	function resolve(id) {
       
    51 		var target = exports;
       
    52 		var fragments = id.split(/[.\/]/);
       
    53 
       
    54 		for (var fi = 0; fi < fragments.length; ++fi) {
       
    55 			if (!target[fragments[fi]]) {
       
    56 				return;
       
    57 			}
       
    58 
       
    59 			target = target[fragments[fi]];
       
    60 		}
       
    61 
       
    62 		return target;
       
    63 	}
       
    64 
       
    65 	function expose(ids) {
       
    66 		for (var i = 0; i < ids.length; i++) {
       
    67 			var target = exports;
       
    68 			var id = ids[i];
       
    69 			var fragments = id.split(/[.\/]/);
       
    70 
       
    71 			for (var fi = 0; fi < fragments.length - 1; ++fi) {
       
    72 				if (target[fragments[fi]] === undefined) {
       
    73 					target[fragments[fi]] = {};
       
    74 				}
       
    75 
       
    76 				target = target[fragments[fi]];
       
    77 			}
       
    78 
       
    79 			target[fragments[fragments.length - 1]] = modules[id];
       
    80 		}
       
    81 	}
       
    82 
       
    83 // Included from: js/tinymce/plugins/paste/classes/Utils.js
       
    84 
       
    85 /**
       
    86  * Utils.js
       
    87  *
       
    88  * Copyright, Moxiecode Systems AB
       
    89  * Released under LGPL License.
       
    90  *
       
    91  * License: http://www.tinymce.com/license
       
    92  * Contributing: http://www.tinymce.com/contributing
       
    93  */
       
    94 
       
    95 /**
       
    96  * This class contails various utility functions for the paste plugin.
       
    97  *
       
    98  * @class tinymce.pasteplugin.Utils
       
    99  */
       
   100 define("tinymce/pasteplugin/Utils", [
       
   101 	"tinymce/util/Tools",
       
   102 	"tinymce/html/DomParser",
       
   103 	"tinymce/html/Schema"
       
   104 ], function(Tools, DomParser, Schema) {
       
   105 	function filter(content, items) {
       
   106 		Tools.each(items, function(v) {
       
   107 			if (v.constructor == RegExp) {
       
   108 				content = content.replace(v, '');
       
   109 			} else {
       
   110 				content = content.replace(v[0], v[1]);
       
   111 			}
       
   112 		});
       
   113 
       
   114 		return content;
       
   115 	}
       
   116 
       
   117 	/**
       
   118 	 * Gets the innerText of the specified element. It will handle edge cases
       
   119 	 * and works better than textContent on Gecko.
       
   120 	 *
       
   121 	 * @param {String} html HTML string to get text from.
       
   122 	 * @return {String} String of text with line feeds.
       
   123 	 */
       
   124 	function innerText(html) {
       
   125 		var schema = new Schema(), domParser = new DomParser({}, schema), text = '';
       
   126 		var shortEndedElements = schema.getShortEndedElements();
       
   127 		var ignoreElements = Tools.makeMap('script noscript style textarea video audio iframe object', ' ');
       
   128 		var blockElements = schema.getBlockElements();
       
   129 
       
   130 		function walk(node) {
       
   131 			var name = node.name, currentNode = node;
       
   132 
       
   133 			if (name === 'br') {
       
   134 				text += '\n';
       
   135 				return;
       
   136 			}
       
   137 
       
   138 			// img/input/hr
       
   139 			if (shortEndedElements[name]) {
       
   140 				text += ' ';
       
   141 			}
       
   142 
       
   143 			// Ingore script, video contents
       
   144 			if (ignoreElements[name]) {
       
   145 				text += ' ';
       
   146 				return;
       
   147 			}
       
   148 
       
   149 			if (node.type == 3) {
       
   150 				text += node.value;
       
   151 			}
       
   152 
       
   153 			// Walk all children
       
   154 			if (!node.shortEnded) {
       
   155 				if ((node = node.firstChild)) {
       
   156 					do {
       
   157 						walk(node);
       
   158 					} while ((node = node.next));
       
   159 				}
       
   160 			}
       
   161 
       
   162 			// Add \n or \n\n for blocks or P
       
   163 			if (blockElements[name] && currentNode.next) {
       
   164 				text += '\n';
       
   165 
       
   166 				if (name == 'p') {
       
   167 					text += '\n';
       
   168 				}
       
   169 			}
       
   170 		}
       
   171 
       
   172 		html = filter(html, [
       
   173 			/<!\[[^\]]+\]>/g // Conditional comments
       
   174 		]);
       
   175 
       
   176 		walk(domParser.parse(html));
       
   177 
       
   178 		return text;
       
   179 	}
       
   180 
       
   181 	/**
       
   182 	 * Trims the specified HTML by removing all WebKit fragments, all elements wrapping the body trailing BR elements etc.
       
   183 	 *
       
   184 	 * @param {String} html Html string to trim contents on.
       
   185 	 * @return {String} Html contents that got trimmed.
       
   186 	 */
       
   187 	function trimHtml(html) {
       
   188 		function trimSpaces(all, s1, s2) {
       
   189 			// WebKit &nbsp; meant to preserve multiple spaces but instead inserted around all inline tags,
       
   190 			// including the spans with inline styles created on paste
       
   191 			if (!s1 && !s2) {
       
   192 				return ' ';
       
   193 			}
       
   194 
       
   195 			return '\u00a0';
       
   196 		}
       
   197 
       
   198 		html = filter(html, [
       
   199 			/^[\s\S]*<body[^>]*>\s*|\s*<\/body[^>]*>[\s\S]*$/g, // Remove anything but the contents within the BODY element
       
   200 			/<!--StartFragment-->|<!--EndFragment-->/g, // Inner fragments (tables from excel on mac)
       
   201 			[/( ?)<span class="Apple-converted-space">\u00a0<\/span>( ?)/g, trimSpaces],
       
   202 			/<br>$/i // Trailing BR elements
       
   203 		]);
       
   204 
       
   205 		return html;
       
   206 	}
       
   207 
       
   208 	return {
       
   209 		filter: filter,
       
   210 		innerText: innerText,
       
   211 		trimHtml: trimHtml
       
   212 	};
       
   213 });
       
   214 
       
   215 // Included from: js/tinymce/plugins/paste/classes/Clipboard.js
       
   216 
       
   217 /**
       
   218  * Clipboard.js
       
   219  *
       
   220  * Copyright, Moxiecode Systems AB
       
   221  * Released under LGPL License.
       
   222  *
       
   223  * License: http://www.tinymce.com/license
       
   224  * Contributing: http://www.tinymce.com/contributing
       
   225  */
       
   226 
       
   227 /**
       
   228  * This class contains logic for getting HTML contents out of the clipboard.
       
   229  *
       
   230  * We need to make a lot of ugly hacks to get the contents out of the clipboard since
       
   231  * the W3C Clipboard API is broken in all browsers that have it: Gecko/WebKit/Blink.
       
   232  * We might rewrite this the way those API:s stabilize. Browsers doesn't handle pasting
       
   233  * from applications like Word the same way as it does when pasting into a contentEditable area
       
   234  * so we need to do lots of extra work to try to get to this clipboard data.
       
   235  *
       
   236  * Current implementation steps:
       
   237  *  1. On keydown with paste keys Ctrl+V or Shift+Insert create
       
   238  *     a paste bin element and move focus to that element.
       
   239  *  2. Wait for the browser to fire a "paste" event and get the contents out of the paste bin.
       
   240  *  3. Check if the paste was successful if true, process the HTML.
       
   241  *  (4). If the paste was unsuccessful use IE execCommand, Clipboard API, document.dataTransfer old WebKit API etc.
       
   242  *
       
   243  * @class tinymce.pasteplugin.Clipboard
       
   244  * @private
       
   245  */
       
   246 define("tinymce/pasteplugin/Clipboard", [
       
   247 	"tinymce/Env",
       
   248 	"tinymce/dom/RangeUtils",
       
   249 	"tinymce/util/VK",
       
   250 	"tinymce/pasteplugin/Utils"
       
   251 ], function(Env, RangeUtils, VK, Utils) {
       
   252 	return function(editor) {
       
   253 		var self = this, pasteBinElm, lastRng, keyboardPasteTimeStamp = 0, draggingInternally = false;
       
   254 		var pasteBinDefaultContent = '%MCEPASTEBIN%', keyboardPastePlainTextState;
       
   255 		var mceInternalUrlPrefix = 'data:text/mce-internal,';
       
   256 
       
   257 		/**
       
   258 		 * Pastes the specified HTML. This means that the HTML is filtered and then
       
   259 		 * inserted at the current selection in the editor. It will also fire paste events
       
   260 		 * for custom user filtering.
       
   261 		 *
       
   262 		 * @param {String} html HTML code to paste into the current selection.
       
   263 		 */
       
   264 		function pasteHtml(html) {
       
   265 			var args, dom = editor.dom;
       
   266 
       
   267 			args = editor.fire('BeforePastePreProcess', {content: html}); // Internal event used by Quirks
       
   268 			args = editor.fire('PastePreProcess', args);
       
   269 			html = args.content;
       
   270 
       
   271 			if (!args.isDefaultPrevented()) {
       
   272 				// User has bound PastePostProcess events then we need to pass it through a DOM node
       
   273 				// This is not ideal but we don't want to let the browser mess up the HTML for example
       
   274 				// some browsers add &nbsp; to P tags etc
       
   275 				if (editor.hasEventListeners('PastePostProcess') && !args.isDefaultPrevented()) {
       
   276 					// We need to attach the element to the DOM so Sizzle selectors work on the contents
       
   277 					var tempBody = dom.add(editor.getBody(), 'div', {style: 'display:none'}, html);
       
   278 					args = editor.fire('PastePostProcess', {node: tempBody});
       
   279 					dom.remove(tempBody);
       
   280 					html = args.node.innerHTML;
       
   281 				}
       
   282 
       
   283 				if (!args.isDefaultPrevented()) {
       
   284 					editor.insertContent(html, {merge: editor.settings.paste_merge_formats !== false});
       
   285 				}
       
   286 			}
       
   287 		}
       
   288 
       
   289 		/**
       
   290 		 * Pastes the specified text. This means that the plain text is processed
       
   291 		 * and converted into BR and P elements. It will fire paste events for custom filtering.
       
   292 		 *
       
   293 		 * @param {String} text Text to paste as the current selection location.
       
   294 		 */
       
   295 		function pasteText(text) {
       
   296 			text = editor.dom.encode(text).replace(/\r\n/g, '\n');
       
   297 
       
   298 			var startBlock = editor.dom.getParent(editor.selection.getStart(), editor.dom.isBlock);
       
   299 
       
   300 			// Create start block html for example <p attr="value">
       
   301 			var forcedRootBlockName = editor.settings.forced_root_block;
       
   302 			var forcedRootBlockStartHtml;
       
   303 			if (forcedRootBlockName) {
       
   304 				forcedRootBlockStartHtml = editor.dom.createHTML(forcedRootBlockName, editor.settings.forced_root_block_attrs);
       
   305 				forcedRootBlockStartHtml = forcedRootBlockStartHtml.substr(0, forcedRootBlockStartHtml.length - 3) + '>';
       
   306 			}
       
   307 
       
   308 			if ((startBlock && /^(PRE|DIV)$/.test(startBlock.nodeName)) || !forcedRootBlockName) {
       
   309 				text = Utils.filter(text, [
       
   310 					[/\n/g, "<br>"]
       
   311 				]);
       
   312 			} else {
       
   313 				text = Utils.filter(text, [
       
   314 					[/\n\n/g, "</p>" + forcedRootBlockStartHtml],
       
   315 					[/^(.*<\/p>)(<p>)$/, forcedRootBlockStartHtml + '$1'],
       
   316 					[/\n/g, "<br />"]
       
   317 				]);
       
   318 
       
   319 				if (text.indexOf('<p>') != -1) {
       
   320 					text = forcedRootBlockStartHtml + text;
       
   321 				}
       
   322 			}
       
   323 
       
   324 			pasteHtml(text);
       
   325 		}
       
   326 
       
   327 		/**
       
   328 		 * Creates a paste bin element as close as possible to the current caret location and places the focus inside that element
       
   329 		 * so that when the real paste event occurs the contents gets inserted into this element
       
   330 		 * instead of the current editor selection element.
       
   331 		 */
       
   332 		function createPasteBin() {
       
   333 			var dom = editor.dom, body = editor.getBody();
       
   334 			var viewport = editor.dom.getViewPort(editor.getWin()), scrollTop = viewport.y, top = 20;
       
   335 			var scrollContainer;
       
   336 
       
   337 			lastRng = editor.selection.getRng();
       
   338 
       
   339 			if (editor.inline) {
       
   340 				scrollContainer = editor.selection.getScrollContainer();
       
   341 
       
   342 				// Can't always rely on scrollTop returning a useful value.
       
   343 				// It returns 0 if the browser doesn't support scrollTop for the element or is non-scrollable
       
   344 				if (scrollContainer && scrollContainer.scrollTop > 0) {
       
   345 					scrollTop = scrollContainer.scrollTop;
       
   346 				}
       
   347 			}
       
   348 
       
   349 			/**
       
   350 			 * Returns the rect of the current caret if the caret is in an empty block before a
       
   351 			 * BR we insert a temporary invisible character that we get the rect this way we always get a proper rect.
       
   352 			 *
       
   353 			 * TODO: This might be useful in core.
       
   354 			 */
       
   355 			function getCaretRect(rng) {
       
   356 				var rects, textNode, node, container = rng.startContainer;
       
   357 
       
   358 				rects = rng.getClientRects();
       
   359 				if (rects.length) {
       
   360 					return rects[0];
       
   361 				}
       
   362 
       
   363 				if (!rng.collapsed || container.nodeType != 1) {
       
   364 					return;
       
   365 				}
       
   366 
       
   367 				node = container.childNodes[lastRng.startOffset];
       
   368 
       
   369 				// Skip empty whitespace nodes
       
   370 				while (node && node.nodeType == 3 && !node.data.length) {
       
   371 					node = node.nextSibling;
       
   372 				}
       
   373 
       
   374 				if (!node) {
       
   375 					return;
       
   376 				}
       
   377 
       
   378 				// Check if the location is |<br>
       
   379 				// TODO: Might need to expand this to say |<table>
       
   380 				if (node.tagName == 'BR') {
       
   381 					textNode = dom.doc.createTextNode('\uFEFF');
       
   382 					node.parentNode.insertBefore(textNode, node);
       
   383 
       
   384 					rng = dom.createRng();
       
   385 					rng.setStartBefore(textNode);
       
   386 					rng.setEndAfter(textNode);
       
   387 
       
   388 					rects = rng.getClientRects();
       
   389 					dom.remove(textNode);
       
   390 				}
       
   391 
       
   392 				if (rects.length) {
       
   393 					return rects[0];
       
   394 				}
       
   395 			}
       
   396 
       
   397 			// Calculate top cordinate this is needed to avoid scrolling to top of document
       
   398 			// We want the paste bin to be as close to the caret as possible to avoid scrolling
       
   399 			if (lastRng.getClientRects) {
       
   400 				var rect = getCaretRect(lastRng);
       
   401 
       
   402 				if (rect) {
       
   403 					// Client rects gets us closes to the actual
       
   404 					// caret location in for example a wrapped paragraph block
       
   405 					top = scrollTop + (rect.top - dom.getPos(body).y);
       
   406 				} else {
       
   407 					top = scrollTop;
       
   408 
       
   409 					// Check if we can find a closer location by checking the range element
       
   410 					var container = lastRng.startContainer;
       
   411 					if (container) {
       
   412 						if (container.nodeType == 3 && container.parentNode != body) {
       
   413 							container = container.parentNode;
       
   414 						}
       
   415 
       
   416 						if (container.nodeType == 1) {
       
   417 							top = dom.getPos(container, scrollContainer || body).y;
       
   418 						}
       
   419 					}
       
   420 				}
       
   421 			}
       
   422 
       
   423 			// Create a pastebin
       
   424 			pasteBinElm = dom.add(editor.getBody(), 'div', {
       
   425 				id: "mcepastebin",
       
   426 				contentEditable: true,
       
   427 				"data-mce-bogus": "all",
       
   428 				style: 'position: absolute; top: ' + top + 'px;' +
       
   429 					'width: 10px; height: 10px; overflow: hidden; opacity: 0'
       
   430 			}, pasteBinDefaultContent);
       
   431 
       
   432 			// Move paste bin out of sight since the controlSelection rect gets displayed otherwise on IE and Gecko
       
   433 			if (Env.ie || Env.gecko) {
       
   434 				dom.setStyle(pasteBinElm, 'left', dom.getStyle(body, 'direction', true) == 'rtl' ? 0xFFFF : -0xFFFF);
       
   435 			}
       
   436 
       
   437 			// Prevent focus events from bubbeling fixed FocusManager issues
       
   438 			dom.bind(pasteBinElm, 'beforedeactivate focusin focusout', function(e) {
       
   439 				e.stopPropagation();
       
   440 			});
       
   441 
       
   442 			pasteBinElm.focus();
       
   443 			editor.selection.select(pasteBinElm, true);
       
   444 		}
       
   445 
       
   446 		/**
       
   447 		 * Removes the paste bin if it exists.
       
   448 		 */
       
   449 		function removePasteBin() {
       
   450 			if (pasteBinElm) {
       
   451 				var pasteBinClone;
       
   452 
       
   453 				// WebKit/Blink might clone the div so
       
   454 				// lets make sure we remove all clones
       
   455 				// TODO: Man o man is this ugly. WebKit is the new IE! Remove this if they ever fix it!
       
   456 				while ((pasteBinClone = editor.dom.get('mcepastebin'))) {
       
   457 					editor.dom.remove(pasteBinClone);
       
   458 					editor.dom.unbind(pasteBinClone);
       
   459 				}
       
   460 
       
   461 				if (lastRng) {
       
   462 					editor.selection.setRng(lastRng);
       
   463 				}
       
   464 			}
       
   465 
       
   466 			pasteBinElm = lastRng = null;
       
   467 		}
       
   468 
       
   469 		/**
       
   470 		 * Returns the contents of the paste bin as a HTML string.
       
   471 		 *
       
   472 		 * @return {String} Get the contents of the paste bin.
       
   473 		 */
       
   474 		function getPasteBinHtml() {
       
   475 			var html = '', pasteBinClones, i, clone, cloneHtml;
       
   476 
       
   477 			// Since WebKit/Chrome might clone the paste bin when pasting
       
   478 			// for example: <img style="float: right"> we need to check if any of them contains some useful html.
       
   479 			// TODO: Man o man is this ugly. WebKit is the new IE! Remove this if they ever fix it!
       
   480 			pasteBinClones = editor.dom.select('div[id=mcepastebin]');
       
   481 			for (i = 0; i < pasteBinClones.length; i++) {
       
   482 				clone = pasteBinClones[i];
       
   483 
       
   484 				// Pasting plain text produces pastebins in pastebinds makes sence right!?
       
   485 				if (clone.firstChild && clone.firstChild.id == 'mcepastebin') {
       
   486 					clone = clone.firstChild;
       
   487 				}
       
   488 
       
   489 				cloneHtml = clone.innerHTML;
       
   490 				if (html != pasteBinDefaultContent) {
       
   491 					html += cloneHtml;
       
   492 				}
       
   493 			}
       
   494 
       
   495 			return html;
       
   496 		}
       
   497 
       
   498 		/**
       
   499 		 * Gets various content types out of a datatransfer object.
       
   500 		 *
       
   501 		 * @param {DataTransfer} dataTransfer Event fired on paste.
       
   502 		 * @return {Object} Object with mime types and data for those mime types.
       
   503 		 */
       
   504 		function getDataTransferItems(dataTransfer) {
       
   505 			var data = {};
       
   506 
       
   507 			if (dataTransfer) {
       
   508 				// Use old WebKit/IE API
       
   509 				if (dataTransfer.getData) {
       
   510 					var legacyText = dataTransfer.getData('Text');
       
   511 					if (legacyText && legacyText.length > 0) {
       
   512 						if (legacyText.indexOf(mceInternalUrlPrefix) == -1) {
       
   513 							data['text/plain'] = legacyText;
       
   514 						}
       
   515 					}
       
   516 				}
       
   517 
       
   518 				if (dataTransfer.types) {
       
   519 					for (var i = 0; i < dataTransfer.types.length; i++) {
       
   520 						var contentType = dataTransfer.types[i];
       
   521 						data[contentType] = dataTransfer.getData(contentType);
       
   522 					}
       
   523 				}
       
   524 			}
       
   525 
       
   526 			return data;
       
   527 		}
       
   528 
       
   529 		/**
       
   530 		 * Gets various content types out of the Clipboard API. It will also get the
       
   531 		 * plain text using older IE and WebKit API:s.
       
   532 		 *
       
   533 		 * @param {ClipboardEvent} clipboardEvent Event fired on paste.
       
   534 		 * @return {Object} Object with mime types and data for those mime types.
       
   535 		 */
       
   536 		function getClipboardContent(clipboardEvent) {
       
   537 			return getDataTransferItems(clipboardEvent.clipboardData || editor.getDoc().dataTransfer);
       
   538 		}
       
   539 
       
   540 		/**
       
   541 		 * Checks if the clipboard contains image data if it does it will take that data
       
   542 		 * and convert it into a data url image and paste that image at the caret location.
       
   543 		 *
       
   544 		 * @param  {ClipboardEvent} e Paste/drop event object.
       
   545 		 * @param  {DOMRange} rng Optional rng object to move selection to.
       
   546 		 * @return {Boolean} true/false if the image data was found or not.
       
   547 		 */
       
   548 		function pasteImageData(e, rng) {
       
   549 			var dataTransfer = e.clipboardData || e.dataTransfer;
       
   550 
       
   551 			function processItems(items) {
       
   552 				var i, item, reader, hadImage = false;
       
   553 
       
   554 				function pasteImage(reader) {
       
   555 					if (rng) {
       
   556 						editor.selection.setRng(rng);
       
   557 						rng = null;
       
   558 					}
       
   559 
       
   560 					pasteHtml('<img src="' + reader.result + '">');
       
   561 				}
       
   562 
       
   563 				if (items) {
       
   564 					for (i = 0; i < items.length; i++) {
       
   565 						item = items[i];
       
   566 
       
   567 						if (/^image\/(jpeg|png|gif|bmp)$/.test(item.type)) {
       
   568 							reader = new FileReader();
       
   569 							reader.onload = pasteImage.bind(null, reader);
       
   570 							reader.readAsDataURL(item.getAsFile ? item.getAsFile() : item);
       
   571 
       
   572 							e.preventDefault();
       
   573 							hadImage = true;
       
   574 						}
       
   575 					}
       
   576 				}
       
   577 
       
   578 				return hadImage;
       
   579 			}
       
   580 
       
   581 			if (editor.settings.paste_data_images && dataTransfer) {
       
   582 				return processItems(dataTransfer.items) || processItems(dataTransfer.files);
       
   583 			}
       
   584 		}
       
   585 
       
   586 		/**
       
   587 		 * Chrome on Android doesn't support proper clipboard access so we have no choice but to allow the browser default behavior.
       
   588 		 *
       
   589 		 * @param {Event} e Paste event object to check if it contains any data.
       
   590 		 * @return {Boolean} true/false if the clipboard is empty or not.
       
   591 		 */
       
   592 		function isBrokenAndroidClipboardEvent(e) {
       
   593 			var clipboardData = e.clipboardData;
       
   594 
       
   595 			return navigator.userAgent.indexOf('Android') != -1 && clipboardData && clipboardData.items && clipboardData.items.length === 0;
       
   596 		}
       
   597 
       
   598 		function getCaretRangeFromEvent(e) {
       
   599 			return RangeUtils.getCaretRangeFromPoint(e.clientX, e.clientY, editor.getDoc());
       
   600 		}
       
   601 
       
   602 		function hasContentType(clipboardContent, mimeType) {
       
   603 			return mimeType in clipboardContent && clipboardContent[mimeType].length > 0;
       
   604 		}
       
   605 
       
   606 		function isKeyboardPasteEvent(e) {
       
   607 			return (VK.metaKeyPressed(e) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45);
       
   608 		}
       
   609 
       
   610 		function registerEventHandlers() {
       
   611 			editor.on('keydown', function(e) {
       
   612 				function removePasteBinOnKeyUp(e) {
       
   613 					// Ctrl+V or Shift+Insert
       
   614 					if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) {
       
   615 						removePasteBin();
       
   616 					}
       
   617 				}
       
   618 
       
   619 				// Ctrl+V or Shift+Insert
       
   620 				if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) {
       
   621 					keyboardPastePlainTextState = e.shiftKey && e.keyCode == 86;
       
   622 
       
   623 					// Edge case on Safari on Mac where it doesn't handle Cmd+Shift+V correctly
       
   624 					// it fires the keydown but no paste or keyup so we are left with a paste bin
       
   625 					if (keyboardPastePlainTextState && Env.webkit && navigator.userAgent.indexOf('Version/') != -1) {
       
   626 						return;
       
   627 					}
       
   628 
       
   629 					// Prevent undoManager keydown handler from making an undo level with the pastebin in it
       
   630 					e.stopImmediatePropagation();
       
   631 
       
   632 					keyboardPasteTimeStamp = new Date().getTime();
       
   633 
       
   634 					// IE doesn't support Ctrl+Shift+V and it doesn't even produce a paste event
       
   635 					// so lets fake a paste event and let IE use the execCommand/dataTransfer methods
       
   636 					if (Env.ie && keyboardPastePlainTextState) {
       
   637 						e.preventDefault();
       
   638 						editor.fire('paste', {ieFake: true});
       
   639 						return;
       
   640 					}
       
   641 
       
   642 					removePasteBin();
       
   643 					createPasteBin();
       
   644 
       
   645 					// Remove pastebin if we get a keyup and no paste event
       
   646 					// For example pasting a file in IE 11 will not produce a paste event
       
   647 					editor.once('keyup', removePasteBinOnKeyUp);
       
   648 					editor.once('paste', function() {
       
   649 						editor.off('keyup', removePasteBinOnKeyUp);
       
   650 					});
       
   651 				}
       
   652 			});
       
   653 
       
   654 			editor.on('paste', function(e) {
       
   655 				// Getting content from the Clipboard can take some time
       
   656 				var clipboardTimer = new Date().getTime();
       
   657 				var clipboardContent = getClipboardContent(e);
       
   658 				var clipboardDelay = new Date().getTime() - clipboardTimer;
       
   659 
       
   660 				var isKeyBoardPaste = (new Date().getTime() - keyboardPasteTimeStamp - clipboardDelay) < 1000;
       
   661 				var plainTextMode = self.pasteFormat == "text" || keyboardPastePlainTextState;
       
   662 
       
   663 				keyboardPastePlainTextState = false;
       
   664 
       
   665 				if (e.isDefaultPrevented() || isBrokenAndroidClipboardEvent(e)) {
       
   666 					removePasteBin();
       
   667 					return;
       
   668 				}
       
   669 
       
   670 				if (pasteImageData(e)) {
       
   671 					removePasteBin();
       
   672 					return;
       
   673 				}
       
   674 
       
   675 				// Not a keyboard paste prevent default paste and try to grab the clipboard contents using different APIs
       
   676 				if (!isKeyBoardPaste) {
       
   677 					e.preventDefault();
       
   678 				}
       
   679 
       
   680 				// Try IE only method if paste isn't a keyboard paste
       
   681 				if (Env.ie && (!isKeyBoardPaste || e.ieFake)) {
       
   682 					createPasteBin();
       
   683 
       
   684 					editor.dom.bind(pasteBinElm, 'paste', function(e) {
       
   685 						e.stopPropagation();
       
   686 					});
       
   687 
       
   688 					editor.getDoc().execCommand('Paste', false, null);
       
   689 					clipboardContent["text/html"] = getPasteBinHtml();
       
   690 				}
       
   691 
       
   692 				setTimeout(function() {
       
   693 					var content;
       
   694 
       
   695 					// Grab HTML from Clipboard API or paste bin as a fallback
       
   696 					if (hasContentType(clipboardContent, 'text/html')) {
       
   697 						content = clipboardContent['text/html'];
       
   698 					} else {
       
   699 						content = getPasteBinHtml();
       
   700 
       
   701 						// If paste bin is empty try using plain text mode
       
   702 						// since that is better than nothing right
       
   703 						if (content == pasteBinDefaultContent) {
       
   704 							plainTextMode = true;
       
   705 						}
       
   706 					}
       
   707 
       
   708 					content = Utils.trimHtml(content);
       
   709 
       
   710 					// WebKit has a nice bug where it clones the paste bin if you paste from for example notepad
       
   711 					// so we need to force plain text mode in this case
       
   712 					if (pasteBinElm && pasteBinElm.firstChild && pasteBinElm.firstChild.id === 'mcepastebin') {
       
   713 						plainTextMode = true;
       
   714 					}
       
   715 
       
   716 					removePasteBin();
       
   717 
       
   718 					// If we got nothing from clipboard API and pastebin then we could try the last resort: plain/text
       
   719 					if (!content.length) {
       
   720 						plainTextMode = true;
       
   721 					}
       
   722 
       
   723 					// Grab plain text from Clipboard API or convert existing HTML to plain text
       
   724 					if (plainTextMode) {
       
   725 						// Use plain text contents from Clipboard API unless the HTML contains paragraphs then
       
   726 						// we should convert the HTML to plain text since works better when pasting HTML/Word contents as plain text
       
   727 						if (hasContentType(clipboardContent, 'text/plain') && content.indexOf('</p>') == -1) {
       
   728 							content = clipboardContent['text/plain'];
       
   729 						} else {
       
   730 							content = Utils.innerText(content);
       
   731 						}
       
   732 					}
       
   733 
       
   734 					// If the content is the paste bin default HTML then it was
       
   735 					// impossible to get the cliboard data out.
       
   736 					if (content == pasteBinDefaultContent) {
       
   737 						if (!isKeyBoardPaste) {
       
   738 							editor.windowManager.alert('Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents.');
       
   739 						}
       
   740 
       
   741 						return;
       
   742 					}
       
   743 
       
   744 					if (plainTextMode) {
       
   745 						pasteText(content);
       
   746 					} else {
       
   747 						pasteHtml(content);
       
   748 					}
       
   749 				}, 0);
       
   750 			});
       
   751 
       
   752 			editor.on('dragstart dragend', function(e) {
       
   753 				draggingInternally = e.type == 'dragstart';
       
   754 			});
       
   755 
       
   756 			editor.on('drop', function(e) {
       
   757 				var rng = getCaretRangeFromEvent(e);
       
   758 
       
   759 				if (e.isDefaultPrevented() || draggingInternally) {
       
   760 					return;
       
   761 				}
       
   762 
       
   763 				if (pasteImageData(e, rng)) {
       
   764 					return;
       
   765 				}
       
   766 
       
   767 				if (rng && editor.settings.paste_filter_drop !== false) {
       
   768 					var dropContent = getDataTransferItems(e.dataTransfer);
       
   769 					var content = dropContent['mce-internal'] || dropContent['text/html'] || dropContent['text/plain'];
       
   770 
       
   771 					if (content) {
       
   772 						e.preventDefault();
       
   773 
       
   774 						editor.undoManager.transact(function() {
       
   775 							if (dropContent['mce-internal']) {
       
   776 								editor.execCommand('Delete');
       
   777 							}
       
   778 
       
   779 							editor.selection.setRng(rng);
       
   780 
       
   781 							content = Utils.trimHtml(content);
       
   782 
       
   783 							if (!dropContent['text/html']) {
       
   784 								pasteText(content);
       
   785 							} else {
       
   786 								pasteHtml(content);
       
   787 							}
       
   788 						});
       
   789 					}
       
   790 				}
       
   791 			});
       
   792 
       
   793 			editor.on('dragover dragend', function(e) {
       
   794 				if (editor.settings.paste_data_images) {
       
   795 					e.preventDefault();
       
   796 				}
       
   797 			});
       
   798 		}
       
   799 
       
   800 		self.pasteHtml = pasteHtml;
       
   801 		self.pasteText = pasteText;
       
   802 
       
   803 		editor.on('preInit', function() {
       
   804 			registerEventHandlers();
       
   805 
       
   806 			// Remove all data images from paste for example from Gecko
       
   807 			// except internal images like video elements
       
   808 			editor.parser.addNodeFilter('img', function(nodes) {
       
   809 				if (!editor.settings.paste_data_images) {
       
   810 					var i = nodes.length;
       
   811 
       
   812 					while (i--) {
       
   813 						var src = nodes[i].attributes.map.src;
       
   814 
       
   815 						// Some browsers automatically produce data uris on paste
       
   816 						// Safari on Mac produces webkit-fake-url see: https://bugs.webkit.org/show_bug.cgi?id=49141
       
   817 						if (src && /^(data:image|webkit\-fake\-url)/.test(src)) {
       
   818 							if (!nodes[i].attr('data-mce-object') && src !== Env.transparentSrc) {
       
   819 								nodes[i].remove();
       
   820 							}
       
   821 						}
       
   822 					}
       
   823 				}
       
   824 			});
       
   825 		});
       
   826 	};
       
   827 });
       
   828 
       
   829 // Included from: js/tinymce/plugins/paste/classes/WordFilter.js
       
   830 
       
   831 /**
       
   832  * WordFilter.js
       
   833  *
       
   834  * Copyright, Moxiecode Systems AB
       
   835  * Released under LGPL License.
       
   836  *
       
   837  * License: http://www.tinymce.com/license
       
   838  * Contributing: http://www.tinymce.com/contributing
       
   839  */
       
   840 
       
   841 /**
       
   842  * This class parses word HTML into proper TinyMCE markup.
       
   843  *
       
   844  * @class tinymce.pasteplugin.WordFilter
       
   845  * @private
       
   846  */
       
   847 define("tinymce/pasteplugin/WordFilter", [
       
   848 	"tinymce/util/Tools",
       
   849 	"tinymce/html/DomParser",
       
   850 	"tinymce/html/Schema",
       
   851 	"tinymce/html/Serializer",
       
   852 	"tinymce/html/Node",
       
   853 	"tinymce/pasteplugin/Utils"
       
   854 ], function(Tools, DomParser, Schema, Serializer, Node, Utils) {
       
   855 	/**
       
   856 	 * Checks if the specified content is from any of the following sources: MS Word/Office 365/Google docs.
       
   857 	 */
       
   858 	function isWordContent(content) {
       
   859 		return (
       
   860 			(/<font face="Times New Roman"|class="?Mso|style="[^"]*\bmso-|style='[^'']*\bmso-|w:WordDocument/i).test(content) ||
       
   861 			(/class="OutlineElement/).test(content) ||
       
   862 			(/id="?docs\-internal\-guid\-/.test(content))
       
   863 		);
       
   864 	}
       
   865 
       
   866 	/**
       
   867 	 * Checks if the specified text starts with "1. " or "a. " etc.
       
   868 	 */
       
   869 	function isNumericList(text) {
       
   870 		var found, patterns;
       
   871 
       
   872 		patterns = [
       
   873 			/^[IVXLMCD]{1,2}\.[ \u00a0]/,  // Roman upper case
       
   874 			/^[ivxlmcd]{1,2}\.[ \u00a0]/,  // Roman lower case
       
   875 			/^[a-z]{1,2}[\.\)][ \u00a0]/,  // Alphabetical a-z
       
   876 			/^[A-Z]{1,2}[\.\)][ \u00a0]/,  // Alphabetical A-Z
       
   877 			/^[0-9]+\.[ \u00a0]/,          // Numeric lists
       
   878 			/^[\u3007\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d]+\.[ \u00a0]/, // Japanese
       
   879 			/^[\u58f1\u5f10\u53c2\u56db\u4f0d\u516d\u4e03\u516b\u4e5d\u62fe]+\.[ \u00a0]/  // Chinese
       
   880 		];
       
   881 
       
   882 		text = text.replace(/^[\u00a0 ]+/, '');
       
   883 
       
   884 		Tools.each(patterns, function(pattern) {
       
   885 			if (pattern.test(text)) {
       
   886 				found = true;
       
   887 				return false;
       
   888 			}
       
   889 		});
       
   890 
       
   891 		return found;
       
   892 	}
       
   893 
       
   894 	function isBulletList(text) {
       
   895 		return /^[\s\u00a0]*[\u2022\u00b7\u00a7\u25CF]\s*/.test(text);
       
   896 	}
       
   897 
       
   898 	function WordFilter(editor) {
       
   899 		var settings = editor.settings;
       
   900 
       
   901 		editor.on('BeforePastePreProcess', function(e) {
       
   902 			var content = e.content, retainStyleProperties, validStyles;
       
   903 
       
   904 			// Remove google docs internal guid markers
       
   905 			content = content.replace(/<b[^>]+id="?docs-internal-[^>]*>/gi, '');
       
   906 			content = content.replace(/<br class="?Apple-interchange-newline"?>/gi, '');
       
   907 
       
   908 			retainStyleProperties = settings.paste_retain_style_properties;
       
   909 			if (retainStyleProperties) {
       
   910 				validStyles = Tools.makeMap(retainStyleProperties.split(/[, ]/));
       
   911 			}
       
   912 
       
   913 			/**
       
   914 			 * Converts fake bullet and numbered lists to real semantic OL/UL.
       
   915 			 *
       
   916 			 * @param {tinymce.html.Node} node Root node to convert children of.
       
   917 			 */
       
   918 			function convertFakeListsToProperLists(node) {
       
   919 				var currentListNode, prevListNode, lastLevel = 1;
       
   920 
       
   921 				function getText(node) {
       
   922 					var txt = '';
       
   923 
       
   924 					if (node.type === 3) {
       
   925 						return node.value;
       
   926 					}
       
   927 
       
   928 					if ((node = node.firstChild)) {
       
   929 						do {
       
   930 							txt += getText(node);
       
   931 						} while ((node = node.next));
       
   932 					}
       
   933 
       
   934 					return txt;
       
   935 				}
       
   936 
       
   937 				function trimListStart(node, regExp) {
       
   938 					if (node.type === 3) {
       
   939 						if (regExp.test(node.value)) {
       
   940 							node.value = node.value.replace(regExp, '');
       
   941 							return false;
       
   942 						}
       
   943 					}
       
   944 
       
   945 					if ((node = node.firstChild)) {
       
   946 						do {
       
   947 							if (!trimListStart(node, regExp)) {
       
   948 								return false;
       
   949 							}
       
   950 						} while ((node = node.next));
       
   951 					}
       
   952 
       
   953 					return true;
       
   954 				}
       
   955 
       
   956 				function removeIgnoredNodes(node) {
       
   957 					if (node._listIgnore) {
       
   958 						node.remove();
       
   959 						return;
       
   960 					}
       
   961 
       
   962 					if ((node = node.firstChild)) {
       
   963 						do {
       
   964 							removeIgnoredNodes(node);
       
   965 						} while ((node = node.next));
       
   966 					}
       
   967 				}
       
   968 
       
   969 				function convertParagraphToLi(paragraphNode, listName, start) {
       
   970 					var level = paragraphNode._listLevel || lastLevel;
       
   971 
       
   972 					// Handle list nesting
       
   973 					if (level != lastLevel) {
       
   974 						if (level < lastLevel) {
       
   975 							// Move to parent list
       
   976 							if (currentListNode) {
       
   977 								currentListNode = currentListNode.parent.parent;
       
   978 							}
       
   979 						} else {
       
   980 							// Create new list
       
   981 							prevListNode = currentListNode;
       
   982 							currentListNode = null;
       
   983 						}
       
   984 					}
       
   985 
       
   986 					if (!currentListNode || currentListNode.name != listName) {
       
   987 						prevListNode = prevListNode || currentListNode;
       
   988 						currentListNode = new Node(listName, 1);
       
   989 
       
   990 						if (start > 1) {
       
   991 							currentListNode.attr('start', '' + start);
       
   992 						}
       
   993 
       
   994 						paragraphNode.wrap(currentListNode);
       
   995 					} else {
       
   996 						currentListNode.append(paragraphNode);
       
   997 					}
       
   998 
       
   999 					paragraphNode.name = 'li';
       
  1000 
       
  1001 					// Append list to previous list if it exists
       
  1002 					if (level > lastLevel && prevListNode) {
       
  1003 						prevListNode.lastChild.append(currentListNode);
       
  1004 					}
       
  1005 
       
  1006 					lastLevel = level;
       
  1007 
       
  1008 					// Remove start of list item "1. " or "&middot; " etc
       
  1009 					removeIgnoredNodes(paragraphNode);
       
  1010 					trimListStart(paragraphNode, /^\u00a0+/);
       
  1011 					trimListStart(paragraphNode, /^\s*([\u2022\u00b7\u00a7\u25CF]|\w+\.)/);
       
  1012 					trimListStart(paragraphNode, /^\u00a0+/);
       
  1013 				}
       
  1014 
       
  1015 				// Build a list of all root level elements before we start
       
  1016 				// altering them in the loop below.
       
  1017 				var elements = [], child = node.firstChild;
       
  1018 				while (typeof child !== 'undefined' && child !== null) {
       
  1019 					elements.push(child);
       
  1020 
       
  1021 					child = child.walk();
       
  1022 					if (child !== null) {
       
  1023 						while (typeof child !== 'undefined' && child.parent !== node) {
       
  1024 							child = child.walk();
       
  1025 						}
       
  1026 					}
       
  1027 				}
       
  1028 
       
  1029 				for (var i = 0; i < elements.length; i++) {
       
  1030 					node = elements[i];
       
  1031 
       
  1032 					if (node.name == 'p' && node.firstChild) {
       
  1033 						// Find first text node in paragraph
       
  1034 						var nodeText = getText(node);
       
  1035 
       
  1036 						// Detect unordered lists look for bullets
       
  1037 						if (isBulletList(nodeText)) {
       
  1038 							convertParagraphToLi(node, 'ul');
       
  1039 							continue;
       
  1040 						}
       
  1041 
       
  1042 						// Detect ordered lists 1., a. or ixv.
       
  1043 						if (isNumericList(nodeText)) {
       
  1044 							// Parse OL start number
       
  1045 							var matches = /([0-9]+)\./.exec(nodeText);
       
  1046 							var start = 1;
       
  1047 							if (matches) {
       
  1048 								start = parseInt(matches[1], 10);
       
  1049 							}
       
  1050 
       
  1051 							convertParagraphToLi(node, 'ol', start);
       
  1052 							continue;
       
  1053 						}
       
  1054 
       
  1055 						// Convert paragraphs marked as lists but doesn't look like anything
       
  1056 						if (node._listLevel) {
       
  1057 							convertParagraphToLi(node, 'ul', 1);
       
  1058 							continue;
       
  1059 						}
       
  1060 
       
  1061 						currentListNode = null;
       
  1062 					} else {
       
  1063 						// If the root level element isn't a p tag which can be
       
  1064 						// processed by convertParagraphToLi, it interrupts the
       
  1065 						// lists, causing a new list to start instead of having
       
  1066 						// elements from the next list inserted above this tag.
       
  1067 						prevListNode = currentListNode;
       
  1068 						currentListNode = null;
       
  1069 					}
       
  1070 				}
       
  1071 			}
       
  1072 
       
  1073 			function filterStyles(node, styleValue) {
       
  1074 				var outputStyles = {}, matches, styles = editor.dom.parseStyle(styleValue);
       
  1075 
       
  1076 				Tools.each(styles, function(value, name) {
       
  1077 					// Convert various MS styles to W3C styles
       
  1078 					switch (name) {
       
  1079 						case 'mso-list':
       
  1080 							// Parse out list indent level for lists
       
  1081 							matches = /\w+ \w+([0-9]+)/i.exec(styleValue);
       
  1082 							if (matches) {
       
  1083 								node._listLevel = parseInt(matches[1], 10);
       
  1084 							}
       
  1085 
       
  1086 							// Remove these nodes <span style="mso-list:Ignore">o</span>
       
  1087 							// Since the span gets removed we mark the text node and the span
       
  1088 							if (/Ignore/i.test(value) && node.firstChild) {
       
  1089 								node._listIgnore = true;
       
  1090 								node.firstChild._listIgnore = true;
       
  1091 							}
       
  1092 
       
  1093 							break;
       
  1094 
       
  1095 						case "horiz-align":
       
  1096 							name = "text-align";
       
  1097 							break;
       
  1098 
       
  1099 						case "vert-align":
       
  1100 							name = "vertical-align";
       
  1101 							break;
       
  1102 
       
  1103 						case "font-color":
       
  1104 						case "mso-foreground":
       
  1105 							name = "color";
       
  1106 							break;
       
  1107 
       
  1108 						case "mso-background":
       
  1109 						case "mso-highlight":
       
  1110 							name = "background";
       
  1111 							break;
       
  1112 
       
  1113 						case "font-weight":
       
  1114 						case "font-style":
       
  1115 							if (value != "normal") {
       
  1116 								outputStyles[name] = value;
       
  1117 							}
       
  1118 							return;
       
  1119 
       
  1120 						case "mso-element":
       
  1121 							// Remove track changes code
       
  1122 							if (/^(comment|comment-list)$/i.test(value)) {
       
  1123 								node.remove();
       
  1124 								return;
       
  1125 							}
       
  1126 
       
  1127 							break;
       
  1128 					}
       
  1129 
       
  1130 					if (name.indexOf('mso-comment') === 0) {
       
  1131 						node.remove();
       
  1132 						return;
       
  1133 					}
       
  1134 
       
  1135 					// Never allow mso- prefixed names
       
  1136 					if (name.indexOf('mso-') === 0) {
       
  1137 						return;
       
  1138 					}
       
  1139 
       
  1140 					// Output only valid styles
       
  1141 					if (retainStyleProperties == "all" || (validStyles && validStyles[name])) {
       
  1142 						outputStyles[name] = value;
       
  1143 					}
       
  1144 				});
       
  1145 
       
  1146 				// Convert bold style to "b" element
       
  1147 				if (/(bold)/i.test(outputStyles["font-weight"])) {
       
  1148 					delete outputStyles["font-weight"];
       
  1149 					node.wrap(new Node("b", 1));
       
  1150 				}
       
  1151 
       
  1152 				// Convert italic style to "i" element
       
  1153 				if (/(italic)/i.test(outputStyles["font-style"])) {
       
  1154 					delete outputStyles["font-style"];
       
  1155 					node.wrap(new Node("i", 1));
       
  1156 				}
       
  1157 
       
  1158 				// Serialize the styles and see if there is something left to keep
       
  1159 				outputStyles = editor.dom.serializeStyle(outputStyles, node.name);
       
  1160 				if (outputStyles) {
       
  1161 					return outputStyles;
       
  1162 				}
       
  1163 
       
  1164 				return null;
       
  1165 			}
       
  1166 
       
  1167 			if (settings.paste_enable_default_filters === false) {
       
  1168 				return;
       
  1169 			}
       
  1170 
       
  1171 			// Detect is the contents is Word junk HTML
       
  1172 			if (isWordContent(e.content)) {
       
  1173 				e.wordContent = true; // Mark it for other processors
       
  1174 
       
  1175 				// Remove basic Word junk
       
  1176 				content = Utils.filter(content, [
       
  1177 					// Word comments like conditional comments etc
       
  1178 					/<!--[\s\S]+?-->/gi,
       
  1179 
       
  1180 					// Remove comments, scripts (e.g., msoShowComment), XML tag, VML content,
       
  1181 					// MS Office namespaced tags, and a few other tags
       
  1182 					/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,
       
  1183 
       
  1184 					// Convert <s> into <strike> for line-though
       
  1185 					[/<(\/?)s>/gi, "<$1strike>"],
       
  1186 
       
  1187 					// Replace nsbp entites to char since it's easier to handle
       
  1188 					[/&nbsp;/gi, "\u00a0"],
       
  1189 
       
  1190 					// Convert <span style="mso-spacerun:yes">___</span> to string of alternating
       
  1191 					// breaking/non-breaking spaces of same length
       
  1192 					[/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi,
       
  1193 						function(str, spaces) {
       
  1194 							return (spaces.length > 0) ?
       
  1195 								spaces.replace(/./, " ").slice(Math.floor(spaces.length / 2)).split("").join("\u00a0") : "";
       
  1196 						}
       
  1197 					]
       
  1198 				]);
       
  1199 
       
  1200 				var validElements = settings.paste_word_valid_elements;
       
  1201 				if (!validElements) {
       
  1202 					validElements = (
       
  1203 						'-strong/b,-em/i,-u,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,' +
       
  1204 						'-p/div,-a[href|name],sub,sup,strike,br,del,table[width],tr,' +
       
  1205 						'td[colspan|rowspan|width],th[colspan|rowspan|width],thead,tfoot,tbody'
       
  1206 					);
       
  1207 				}
       
  1208 
       
  1209 				// Setup strict schema
       
  1210 				var schema = new Schema({
       
  1211 					valid_elements: validElements,
       
  1212 					valid_children: '-li[p]'
       
  1213 				});
       
  1214 
       
  1215 				// Add style/class attribute to all element rules since the user might have removed them from
       
  1216 				// paste_word_valid_elements config option and we need to check them for properties
       
  1217 				Tools.each(schema.elements, function(rule) {
       
  1218 					/*eslint dot-notation:0*/
       
  1219 					if (!rule.attributes["class"]) {
       
  1220 						rule.attributes["class"] = {};
       
  1221 						rule.attributesOrder.push("class");
       
  1222 					}
       
  1223 
       
  1224 					if (!rule.attributes.style) {
       
  1225 						rule.attributes.style = {};
       
  1226 						rule.attributesOrder.push("style");
       
  1227 					}
       
  1228 				});
       
  1229 
       
  1230 				// Parse HTML into DOM structure
       
  1231 				var domParser = new DomParser({}, schema);
       
  1232 
       
  1233 				// Filter styles to remove "mso" specific styles and convert some of them
       
  1234 				domParser.addAttributeFilter('style', function(nodes) {
       
  1235 					var i = nodes.length, node;
       
  1236 
       
  1237 					while (i--) {
       
  1238 						node = nodes[i];
       
  1239 						node.attr('style', filterStyles(node, node.attr('style')));
       
  1240 
       
  1241 						// Remove pointess spans
       
  1242 						if (node.name == 'span' && node.parent && !node.attributes.length) {
       
  1243 							node.unwrap();
       
  1244 						}
       
  1245 					}
       
  1246 				});
       
  1247 
       
  1248 				// Check the class attribute for comments or del items and remove those
       
  1249 				domParser.addAttributeFilter('class', function(nodes) {
       
  1250 					var i = nodes.length, node, className;
       
  1251 
       
  1252 					while (i--) {
       
  1253 						node = nodes[i];
       
  1254 
       
  1255 						className = node.attr('class');
       
  1256 						if (/^(MsoCommentReference|MsoCommentText|msoDel)$/i.test(className)) {
       
  1257 							node.remove();
       
  1258 						}
       
  1259 
       
  1260 						node.attr('class', null);
       
  1261 					}
       
  1262 				});
       
  1263 
       
  1264 				// Remove all del elements since we don't want the track changes code in the editor
       
  1265 				domParser.addNodeFilter('del', function(nodes) {
       
  1266 					var i = nodes.length;
       
  1267 
       
  1268 					while (i--) {
       
  1269 						nodes[i].remove();
       
  1270 					}
       
  1271 				});
       
  1272 
       
  1273 				// Keep some of the links and anchors
       
  1274 				domParser.addNodeFilter('a', function(nodes) {
       
  1275 					var i = nodes.length, node, href, name;
       
  1276 
       
  1277 					while (i--) {
       
  1278 						node = nodes[i];
       
  1279 						href = node.attr('href');
       
  1280 						name = node.attr('name');
       
  1281 
       
  1282 						if (href && href.indexOf('#_msocom_') != -1) {
       
  1283 							node.remove();
       
  1284 							continue;
       
  1285 						}
       
  1286 
       
  1287 						if (href && href.indexOf('file://') === 0) {
       
  1288 							href = href.split('#')[1];
       
  1289 							if (href) {
       
  1290 								href = '#' + href;
       
  1291 							}
       
  1292 						}
       
  1293 
       
  1294 						if (!href && !name) {
       
  1295 							node.unwrap();
       
  1296 						} else {
       
  1297 							// Remove all named anchors that aren't specific to TOC, Footnotes or Endnotes
       
  1298 							if (name && !/^_?(?:toc|edn|ftn)/i.test(name)) {
       
  1299 								node.unwrap();
       
  1300 								continue;
       
  1301 							}
       
  1302 
       
  1303 							node.attr({
       
  1304 								href: href,
       
  1305 								name: name
       
  1306 							});
       
  1307 						}
       
  1308 					}
       
  1309 				});
       
  1310 
       
  1311 				// Parse into DOM structure
       
  1312 				var rootNode = domParser.parse(content);
       
  1313 
       
  1314 				// Process DOM
       
  1315 				if (settings.paste_convert_word_fake_lists !== false) {
       
  1316 					convertFakeListsToProperLists(rootNode);
       
  1317 				}
       
  1318 
       
  1319 				// Serialize DOM back to HTML
       
  1320 				e.content = new Serializer({}, schema).serialize(rootNode);
       
  1321 			}
       
  1322 		});
       
  1323 	}
       
  1324 
       
  1325 	WordFilter.isWordContent = isWordContent;
       
  1326 
       
  1327 	return WordFilter;
       
  1328 });
       
  1329 
       
  1330 // Included from: js/tinymce/plugins/paste/classes/Quirks.js
       
  1331 
       
  1332 /**
       
  1333  * Quirks.js
       
  1334  *
       
  1335  * Copyright, Moxiecode Systems AB
       
  1336  * Released under LGPL License.
       
  1337  *
       
  1338  * License: http://www.tinymce.com/license
       
  1339  * Contributing: http://www.tinymce.com/contributing
       
  1340  */
       
  1341 
       
  1342 /**
       
  1343  * This class contains various fixes for browsers. These issues can not be feature
       
  1344  * detected since we have no direct control over the clipboard. However we might be able
       
  1345  * to remove some of these fixes once the browsers gets updated/fixed.
       
  1346  *
       
  1347  * @class tinymce.pasteplugin.Quirks
       
  1348  * @private
       
  1349  */
       
  1350 define("tinymce/pasteplugin/Quirks", [
       
  1351 	"tinymce/Env",
       
  1352 	"tinymce/util/Tools",
       
  1353 	"tinymce/pasteplugin/WordFilter",
       
  1354 	"tinymce/pasteplugin/Utils"
       
  1355 ], function(Env, Tools, WordFilter, Utils) {
       
  1356 	"use strict";
       
  1357 
       
  1358 	return function(editor) {
       
  1359 		function addPreProcessFilter(filterFunc) {
       
  1360 			editor.on('BeforePastePreProcess', function(e) {
       
  1361 				e.content = filterFunc(e.content);
       
  1362 			});
       
  1363 		}
       
  1364 
       
  1365 		/**
       
  1366 		 * Removes BR elements after block elements. IE9 has a nasty bug where it puts a BR element after each
       
  1367 		 * block element when pasting from word. This removes those elements.
       
  1368 		 *
       
  1369 		 * This:
       
  1370 		 *  <p>a</p><br><p>b</p>
       
  1371 		 *
       
  1372 		 * Becomes:
       
  1373 		 *  <p>a</p><p>b</p>
       
  1374 		 */
       
  1375 		function removeExplorerBrElementsAfterBlocks(html) {
       
  1376 			// Only filter word specific content
       
  1377 			if (!WordFilter.isWordContent(html)) {
       
  1378 				return html;
       
  1379 			}
       
  1380 
       
  1381 			// Produce block regexp based on the block elements in schema
       
  1382 			var blockElements = [];
       
  1383 
       
  1384 			Tools.each(editor.schema.getBlockElements(), function(block, blockName) {
       
  1385 				blockElements.push(blockName);
       
  1386 			});
       
  1387 
       
  1388 			var explorerBlocksRegExp = new RegExp(
       
  1389 				'(?:<br>&nbsp;[\\s\\r\\n]+|<br>)*(<\\/?(' + blockElements.join('|') + ')[^>]*>)(?:<br>&nbsp;[\\s\\r\\n]+|<br>)*',
       
  1390 				'g'
       
  1391 			);
       
  1392 
       
  1393 			// Remove BR:s from: <BLOCK>X</BLOCK><BR>
       
  1394 			html = Utils.filter(html, [
       
  1395 				[explorerBlocksRegExp, '$1']
       
  1396 			]);
       
  1397 
       
  1398 			// IE9 also adds an extra BR element for each soft-linefeed and it also adds a BR for each word wrap break
       
  1399 			html = Utils.filter(html, [
       
  1400 				[/<br><br>/g, '<BR><BR>'], // Replace multiple BR elements with uppercase BR to keep them intact
       
  1401 				[/<br>/g, ' '],            // Replace single br elements with space since they are word wrap BR:s
       
  1402 				[/<BR><BR>/g, '<br>']      // Replace back the double brs but into a single BR
       
  1403 			]);
       
  1404 
       
  1405 			return html;
       
  1406 		}
       
  1407 
       
  1408 		/**
       
  1409 		 * WebKit has a nasty bug where the all computed styles gets added to style attributes when copy/pasting contents.
       
  1410 		 * This fix solves that by simply removing the whole style attribute.
       
  1411 		 *
       
  1412 		 * The paste_webkit_styles option can be set to specify what to keep:
       
  1413 		 *  paste_webkit_styles: "none" // Keep no styles
       
  1414 		 *  paste_webkit_styles: "all", // Keep all of them
       
  1415 		 *  paste_webkit_styles: "font-weight color" // Keep specific ones
       
  1416 		 *
       
  1417 		 * @param {String} content Content that needs to be processed.
       
  1418 		 * @return {String} Processed contents.
       
  1419 		 */
       
  1420 		function removeWebKitStyles(content) {
       
  1421 			// Passthrough all styles from Word and let the WordFilter handle that junk
       
  1422 			if (WordFilter.isWordContent(content)) {
       
  1423 				return content;
       
  1424 			}
       
  1425 
       
  1426 			// Filter away styles that isn't matching the target node
       
  1427 			var webKitStyles = editor.settings.paste_webkit_styles;
       
  1428 
       
  1429 			if (editor.settings.paste_remove_styles_if_webkit === false || webKitStyles == "all") {
       
  1430 				return content;
       
  1431 			}
       
  1432 
       
  1433 			if (webKitStyles) {
       
  1434 				webKitStyles = webKitStyles.split(/[, ]/);
       
  1435 			}
       
  1436 
       
  1437 			// Keep specific styles that doesn't match the current node computed style
       
  1438 			if (webKitStyles) {
       
  1439 				var dom = editor.dom, node = editor.selection.getNode();
       
  1440 
       
  1441 				content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, function(all, before, value, after) {
       
  1442 					var inputStyles = dom.parseStyle(value, 'span'), outputStyles = {};
       
  1443 
       
  1444 					if (webKitStyles === "none") {
       
  1445 						return before + after;
       
  1446 					}
       
  1447 
       
  1448 					for (var i = 0; i < webKitStyles.length; i++) {
       
  1449 						var inputValue = inputStyles[webKitStyles[i]], currentValue = dom.getStyle(node, webKitStyles[i], true);
       
  1450 
       
  1451 						if (/color/.test(webKitStyles[i])) {
       
  1452 							inputValue = dom.toHex(inputValue);
       
  1453 							currentValue = dom.toHex(currentValue);
       
  1454 						}
       
  1455 
       
  1456 						if (currentValue != inputValue) {
       
  1457 							outputStyles[webKitStyles[i]] = inputValue;
       
  1458 						}
       
  1459 					}
       
  1460 
       
  1461 					outputStyles = dom.serializeStyle(outputStyles, 'span');
       
  1462 					if (outputStyles) {
       
  1463 						return before + ' style="' + outputStyles + '"' + after;
       
  1464 					}
       
  1465 
       
  1466 					return before + after;
       
  1467 				});
       
  1468 			} else {
       
  1469 				// Remove all external styles
       
  1470 				content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, '$1$3');
       
  1471 			}
       
  1472 
       
  1473 			// Keep internal styles
       
  1474 			content = content.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi, function(all, before, value, after) {
       
  1475 				return before + ' style="' + value + '"' + after;
       
  1476 			});
       
  1477 
       
  1478 			return content;
       
  1479 		}
       
  1480 
       
  1481 		// Sniff browsers and apply fixes since we can't feature detect
       
  1482 		if (Env.webkit) {
       
  1483 			addPreProcessFilter(removeWebKitStyles);
       
  1484 		}
       
  1485 
       
  1486 		if (Env.ie) {
       
  1487 			addPreProcessFilter(removeExplorerBrElementsAfterBlocks);
       
  1488 		}
       
  1489 	};
       
  1490 });
       
  1491 
       
  1492 // Included from: js/tinymce/plugins/paste/classes/Plugin.js
       
  1493 
       
  1494 /**
       
  1495  * Plugin.js
       
  1496  *
       
  1497  * Copyright, Moxiecode Systems AB
       
  1498  * Released under LGPL License.
       
  1499  *
       
  1500  * License: http://www.tinymce.com/license
       
  1501  * Contributing: http://www.tinymce.com/contributing
       
  1502  */
       
  1503 
       
  1504 /**
       
  1505  * This class contains the tinymce plugin logic for the paste plugin.
       
  1506  *
       
  1507  * @class tinymce.pasteplugin.Plugin
       
  1508  * @private
       
  1509  */
       
  1510 define("tinymce/pasteplugin/Plugin", [
       
  1511 	"tinymce/PluginManager",
       
  1512 	"tinymce/pasteplugin/Clipboard",
       
  1513 	"tinymce/pasteplugin/WordFilter",
       
  1514 	"tinymce/pasteplugin/Quirks"
       
  1515 ], function(PluginManager, Clipboard, WordFilter, Quirks) {
       
  1516 	var userIsInformed;
       
  1517 
       
  1518 	PluginManager.add('paste', function(editor) {
       
  1519 		var self = this, clipboard, settings = editor.settings;
       
  1520 
       
  1521 		function togglePlainTextPaste() {
       
  1522 			if (clipboard.pasteFormat == "text") {
       
  1523 				this.active(false);
       
  1524 				clipboard.pasteFormat = "html";
       
  1525 			} else {
       
  1526 				clipboard.pasteFormat = "text";
       
  1527 				this.active(true);
       
  1528 
       
  1529 				if (!userIsInformed) {
       
  1530 					editor.windowManager.alert(
       
  1531 						'Paste is now in plain text mode. Contents will now ' +
       
  1532 						'be pasted as plain text until you toggle this option off.'
       
  1533 					);
       
  1534 
       
  1535 					userIsInformed = true;
       
  1536 				}
       
  1537 			}
       
  1538 		}
       
  1539 
       
  1540 		self.clipboard = clipboard = new Clipboard(editor);
       
  1541 		self.quirks = new Quirks(editor);
       
  1542 		self.wordFilter = new WordFilter(editor);
       
  1543 
       
  1544 		if (editor.settings.paste_as_text) {
       
  1545 			self.clipboard.pasteFormat = "text";
       
  1546 		}
       
  1547 
       
  1548 		if (settings.paste_preprocess) {
       
  1549 			editor.on('PastePreProcess', function(e) {
       
  1550 				settings.paste_preprocess.call(self, self, e);
       
  1551 			});
       
  1552 		}
       
  1553 
       
  1554 		if (settings.paste_postprocess) {
       
  1555 			editor.on('PastePostProcess', function(e) {
       
  1556 				settings.paste_postprocess.call(self, self, e);
       
  1557 			});
       
  1558 		}
       
  1559 
       
  1560 		editor.addCommand('mceInsertClipboardContent', function(ui, value) {
       
  1561 			if (value.content) {
       
  1562 				self.clipboard.pasteHtml(value.content);
       
  1563 			}
       
  1564 
       
  1565 			if (value.text) {
       
  1566 				self.clipboard.pasteText(value.text);
       
  1567 			}
       
  1568 		});
       
  1569 
       
  1570 		// Block all drag/drop events
       
  1571 		if (editor.paste_block_drop) {
       
  1572 			editor.on('dragend dragover draggesture dragdrop drop drag', function(e) {
       
  1573 				e.preventDefault();
       
  1574 				e.stopPropagation();
       
  1575 			});
       
  1576 		}
       
  1577 
       
  1578 		// Prevent users from dropping data images on Gecko
       
  1579 		if (!editor.settings.paste_data_images) {
       
  1580 			editor.on('drop', function(e) {
       
  1581 				var dataTransfer = e.dataTransfer;
       
  1582 
       
  1583 				if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
       
  1584 					e.preventDefault();
       
  1585 				}
       
  1586 			});
       
  1587 		}
       
  1588 
       
  1589 		editor.addButton('pastetext', {
       
  1590 			icon: 'pastetext',
       
  1591 			tooltip: 'Paste as text',
       
  1592 			onclick: togglePlainTextPaste,
       
  1593 			active: self.clipboard.pasteFormat == "text"
       
  1594 		});
       
  1595 
       
  1596 		editor.addMenuItem('pastetext', {
       
  1597 			text: 'Paste as text',
       
  1598 			selectable: true,
       
  1599 			active: clipboard.pasteFormat,
       
  1600 			onclick: togglePlainTextPaste
       
  1601 		});
       
  1602 	});
       
  1603 });
       
  1604 
       
  1605 expose(["tinymce/pasteplugin/Utils"]);
       
  1606 })(this);