home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress2 / wp-includes / js / tinymce / plugins / paste / plugin.js next >
Encoding:
JavaScript  |  2017-09-26  |  74.1 KB  |  2,489 lines

  1. (function () {
  2.  
  3. var defs = {}; // id -> {dependencies, definition, instance (possibly undefined)}
  4.  
  5. // Used when there is no 'main' module.
  6. // The name is probably (hopefully) unique so minification removes for releases.
  7. var register_3795 = function (id) {
  8.   var module = dem(id);
  9.   var fragments = id.split('.');
  10.   var target = Function('return this;')();
  11.   for (var i = 0; i < fragments.length - 1; ++i) {
  12.     if (target[fragments[i]] === undefined)
  13.       target[fragments[i]] = {};
  14.     target = target[fragments[i]];
  15.   }
  16.   target[fragments[fragments.length - 1]] = module;
  17. };
  18.  
  19. var instantiate = function (id) {
  20.   var actual = defs[id];
  21.   var dependencies = actual.deps;
  22.   var definition = actual.defn;
  23.   var len = dependencies.length;
  24.   var instances = new Array(len);
  25.   for (var i = 0; i < len; ++i)
  26.     instances[i] = dem(dependencies[i]);
  27.   var defResult = definition.apply(null, instances);
  28.   if (defResult === undefined)
  29.      throw 'module [' + id + '] returned undefined';
  30.   actual.instance = defResult;
  31. };
  32.  
  33. var def = function (id, dependencies, definition) {
  34.   if (typeof id !== 'string')
  35.     throw 'module id must be a string';
  36.   else if (dependencies === undefined)
  37.     throw 'no dependencies for ' + id;
  38.   else if (definition === undefined)
  39.     throw 'no definition function for ' + id;
  40.   defs[id] = {
  41.     deps: dependencies,
  42.     defn: definition,
  43.     instance: undefined
  44.   };
  45. };
  46.  
  47. var dem = function (id) {
  48.   var actual = defs[id];
  49.   if (actual === undefined)
  50.     throw 'module [' + id + '] was undefined';
  51.   else if (actual.instance === undefined)
  52.     instantiate(id);
  53.   return actual.instance;
  54. };
  55.  
  56. var req = function (ids, callback) {
  57.   var len = ids.length;
  58.   var instances = new Array(len);
  59.   for (var i = 0; i < len; ++i)
  60.     instances.push(dem(ids[i]));
  61.   callback.apply(null, callback);
  62. };
  63.  
  64. var ephox = {};
  65.  
  66. ephox.bolt = {
  67.   module: {
  68.     api: {
  69.       define: def,
  70.       require: req,
  71.       demand: dem
  72.     }
  73.   }
  74. };
  75.  
  76. var define = def;
  77. var require = req;
  78. var demand = dem;
  79. // this helps with minificiation when using a lot of global references
  80. var defineGlobal = function (id, ref) {
  81.   define(id, [], function () { return ref; });
  82. };
  83. /*jsc
  84. ["tinymce.plugins.paste.Plugin","tinymce.core.PluginManager","tinymce.plugins.paste.api.Events","tinymce.plugins.paste.core.Clipboard","tinymce.plugins.paste.core.CutCopy","tinymce.plugins.paste.core.Quirks","global!tinymce.util.Tools.resolve","tinymce.core.dom.RangeUtils","tinymce.core.Env","tinymce.core.util.Delay","tinymce.core.util.Tools","tinymce.core.util.VK","tinymce.plugins.paste.core.InternalHtml","tinymce.plugins.paste.core.Newlines","tinymce.plugins.paste.core.PasteBin","tinymce.plugins.paste.core.ProcessFilters","tinymce.plugins.paste.core.SmartPaste","tinymce.plugins.paste.core.Utils","tinymce.plugins.paste.core.WordFilter","tinymce.core.html.Entities","tinymce.core.html.DomParser","tinymce.core.html.Schema","tinymce.core.html.Serializer","tinymce.core.html.Node"]
  85. jsc*/
  86. defineGlobal("global!tinymce.util.Tools.resolve", tinymce.util.Tools.resolve);
  87. /**
  88.  * ResolveGlobal.js
  89.  *
  90.  * Released under LGPL License.
  91.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  92.  *
  93.  * License: http://www.tinymce.com/license
  94.  * Contributing: http://www.tinymce.com/contributing
  95.  */
  96.  
  97. define(
  98.   'tinymce.core.PluginManager',
  99.   [
  100.     'global!tinymce.util.Tools.resolve'
  101.   ],
  102.   function (resolve) {
  103.     return resolve('tinymce.PluginManager');
  104.   }
  105. );
  106.  
  107. /**
  108.  * Events.js
  109.  *
  110.  * Released under LGPL License.
  111.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  112.  *
  113.  * License: http://www.tinymce.com/license
  114.  * Contributing: http://www.tinymce.com/contributing
  115.  */
  116.  
  117. define(
  118.   'tinymce.plugins.paste.api.Events',
  119.   [
  120.   ],
  121.   function () {
  122.     var firePastePreProcess = function (editor, html, internal, isWordHtml) {
  123.       return editor.fire('PastePreProcess', { content: html, internal: internal, wordContent: isWordHtml });
  124.     };
  125.  
  126.     var firePastePostProcess = function (editor, node, internal, isWordHtml) {
  127.       return editor.fire('PastePostProcess', { node: node, internal: internal, wordContent: isWordHtml });
  128.     };
  129.  
  130.     var firePastePlainTextToggle = function (editor, state) {
  131.       return editor.fire('PastePlainTextToggle', { state: state });
  132.     };
  133.  
  134.     return {
  135.       firePastePreProcess: firePastePreProcess,
  136.       firePastePostProcess: firePastePostProcess,
  137.       firePastePlainTextToggle: firePastePlainTextToggle
  138.     };
  139.   }
  140. );
  141.  
  142. /**
  143.  * ResolveGlobal.js
  144.  *
  145.  * Released under LGPL License.
  146.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  147.  *
  148.  * License: http://www.tinymce.com/license
  149.  * Contributing: http://www.tinymce.com/contributing
  150.  */
  151.  
  152. define(
  153.   'tinymce.core.dom.RangeUtils',
  154.   [
  155.     'global!tinymce.util.Tools.resolve'
  156.   ],
  157.   function (resolve) {
  158.     return resolve('tinymce.dom.RangeUtils');
  159.   }
  160. );
  161.  
  162. /**
  163.  * ResolveGlobal.js
  164.  *
  165.  * Released under LGPL License.
  166.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  167.  *
  168.  * License: http://www.tinymce.com/license
  169.  * Contributing: http://www.tinymce.com/contributing
  170.  */
  171.  
  172. define(
  173.   'tinymce.core.Env',
  174.   [
  175.     'global!tinymce.util.Tools.resolve'
  176.   ],
  177.   function (resolve) {
  178.     return resolve('tinymce.Env');
  179.   }
  180. );
  181.  
  182. /**
  183.  * ResolveGlobal.js
  184.  *
  185.  * Released under LGPL License.
  186.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  187.  *
  188.  * License: http://www.tinymce.com/license
  189.  * Contributing: http://www.tinymce.com/contributing
  190.  */
  191.  
  192. define(
  193.   'tinymce.core.util.Delay',
  194.   [
  195.     'global!tinymce.util.Tools.resolve'
  196.   ],
  197.   function (resolve) {
  198.     return resolve('tinymce.util.Delay');
  199.   }
  200. );
  201.  
  202. /**
  203.  * ResolveGlobal.js
  204.  *
  205.  * Released under LGPL License.
  206.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  207.  *
  208.  * License: http://www.tinymce.com/license
  209.  * Contributing: http://www.tinymce.com/contributing
  210.  */
  211.  
  212. define(
  213.   'tinymce.core.util.Tools',
  214.   [
  215.     'global!tinymce.util.Tools.resolve'
  216.   ],
  217.   function (resolve) {
  218.     return resolve('tinymce.util.Tools');
  219.   }
  220. );
  221.  
  222. /**
  223.  * ResolveGlobal.js
  224.  *
  225.  * Released under LGPL License.
  226.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  227.  *
  228.  * License: http://www.tinymce.com/license
  229.  * Contributing: http://www.tinymce.com/contributing
  230.  */
  231.  
  232. define(
  233.   'tinymce.core.util.VK',
  234.   [
  235.     'global!tinymce.util.Tools.resolve'
  236.   ],
  237.   function (resolve) {
  238.     return resolve('tinymce.util.VK');
  239.   }
  240. );
  241.  
  242. /**
  243.  * InternalHtml.js
  244.  *
  245.  * Released under LGPL License.
  246.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  247.  *
  248.  * License: http://www.tinymce.com/license
  249.  * Contributing: http://www.tinymce.com/contributing
  250.  */
  251.  
  252. define(
  253.   'tinymce.plugins.paste.core.InternalHtml',
  254.   [
  255.   ],
  256.   function () {
  257.     var internalMimeType = 'x-tinymce/html';
  258.     var internalMark = '<!-- ' + internalMimeType + ' -->';
  259.  
  260.     var mark = function (html) {
  261.       return internalMark + html;
  262.     };
  263.  
  264.     var unmark = function (html) {
  265.       return html.replace(internalMark, '');
  266.     };
  267.  
  268.     var isMarked = function (html) {
  269.       return html.indexOf(internalMark) !== -1;
  270.     };
  271.  
  272.     return {
  273.       mark: mark,
  274.       unmark: unmark,
  275.       isMarked: isMarked,
  276.       internalHtmlMime: function () {
  277.         return internalMimeType;
  278.       }
  279.     };
  280.   }
  281. );
  282. /**
  283.  * ResolveGlobal.js
  284.  *
  285.  * Released under LGPL License.
  286.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  287.  *
  288.  * License: http://www.tinymce.com/license
  289.  * Contributing: http://www.tinymce.com/contributing
  290.  */
  291.  
  292. define(
  293.   'tinymce.core.html.Entities',
  294.   [
  295.     'global!tinymce.util.Tools.resolve'
  296.   ],
  297.   function (resolve) {
  298.     return resolve('tinymce.html.Entities');
  299.   }
  300. );
  301.  
  302. /**
  303.  * Newlines.js
  304.  *
  305.  * Released under LGPL License.
  306.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  307.  *
  308.  * License: http://www.tinymce.com/license
  309.  * Contributing: http://www.tinymce.com/contributing
  310.  */
  311.  
  312. /**
  313.  * Newlines class contains utilities to convert newlines (\n or \r\n) tp BRs or to a combination of the specified block element and BRs
  314.  *
  315.  * @class tinymce.Newlines
  316.  * @private
  317.  */
  318. define(
  319.   'tinymce.plugins.paste.core.Newlines',
  320.   [
  321.     'tinymce.core.util.Tools',
  322.     'tinymce.core.html.Entities'
  323.   ],
  324.   function (Tools, Entities) {
  325.  
  326.     var isPlainText = function (text) {
  327.       // so basically any tag that is not one of the "p, div, span, br", or is one of them, but is followed
  328.       // by some additional characters qualifies the text as not a plain text (having some HTML tags)
  329.       // <span style="white-space:pre"> and <br /> are added as separate exceptions to the rule
  330.       return !/<(?:\/?(?!(?:div|p|br|span)>)\w+|(?:(?!(?:span style="white-space:\s?pre;?">)|br\s?\/>))\w+\s[^>]+)>/i.test(text);
  331.     };
  332.  
  333.  
  334.     var toBRs = function (text) {
  335.       return text.replace(/\r?\n/g, '<br>');
  336.     };
  337.  
  338.  
  339.     var openContainer = function (rootTag, rootAttrs) {
  340.       var key, attrs = [];
  341.       var tag = '<' + rootTag;
  342.  
  343.       if (typeof rootAttrs === 'object') {
  344.         for (key in rootAttrs) {
  345.           if (rootAttrs.hasOwnProperty(key)) {
  346.             attrs.push(key + '="' + Entities.encodeAllRaw(rootAttrs[key]) + '"');
  347.           }
  348.         }
  349.  
  350.         if (attrs.length) {
  351.           tag += ' ' + attrs.join(' ');
  352.         }
  353.       }
  354.       return tag + '>';
  355.     };
  356.  
  357.  
  358.     var toBlockElements = function (text, rootTag, rootAttrs) {
  359.       var blocks = text.split(/\n\n/);
  360.       var tagOpen = openContainer(rootTag, rootAttrs);
  361.       var tagClose = '</' + rootTag + '>';
  362.  
  363.       var paragraphs = Tools.map(blocks, function (p) {
  364.         return p.split(/\n/).join('<br />');
  365.       });
  366.  
  367.       var stitch = function (p) {
  368.         return tagOpen + p + tagClose;
  369.       };
  370.  
  371.       return paragraphs.length === 1 ? paragraphs[0] : Tools.map(paragraphs, stitch).join('');
  372.     };
  373.  
  374.  
  375.     var convert = function (text, rootTag, rootAttrs) {
  376.       return rootTag ? toBlockElements(text, rootTag, rootAttrs) : toBRs(text);
  377.     };
  378.  
  379.  
  380.     return {
  381.       isPlainText: isPlainText,
  382.       convert: convert,
  383.       toBRs: toBRs,
  384.       toBlockElements: toBlockElements
  385.     };
  386.   }
  387. );
  388. /**
  389.  * PasteBin.js
  390.  *
  391.  * Released under LGPL License.
  392.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  393.  *
  394.  * License: http://www.tinymce.com/license
  395.  * Contributing: http://www.tinymce.com/contributing
  396.  */
  397.  
  398. /**
  399.  * @class tinymce.pasteplugin.PasteBin
  400.  * @private
  401.  */
  402. define(
  403.   'tinymce.plugins.paste.core.PasteBin',
  404.   [
  405.     'tinymce.core.util.Tools',
  406.     'tinymce.core.Env'
  407.   ],
  408.   function (Tools, Env) {
  409.     return function (editor) {
  410.       var lastRng;
  411.       var pasteBinDefaultContent = '%MCEPASTEBIN%';
  412.  
  413.       /**
  414.        * Creates a paste bin element as close as possible to the current caret location and places the focus inside that element
  415.        * so that when the real paste event occurs the contents gets inserted into this element
  416.        * instead of the current editor selection element.
  417.        */
  418.       var create = function () {
  419.         var dom = editor.dom, body = editor.getBody();
  420.         var viewport = editor.dom.getViewPort(editor.getWin()), scrollTop = viewport.y, top = 20;
  421.         var pasteBinElm;
  422.         var scrollContainer;
  423.  
  424.         lastRng = editor.selection.getRng();
  425.  
  426.         if (editor.inline) {
  427.           scrollContainer = editor.selection.getScrollContainer();
  428.  
  429.           // Can't always rely on scrollTop returning a useful value.
  430.           // It returns 0 if the browser doesn't support scrollTop for the element or is non-scrollable
  431.           if (scrollContainer && scrollContainer.scrollTop > 0) {
  432.             scrollTop = scrollContainer.scrollTop;
  433.           }
  434.         }
  435.  
  436.         /**
  437.          * Returns the rect of the current caret if the caret is in an empty block before a
  438.          * BR we insert a temporary invisible character that we get the rect this way we always get a proper rect.
  439.          *
  440.          * TODO: This might be useful in core.
  441.          */
  442.         function getCaretRect(rng) {
  443.           var rects, textNode, node, container = rng.startContainer;
  444.  
  445.           rects = rng.getClientRects();
  446.           if (rects.length) {
  447.             return rects[0];
  448.           }
  449.  
  450.           if (!rng.collapsed || container.nodeType != 1) {
  451.             return;
  452.           }
  453.  
  454.           node = container.childNodes[lastRng.startOffset];
  455.  
  456.           // Skip empty whitespace nodes
  457.           while (node && node.nodeType == 3 && !node.data.length) {
  458.             node = node.nextSibling;
  459.           }
  460.  
  461.           if (!node) {
  462.             return;
  463.           }
  464.  
  465.           // Check if the location is |<br>
  466.           // TODO: Might need to expand this to say |<table>
  467.           if (node.tagName == 'BR') {
  468.             textNode = dom.doc.createTextNode('\uFEFF');
  469.             node.parentNode.insertBefore(textNode, node);
  470.  
  471.             rng = dom.createRng();
  472.             rng.setStartBefore(textNode);
  473.             rng.setEndAfter(textNode);
  474.  
  475.             rects = rng.getClientRects();
  476.             dom.remove(textNode);
  477.           }
  478.  
  479.           if (rects.length) {
  480.             return rects[0];
  481.           }
  482.         }
  483.  
  484.         // Calculate top cordinate this is needed to avoid scrolling to top of document
  485.         // We want the paste bin to be as close to the caret as possible to avoid scrolling
  486.         if (lastRng.getClientRects) {
  487.           var rect = getCaretRect(lastRng);
  488.  
  489.           if (rect) {
  490.             // Client rects gets us closes to the actual
  491.             // caret location in for example a wrapped paragraph block
  492.             top = scrollTop + (rect.top - dom.getPos(body).y);
  493.           } else {
  494.             top = scrollTop;
  495.  
  496.             // Check if we can find a closer location by checking the range element
  497.             var container = lastRng.startContainer;
  498.             if (container) {
  499.               if (container.nodeType == 3 && container.parentNode != body) {
  500.                 container = container.parentNode;
  501.               }
  502.  
  503.               if (container.nodeType == 1) {
  504.                 top = dom.getPos(container, scrollContainer || body).y;
  505.               }
  506.             }
  507.           }
  508.         }
  509.  
  510.         // Create a pastebin
  511.         pasteBinElm = editor.dom.add(editor.getBody(), 'div', {
  512.           id: "mcepastebin",
  513.           contentEditable: true,
  514.           "data-mce-bogus": "all",
  515.           style: 'position: absolute; top: ' + top + 'px; width: 10px; height: 10px; overflow: hidden; opacity: 0'
  516.         }, pasteBinDefaultContent);
  517.  
  518.         // Move paste bin out of sight since the controlSelection rect gets displayed otherwise on IE and Gecko
  519.         if (Env.ie || Env.gecko) {
  520.           dom.setStyle(pasteBinElm, 'left', dom.getStyle(body, 'direction', true) == 'rtl' ? 0xFFFF : -0xFFFF);
  521.         }
  522.  
  523.         // Prevent focus events from bubbeling fixed FocusManager issues
  524.         dom.bind(pasteBinElm, 'beforedeactivate focusin focusout', function (e) {
  525.           e.stopPropagation();
  526.         });
  527.  
  528.         pasteBinElm.focus();
  529.         editor.selection.select(pasteBinElm, true);
  530.       };
  531.  
  532.       /**
  533.        * Removes the paste bin if it exists.
  534.        */
  535.       var remove = function () {
  536.         if (getEl()) {
  537.           var pasteBinClone;
  538.  
  539.           // WebKit/Blink might clone the div so
  540.           // lets make sure we remove all clones
  541.           // TODO: Man o man is this ugly. WebKit is the new IE! Remove this if they ever fix it!
  542.           while ((pasteBinClone = editor.dom.get('mcepastebin'))) {
  543.             editor.dom.remove(pasteBinClone);
  544.             editor.dom.unbind(pasteBinClone);
  545.           }
  546.  
  547.           if (lastRng) {
  548.             editor.selection.setRng(lastRng);
  549.           }
  550.         }
  551.  
  552.         lastRng = null;
  553.       };
  554.  
  555.  
  556.       var getEl = function () {
  557.         return editor.dom.get('mcepastebin');
  558.       };
  559.  
  560.       /**
  561.        * Returns the contents of the paste bin as a HTML string.
  562.        *
  563.        * @return {String} Get the contents of the paste bin.
  564.        */
  565.       var getHtml = function () {
  566.         var pasteBinElm, pasteBinClones, i, dirtyWrappers, cleanWrapper;
  567.  
  568.         // Since WebKit/Chrome might clone the paste bin when pasting
  569.         // for example: <img style="float: right"> we need to check if any of them contains some useful html.
  570.         // TODO: Man o man is this ugly. WebKit is the new IE! Remove this if they ever fix it!
  571.  
  572.         var copyAndRemove = function (toElm, fromElm) {
  573.           toElm.appendChild(fromElm);
  574.           editor.dom.remove(fromElm, true); // remove, but keep children
  575.         };
  576.  
  577.         // find only top level elements (there might be more nested inside them as well, see TINY-1162)
  578.         pasteBinClones = Tools.grep(editor.getBody().childNodes, function (elm) {
  579.           return elm.id === 'mcepastebin';
  580.         });
  581.         pasteBinElm = pasteBinClones.shift();
  582.  
  583.         // if clones were found, move their content into the first bin
  584.         Tools.each(pasteBinClones, function (pasteBinClone) {
  585.           copyAndRemove(pasteBinElm, pasteBinClone);
  586.         });
  587.  
  588.         // TINY-1162: when copying plain text (from notepad for example) WebKit clones
  589.         // paste bin (with styles and attributes) and uses it as a default  wrapper for
  590.         // the chunks of the content, here we cycle over the whole paste bin and replace
  591.         // those wrappers with a basic div
  592.         dirtyWrappers = editor.dom.select('div[id=mcepastebin]', pasteBinElm);
  593.         for (i = dirtyWrappers.length - 1; i >= 0; i--) {
  594.           cleanWrapper = editor.dom.create('div');
  595.           pasteBinElm.insertBefore(cleanWrapper, dirtyWrappers[i]);
  596.           copyAndRemove(cleanWrapper, dirtyWrappers[i]);
  597.         }
  598.  
  599.         return pasteBinElm ? pasteBinElm.innerHTML : '';
  600.       };
  601.  
  602.  
  603.       var getLastRng = function () {
  604.         return lastRng;
  605.       };
  606.  
  607.  
  608.       var isDefaultContent = function (content) {
  609.         return content === pasteBinDefaultContent;
  610.       };
  611.  
  612.  
  613.       var isPasteBin = function (elm) {
  614.         return elm && elm.id === 'mcepastebin';
  615.       };
  616.  
  617.  
  618.       var isDefault = function () {
  619.         var pasteBinElm = getEl();
  620.         return isPasteBin(pasteBinElm) && isDefaultContent(pasteBinElm.innerHTML);
  621.       };
  622.  
  623.       return {
  624.         create: create,
  625.         remove: remove,
  626.         getEl: getEl,
  627.         getHtml: getHtml,
  628.         getLastRng: getLastRng,
  629.         isDefault: isDefault,
  630.         isDefaultContent: isDefaultContent
  631.       };
  632.     };
  633.   }
  634. );
  635.  
  636. /**
  637.  * ResolveGlobal.js
  638.  *
  639.  * Released under LGPL License.
  640.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  641.  *
  642.  * License: http://www.tinymce.com/license
  643.  * Contributing: http://www.tinymce.com/contributing
  644.  */
  645.  
  646. define(
  647.   'tinymce.core.html.DomParser',
  648.   [
  649.     'global!tinymce.util.Tools.resolve'
  650.   ],
  651.   function (resolve) {
  652.     return resolve('tinymce.html.DomParser');
  653.   }
  654. );
  655.  
  656. /**
  657.  * ResolveGlobal.js
  658.  *
  659.  * Released under LGPL License.
  660.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  661.  *
  662.  * License: http://www.tinymce.com/license
  663.  * Contributing: http://www.tinymce.com/contributing
  664.  */
  665.  
  666. define(
  667.   'tinymce.core.html.Schema',
  668.   [
  669.     'global!tinymce.util.Tools.resolve'
  670.   ],
  671.   function (resolve) {
  672.     return resolve('tinymce.html.Schema');
  673.   }
  674. );
  675.  
  676. /**
  677.  * ResolveGlobal.js
  678.  *
  679.  * Released under LGPL License.
  680.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  681.  *
  682.  * License: http://www.tinymce.com/license
  683.  * Contributing: http://www.tinymce.com/contributing
  684.  */
  685.  
  686. define(
  687.   'tinymce.core.html.Serializer',
  688.   [
  689.     'global!tinymce.util.Tools.resolve'
  690.   ],
  691.   function (resolve) {
  692.     return resolve('tinymce.html.Serializer');
  693.   }
  694. );
  695.  
  696. /**
  697.  * ResolveGlobal.js
  698.  *
  699.  * Released under LGPL License.
  700.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  701.  *
  702.  * License: http://www.tinymce.com/license
  703.  * Contributing: http://www.tinymce.com/contributing
  704.  */
  705.  
  706. define(
  707.   'tinymce.core.html.Node',
  708.   [
  709.     'global!tinymce.util.Tools.resolve'
  710.   ],
  711.   function (resolve) {
  712.     return resolve('tinymce.html.Node');
  713.   }
  714. );
  715.  
  716. /**
  717.  * Utils.js
  718.  *
  719.  * Released under LGPL License.
  720.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  721.  *
  722.  * License: http://www.tinymce.com/license
  723.  * Contributing: http://www.tinymce.com/contributing
  724.  */
  725.  
  726. /**
  727.  * This class contails various utility functions for the paste plugin.
  728.  *
  729.  * @class tinymce.pasteplugin.Utils
  730.  */
  731. define(
  732.   'tinymce.plugins.paste.core.Utils',
  733.   [
  734.     'tinymce.core.util.Tools',
  735.     'tinymce.core.html.DomParser',
  736.     'tinymce.core.html.Schema'
  737.   ],
  738.   function (Tools, DomParser, Schema) {
  739.     function filter(content, items) {
  740.       Tools.each(items, function (v) {
  741.         if (v.constructor == RegExp) {
  742.           content = content.replace(v, '');
  743.         } else {
  744.           content = content.replace(v[0], v[1]);
  745.         }
  746.       });
  747.  
  748.       return content;
  749.     }
  750.  
  751.     /**
  752.      * Gets the innerText of the specified element. It will handle edge cases
  753.      * and works better than textContent on Gecko.
  754.      *
  755.      * @param {String} html HTML string to get text from.
  756.      * @return {String} String of text with line feeds.
  757.      */
  758.     function innerText(html) {
  759.       var schema = new Schema(), domParser = new DomParser({}, schema), text = '';
  760.       var shortEndedElements = schema.getShortEndedElements();
  761.       var ignoreElements = Tools.makeMap('script noscript style textarea video audio iframe object', ' ');
  762.       var blockElements = schema.getBlockElements();
  763.  
  764.       function walk(node) {
  765.         var name = node.name, currentNode = node;
  766.  
  767.         if (name === 'br') {
  768.           text += '\n';
  769.           return;
  770.         }
  771.  
  772.         // img/input/hr
  773.         if (shortEndedElements[name]) {
  774.           text += ' ';
  775.         }
  776.  
  777.         // Ingore script, video contents
  778.         if (ignoreElements[name]) {
  779.           text += ' ';
  780.           return;
  781.         }
  782.  
  783.         if (node.type == 3) {
  784.           text += node.value;
  785.         }
  786.  
  787.         // Walk all children
  788.         if (!node.shortEnded) {
  789.           if ((node = node.firstChild)) {
  790.             do {
  791.               walk(node);
  792.             } while ((node = node.next));
  793.           }
  794.         }
  795.  
  796.         // Add \n or \n\n for blocks or P
  797.         if (blockElements[name] && currentNode.next) {
  798.           text += '\n';
  799.  
  800.           if (name == 'p') {
  801.             text += '\n';
  802.           }
  803.         }
  804.       }
  805.  
  806.       html = filter(html, [
  807.         /<!\[[^\]]+\]>/g // Conditional comments
  808.       ]);
  809.  
  810.       walk(domParser.parse(html));
  811.  
  812.       return text;
  813.     }
  814.  
  815.     /**
  816.      * Trims the specified HTML by removing all WebKit fragments, all elements wrapping the body trailing BR elements etc.
  817.      *
  818.      * @param {String} html Html string to trim contents on.
  819.      * @return {String} Html contents that got trimmed.
  820.      */
  821.     function trimHtml(html) {
  822.       function trimSpaces(all, s1, s2) {
  823.         // WebKit   meant to preserve multiple spaces but instead inserted around all inline tags,
  824.         // including the spans with inline styles created on paste
  825.         if (!s1 && !s2) {
  826.           return ' ';
  827.         }
  828.  
  829.         return '\u00a0';
  830.       }
  831.  
  832.       html = filter(html, [
  833.         /^[\s\S]*<body[^>]*>\s*|\s*<\/body[^>]*>[\s\S]*$/ig, // Remove anything but the contents within the BODY element
  834.         /<!--StartFragment-->|<!--EndFragment-->/g, // Inner fragments (tables from excel on mac)
  835.         [/( ?)<span class="Apple-converted-space">\u00a0<\/span>( ?)/g, trimSpaces],
  836.         /<br class="Apple-interchange-newline">/g,
  837.         /<br>$/i // Trailing BR elements
  838.       ]);
  839.  
  840.       return html;
  841.     }
  842.  
  843.     // TODO: Should be in some global class
  844.     function createIdGenerator(prefix) {
  845.       var count = 0;
  846.  
  847.       return function () {
  848.         return prefix + (count++);
  849.       };
  850.     }
  851.  
  852.     var isMsEdge = function () {
  853.       return navigator.userAgent.indexOf(' Edge/') !== -1;
  854.     };
  855.  
  856.     return {
  857.       filter: filter,
  858.       innerText: innerText,
  859.       trimHtml: trimHtml,
  860.       createIdGenerator: createIdGenerator,
  861.       isMsEdge: isMsEdge
  862.     };
  863.   }
  864. );
  865.  
  866. /**
  867.  * WordFilter.js
  868.  *
  869.  * Released under LGPL License.
  870.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  871.  *
  872.  * License: http://www.tinymce.com/license
  873.  * Contributing: http://www.tinymce.com/contributing
  874.  */
  875.  
  876. /**
  877.  * This class parses word HTML into proper TinyMCE markup.
  878.  *
  879.  * @class tinymce.pasteplugin.WordFilter
  880.  * @private
  881.  */
  882. define(
  883.   'tinymce.plugins.paste.core.WordFilter',
  884.   [
  885.     'tinymce.core.util.Tools',
  886.     'tinymce.core.html.DomParser',
  887.     'tinymce.core.html.Schema',
  888.     'tinymce.core.html.Serializer',
  889.     'tinymce.core.html.Node',
  890.     'tinymce.plugins.paste.core.Utils'
  891.   ],
  892.   function (Tools, DomParser, Schema, Serializer, Node, Utils) {
  893.     /**
  894.      * Checks if the specified content is from any of the following sources: MS Word/Office 365/Google docs.
  895.      */
  896.     function isWordContent(content) {
  897.       return (
  898.         (/<font face="Times New Roman"|class="?Mso|style="[^"]*\bmso-|style='[^'']*\bmso-|w:WordDocument/i).test(content) ||
  899.         (/class="OutlineElement/).test(content) ||
  900.         (/id="?docs\-internal\-guid\-/.test(content))
  901.       );
  902.     }
  903.  
  904.     /**
  905.      * Checks if the specified text starts with "1. " or "a. " etc.
  906.      */
  907.     function isNumericList(text) {
  908.       var found, patterns;
  909.  
  910.       patterns = [
  911.         /^[IVXLMCD]{1,2}\.[ \u00a0]/,  // Roman upper case
  912.         /^[ivxlmcd]{1,2}\.[ \u00a0]/,  // Roman lower case
  913.         /^[a-z]{1,2}[\.\)][ \u00a0]/,  // Alphabetical a-z
  914.         /^[A-Z]{1,2}[\.\)][ \u00a0]/,  // Alphabetical A-Z
  915.         /^[0-9]+\.[ \u00a0]/,          // Numeric lists
  916.         /^[\u3007\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d]+\.[ \u00a0]/, // Japanese
  917.         /^[\u58f1\u5f10\u53c2\u56db\u4f0d\u516d\u4e03\u516b\u4e5d\u62fe]+\.[ \u00a0]/  // Chinese
  918.       ];
  919.  
  920.       text = text.replace(/^[\u00a0 ]+/, '');
  921.  
  922.       Tools.each(patterns, function (pattern) {
  923.         if (pattern.test(text)) {
  924.           found = true;
  925.           return false;
  926.         }
  927.       });
  928.  
  929.       return found;
  930.     }
  931.  
  932.     function isBulletList(text) {
  933.       return /^[\s\u00a0]*[\u2022\u00b7\u00a7\u25CF]\s*/.test(text);
  934.     }
  935.  
  936.     /**
  937.      * Converts fake bullet and numbered lists to real semantic OL/UL.
  938.      *
  939.      * @param {tinymce.html.Node} node Root node to convert children of.
  940.      */
  941.     function convertFakeListsToProperLists(node) {
  942.       var currentListNode, prevListNode, lastLevel = 1;
  943.  
  944.       function getText(node) {
  945.         var txt = '';
  946.  
  947.         if (node.type === 3) {
  948.           return node.value;
  949.         }
  950.  
  951.         if ((node = node.firstChild)) {
  952.           do {
  953.             txt += getText(node);
  954.           } while ((node = node.next));
  955.         }
  956.  
  957.         return txt;
  958.       }
  959.  
  960.       function trimListStart(node, regExp) {
  961.         if (node.type === 3) {
  962.           if (regExp.test(node.value)) {
  963.             node.value = node.value.replace(regExp, '');
  964.             return false;
  965.           }
  966.         }
  967.  
  968.         if ((node = node.firstChild)) {
  969.           do {
  970.             if (!trimListStart(node, regExp)) {
  971.               return false;
  972.             }
  973.           } while ((node = node.next));
  974.         }
  975.  
  976.         return true;
  977.       }
  978.  
  979.       function removeIgnoredNodes(node) {
  980.         if (node._listIgnore) {
  981.           node.remove();
  982.           return;
  983.         }
  984.  
  985.         if ((node = node.firstChild)) {
  986.           do {
  987.             removeIgnoredNodes(node);
  988.           } while ((node = node.next));
  989.         }
  990.       }
  991.  
  992.       function convertParagraphToLi(paragraphNode, listName, start) {
  993.         var level = paragraphNode._listLevel || lastLevel;
  994.  
  995.         // Handle list nesting
  996.         if (level != lastLevel) {
  997.           if (level < lastLevel) {
  998.             // Move to parent list
  999.             if (currentListNode) {
  1000.               currentListNode = currentListNode.parent.parent;
  1001.             }
  1002.           } else {
  1003.             // Create new list
  1004.             prevListNode = currentListNode;
  1005.             currentListNode = null;
  1006.           }
  1007.         }
  1008.  
  1009.         if (!currentListNode || currentListNode.name != listName) {
  1010.           prevListNode = prevListNode || currentListNode;
  1011.           currentListNode = new Node(listName, 1);
  1012.  
  1013.           if (start > 1) {
  1014.             currentListNode.attr('start', '' + start);
  1015.           }
  1016.  
  1017.           paragraphNode.wrap(currentListNode);
  1018.         } else {
  1019.           currentListNode.append(paragraphNode);
  1020.         }
  1021.  
  1022.         paragraphNode.name = 'li';
  1023.  
  1024.         // Append list to previous list if it exists
  1025.         if (level > lastLevel && prevListNode) {
  1026.           prevListNode.lastChild.append(currentListNode);
  1027.         }
  1028.  
  1029.         lastLevel = level;
  1030.  
  1031.         // Remove start of list item "1. " or "· " etc
  1032.         removeIgnoredNodes(paragraphNode);
  1033.         trimListStart(paragraphNode, /^\u00a0+/);
  1034.         trimListStart(paragraphNode, /^\s*([\u2022\u00b7\u00a7\u25CF]|\w+\.)/);
  1035.         trimListStart(paragraphNode, /^\u00a0+/);
  1036.       }
  1037.  
  1038.       // Build a list of all root level elements before we start
  1039.       // altering them in the loop below.
  1040.       var elements = [], child = node.firstChild;
  1041.       while (typeof child !== 'undefined' && child !== null) {
  1042.         elements.push(child);
  1043.  
  1044.         child = child.walk();
  1045.         if (child !== null) {
  1046.           while (typeof child !== 'undefined' && child.parent !== node) {
  1047.             child = child.walk();
  1048.           }
  1049.         }
  1050.       }
  1051.  
  1052.       for (var i = 0; i < elements.length; i++) {
  1053.         node = elements[i];
  1054.  
  1055.         if (node.name == 'p' && node.firstChild) {
  1056.           // Find first text node in paragraph
  1057.           var nodeText = getText(node);
  1058.  
  1059.           // Detect unordered lists look for bullets
  1060.           if (isBulletList(nodeText)) {
  1061.             convertParagraphToLi(node, 'ul');
  1062.             continue;
  1063.           }
  1064.  
  1065.           // Detect ordered lists 1., a. or ixv.
  1066.           if (isNumericList(nodeText)) {
  1067.             // Parse OL start number
  1068.             var matches = /([0-9]+)\./.exec(nodeText);
  1069.             var start = 1;
  1070.             if (matches) {
  1071.               start = parseInt(matches[1], 10);
  1072.             }
  1073.  
  1074.             convertParagraphToLi(node, 'ol', start);
  1075.             continue;
  1076.           }
  1077.  
  1078.           // Convert paragraphs marked as lists but doesn't look like anything
  1079.           if (node._listLevel) {
  1080.             convertParagraphToLi(node, 'ul', 1);
  1081.             continue;
  1082.           }
  1083.  
  1084.           currentListNode = null;
  1085.         } else {
  1086.           // If the root level element isn't a p tag which can be
  1087.           // processed by convertParagraphToLi, it interrupts the
  1088.           // lists, causing a new list to start instead of having
  1089.           // elements from the next list inserted above this tag.
  1090.           prevListNode = currentListNode;
  1091.           currentListNode = null;
  1092.         }
  1093.       }
  1094.     }
  1095.  
  1096.     function filterStyles(editor, validStyles, node, styleValue) {
  1097.       var outputStyles = {}, matches, styles = editor.dom.parseStyle(styleValue);
  1098.  
  1099.       Tools.each(styles, function (value, name) {
  1100.         // Convert various MS styles to W3C styles
  1101.         switch (name) {
  1102.           case 'mso-list':
  1103.             // Parse out list indent level for lists
  1104.             matches = /\w+ \w+([0-9]+)/i.exec(styleValue);
  1105.             if (matches) {
  1106.               node._listLevel = parseInt(matches[1], 10);
  1107.             }
  1108.  
  1109.             // Remove these nodes <span style="mso-list:Ignore">o</span>
  1110.             // Since the span gets removed we mark the text node and the span
  1111.             if (/Ignore/i.test(value) && node.firstChild) {
  1112.               node._listIgnore = true;
  1113.               node.firstChild._listIgnore = true;
  1114.             }
  1115.  
  1116.             break;
  1117.  
  1118.           case "horiz-align":
  1119.             name = "text-align";
  1120.             break;
  1121.  
  1122.           case "vert-align":
  1123.             name = "vertical-align";
  1124.             break;
  1125.  
  1126.           case "font-color":
  1127.           case "mso-foreground":
  1128.             name = "color";
  1129.             break;
  1130.  
  1131.           case "mso-background":
  1132.           case "mso-highlight":
  1133.             name = "background";
  1134.             break;
  1135.  
  1136.           case "font-weight":
  1137.           case "font-style":
  1138.             if (value != "normal") {
  1139.               outputStyles[name] = value;
  1140.             }
  1141.             return;
  1142.  
  1143.           case "mso-element":
  1144.             // Remove track changes code
  1145.             if (/^(comment|comment-list)$/i.test(value)) {
  1146.               node.remove();
  1147.               return;
  1148.             }
  1149.  
  1150.             break;
  1151.         }
  1152.  
  1153.         if (name.indexOf('mso-comment') === 0) {
  1154.           node.remove();
  1155.           return;
  1156.         }
  1157.  
  1158.         // Never allow mso- prefixed names
  1159.         if (name.indexOf('mso-') === 0) {
  1160.           return;
  1161.         }
  1162.  
  1163.         // Output only valid styles
  1164.         if (editor.settings.paste_retain_style_properties == "all" || (validStyles && validStyles[name])) {
  1165.           outputStyles[name] = value;
  1166.         }
  1167.       });
  1168.  
  1169.       // Convert bold style to "b" element
  1170.       if (/(bold)/i.test(outputStyles["font-weight"])) {
  1171.         delete outputStyles["font-weight"];
  1172.         node.wrap(new Node("b", 1));
  1173.       }
  1174.  
  1175.       // Convert italic style to "i" element
  1176.       if (/(italic)/i.test(outputStyles["font-style"])) {
  1177.         delete outputStyles["font-style"];
  1178.         node.wrap(new Node("i", 1));
  1179.       }
  1180.  
  1181.       // Serialize the styles and see if there is something left to keep
  1182.       outputStyles = editor.dom.serializeStyle(outputStyles, node.name);
  1183.       if (outputStyles) {
  1184.         return outputStyles;
  1185.       }
  1186.  
  1187.       return null;
  1188.     }
  1189.  
  1190.     var filterWordContent = function (editor, content) {
  1191.       var retainStyleProperties, validStyles;
  1192.  
  1193.       retainStyleProperties = editor.settings.paste_retain_style_properties;
  1194.       if (retainStyleProperties) {
  1195.         validStyles = Tools.makeMap(retainStyleProperties.split(/[, ]/));
  1196.       }
  1197.  
  1198.       // Remove basic Word junk
  1199.       content = Utils.filter(content, [
  1200.         // Remove apple new line markers
  1201.         /<br class="?Apple-interchange-newline"?>/gi,
  1202.  
  1203.         // Remove google docs internal guid markers
  1204.         /<b[^>]+id="?docs-internal-[^>]*>/gi,
  1205.  
  1206.         // Word comments like conditional comments etc
  1207.         /<!--[\s\S]+?-->/gi,
  1208.  
  1209.         // Remove comments, scripts (e.g., msoShowComment), XML tag, VML content,
  1210.         // MS Office namespaced tags, and a few other tags
  1211.         /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,
  1212.  
  1213.         // Convert <s> into <strike> for line-though
  1214.         [/<(\/?)s>/gi, "<$1strike>"],
  1215.  
  1216.         // Replace nsbp entites to char since it's easier to handle
  1217.         [/ /gi, "\u00a0"],
  1218.  
  1219.         // Convert <span style="mso-spacerun:yes">___</span> to string of alternating
  1220.         // breaking/non-breaking spaces of same length
  1221.         [/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi,
  1222.           function (str, spaces) {
  1223.             return (spaces.length > 0) ?
  1224.               spaces.replace(/./, " ").slice(Math.floor(spaces.length / 2)).split("").join("\u00a0") : "";
  1225.           }
  1226.         ]
  1227.       ]);
  1228.  
  1229.       var validElements = editor.settings.paste_word_valid_elements;
  1230.       if (!validElements) {
  1231.         validElements = (
  1232.           '-strong/b,-em/i,-u,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,' +
  1233.           '-p/div,-a[href|name],sub,sup,strike,br,del,table[width],tr,' +
  1234.           'td[colspan|rowspan|width],th[colspan|rowspan|width],thead,tfoot,tbody'
  1235.         );
  1236.       }
  1237.  
  1238.       // Setup strict schema
  1239.       var schema = new Schema({
  1240.         valid_elements: validElements,
  1241.         valid_children: '-li[p]'
  1242.       });
  1243.  
  1244.       // Add style/class attribute to all element rules since the user might have removed them from
  1245.       // paste_word_valid_elements config option and we need to check them for properties
  1246.       Tools.each(schema.elements, function (rule) {
  1247.         /*eslint dot-notation:0*/
  1248.         if (!rule.attributes["class"]) {
  1249.           rule.attributes["class"] = {};
  1250.           rule.attributesOrder.push("class");
  1251.         }
  1252.  
  1253.         if (!rule.attributes.style) {
  1254.           rule.attributes.style = {};
  1255.           rule.attributesOrder.push("style");
  1256.         }
  1257.       });
  1258.  
  1259.       // Parse HTML into DOM structure
  1260.       var domParser = new DomParser({}, schema);
  1261.  
  1262.       // Filter styles to remove "mso" specific styles and convert some of them
  1263.       domParser.addAttributeFilter('style', function (nodes) {
  1264.         var i = nodes.length, node;
  1265.  
  1266.         while (i--) {
  1267.           node = nodes[i];
  1268.           node.attr('style', filterStyles(editor, validStyles, node, node.attr('style')));
  1269.  
  1270.           // Remove pointess spans
  1271.           if (node.name == 'span' && node.parent && !node.attributes.length) {
  1272.             node.unwrap();
  1273.           }
  1274.         }
  1275.       });
  1276.  
  1277.       // Check the class attribute for comments or del items and remove those
  1278.       domParser.addAttributeFilter('class', function (nodes) {
  1279.         var i = nodes.length, node, className;
  1280.  
  1281.         while (i--) {
  1282.           node = nodes[i];
  1283.  
  1284.           className = node.attr('class');
  1285.           if (/^(MsoCommentReference|MsoCommentText|msoDel)$/i.test(className)) {
  1286.             node.remove();
  1287.           }
  1288.  
  1289.           node.attr('class', null);
  1290.         }
  1291.       });
  1292.  
  1293.       // Remove all del elements since we don't want the track changes code in the editor
  1294.       domParser.addNodeFilter('del', function (nodes) {
  1295.         var i = nodes.length;
  1296.  
  1297.         while (i--) {
  1298.           nodes[i].remove();
  1299.         }
  1300.       });
  1301.  
  1302.       // Keep some of the links and anchors
  1303.       domParser.addNodeFilter('a', function (nodes) {
  1304.         var i = nodes.length, node, href, name;
  1305.  
  1306.         while (i--) {
  1307.           node = nodes[i];
  1308.           href = node.attr('href');
  1309.           name = node.attr('name');
  1310.  
  1311.           if (href && href.indexOf('#_msocom_') != -1) {
  1312.             node.remove();
  1313.             continue;
  1314.           }
  1315.  
  1316.           if (href && href.indexOf('file://') === 0) {
  1317.             href = href.split('#')[1];
  1318.             if (href) {
  1319.               href = '#' + href;
  1320.             }
  1321.           }
  1322.  
  1323.           if (!href && !name) {
  1324.             node.unwrap();
  1325.           } else {
  1326.             // Remove all named anchors that aren't specific to TOC, Footnotes or Endnotes
  1327.             if (name && !/^_?(?:toc|edn|ftn)/i.test(name)) {
  1328.               node.unwrap();
  1329.               continue;
  1330.             }
  1331.  
  1332.             node.attr({
  1333.               href: href,
  1334.               name: name
  1335.             });
  1336.           }
  1337.         }
  1338.       });
  1339.  
  1340.       // Parse into DOM structure
  1341.       var rootNode = domParser.parse(content);
  1342.  
  1343.       // Process DOM
  1344.       if (editor.settings.paste_convert_word_fake_lists !== false) {
  1345.         convertFakeListsToProperLists(rootNode);
  1346.       }
  1347.  
  1348.       // Serialize DOM back to HTML
  1349.       content = new Serializer({
  1350.         validate: editor.settings.validate
  1351.       }, schema).serialize(rootNode);
  1352.  
  1353.       return content;
  1354.     };
  1355.  
  1356.     var preProcess = function (editor, content) {
  1357.       return editor.settings.paste_enable_default_filters === false ? content : filterWordContent(editor, content);
  1358.     };
  1359.  
  1360.     return {
  1361.       preProcess: preProcess,
  1362.       isWordContent: isWordContent
  1363.     };
  1364.   }
  1365. );
  1366.  
  1367. /**
  1368.  * ProcessFilters.js
  1369.  *
  1370.  * Released under LGPL License.
  1371.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  1372.  *
  1373.  * License: http://www.tinymce.com/license
  1374.  * Contributing: http://www.tinymce.com/contributing
  1375.  */
  1376.  
  1377. define(
  1378.   'tinymce.plugins.paste.core.ProcessFilters',
  1379.   [
  1380.     'tinymce.plugins.paste.api.Events',
  1381.     'tinymce.plugins.paste.core.WordFilter'
  1382.   ],
  1383.   function (Events, WordFilter) {
  1384.     var processResult = function (content, cancelled) {
  1385.       return { content: content, cancelled: cancelled };
  1386.     };
  1387.  
  1388.     var postProcessFilter = function (editor, html, internal, isWordHtml) {
  1389.       var tempBody = editor.dom.create('div', { style: 'display:none' }, html);
  1390.       var postProcessArgs = Events.firePastePostProcess(editor, tempBody, internal, isWordHtml);
  1391.       return processResult(postProcessArgs.node.innerHTML, postProcessArgs.isDefaultPrevented());
  1392.     };
  1393.  
  1394.     var filterContent = function (editor, content, internal, isWordHtml) {
  1395.       var preProcessArgs = Events.firePastePreProcess(editor, content, internal, isWordHtml);
  1396.  
  1397.       if (editor.hasEventListeners('PastePostProcess') && !preProcessArgs.isDefaultPrevented()) {
  1398.         return postProcessFilter(editor, preProcessArgs.content, internal, isWordHtml);
  1399.       } else {
  1400.         return processResult(preProcessArgs.content, preProcessArgs.isDefaultPrevented());
  1401.       }
  1402.     };
  1403.  
  1404.     var process = function (editor, html, internal) {
  1405.       var isWordHtml = WordFilter.isWordContent(html);
  1406.       var content = isWordHtml ? WordFilter.preProcess(editor, html) : html;
  1407.  
  1408.       return filterContent(editor, content, internal, isWordHtml);
  1409.     };
  1410.  
  1411.     return {
  1412.       process: process
  1413.     };
  1414.   }
  1415. );
  1416.  
  1417. /**
  1418.  * SmartPaste.js
  1419.  *
  1420.  * Released under LGPL License.
  1421.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  1422.  *
  1423.  * License: http://www.tinymce.com/license
  1424.  * Contributing: http://www.tinymce.com/contributing
  1425.  */
  1426.  
  1427. /**
  1428.  * Tries to be smart depending on what the user pastes if it looks like an url
  1429.  * it will make a link out of the current selection. If it's an image url that looks
  1430.  * like an image it will check if it's an image and insert it as an image.
  1431.  *
  1432.  * @class tinymce.pasteplugin.SmartPaste
  1433.  * @private
  1434.  */
  1435. define(
  1436.   'tinymce.plugins.paste.core.SmartPaste',
  1437.   [
  1438.     'tinymce.core.util.Tools'
  1439.   ],
  1440.   function (Tools) {
  1441.     var isAbsoluteUrl = function (url) {
  1442.       return /^https?:\/\/[\w\?\-\/+=.&%@~#]+$/i.test(url);
  1443.     };
  1444.  
  1445.     var isImageUrl = function (url) {
  1446.       return isAbsoluteUrl(url) && /.(gif|jpe?g|png)$/.test(url);
  1447.     };
  1448.  
  1449.     var createImage = function (editor, url, pasteHtml) {
  1450.       editor.undoManager.extra(function () {
  1451.         pasteHtml(editor, url);
  1452.       }, function () {
  1453.         editor.insertContent('<img src="' + url + '">');
  1454.       });
  1455.  
  1456.       return true;
  1457.     };
  1458.  
  1459.     var createLink = function (editor, url, pasteHtml) {
  1460.       editor.undoManager.extra(function () {
  1461.         pasteHtml(editor, url);
  1462.       }, function () {
  1463.         editor.execCommand('mceInsertLink', false, url);
  1464.       });
  1465.  
  1466.       return true;
  1467.     };
  1468.  
  1469.     var linkSelection = function (editor, html, pasteHtml) {
  1470.       return editor.selection.isCollapsed() === false && isAbsoluteUrl(html) ? createLink(editor, html, pasteHtml) : false;
  1471.     };
  1472.  
  1473.     var insertImage = function (editor, html, pasteHtml) {
  1474.       return isImageUrl(html) ? createImage(editor, html, pasteHtml) : false;
  1475.     };
  1476.  
  1477.     var pasteHtml = function (editor, html) {
  1478.       editor.insertContent(html, {
  1479.         merge: editor.settings.paste_merge_formats !== false,
  1480.         paste: true
  1481.       });
  1482.  
  1483.       return true;
  1484.     };
  1485.  
  1486.     var smartInsertContent = function (editor, html) {
  1487.       Tools.each([
  1488.         linkSelection,
  1489.         insertImage,
  1490.         pasteHtml
  1491.       ], function (action) {
  1492.         return action(editor, html, pasteHtml) !== true;
  1493.       });
  1494.     };
  1495.  
  1496.     var insertContent = function (editor, html) {
  1497.       if (editor.settings.smart_paste === false) {
  1498.         pasteHtml(editor, html);
  1499.       } else {
  1500.         smartInsertContent(editor, html);
  1501.       }
  1502.     };
  1503.  
  1504.     return {
  1505.       isImageUrl: isImageUrl,
  1506.       isAbsoluteUrl: isAbsoluteUrl,
  1507.       insertContent: insertContent
  1508.     };
  1509.   }
  1510. );
  1511.  
  1512. /**
  1513.  * Clipboard.js
  1514.  *
  1515.  * Released under LGPL License.
  1516.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  1517.  *
  1518.  * License: http://www.tinymce.com/license
  1519.  * Contributing: http://www.tinymce.com/contributing
  1520.  */
  1521.  
  1522. /**
  1523.  * This class contains logic for getting HTML contents out of the clipboard.
  1524.  *
  1525.  * We need to make a lot of ugly hacks to get the contents out of the clipboard since
  1526.  * the W3C Clipboard API is broken in all browsers that have it: Gecko/WebKit/Blink.
  1527.  * We might rewrite this the way those API:s stabilize. Browsers doesn't handle pasting
  1528.  * from applications like Word the same way as it does when pasting into a contentEditable area
  1529.  * so we need to do lots of extra work to try to get to this clipboard data.
  1530.  *
  1531.  * Current implementation steps:
  1532.  *  1. On keydown with paste keys Ctrl+V or Shift+Insert create
  1533.  *     a paste bin element and move focus to that element.
  1534.  *  2. Wait for the browser to fire a "paste" event and get the contents out of the paste bin.
  1535.  *  3. Check if the paste was successful if true, process the HTML.
  1536.  *  (4). If the paste was unsuccessful use IE execCommand, Clipboard API, document.dataTransfer old WebKit API etc.
  1537.  *
  1538.  * @class tinymce.pasteplugin.Clipboard
  1539.  * @private
  1540.  */
  1541. define(
  1542.   'tinymce.plugins.paste.core.Clipboard',
  1543.   [
  1544.     'tinymce.core.dom.RangeUtils',
  1545.     'tinymce.core.Env',
  1546.     'tinymce.core.util.Delay',
  1547.     'tinymce.core.util.Tools',
  1548.     'tinymce.core.util.VK',
  1549.     'tinymce.plugins.paste.core.InternalHtml',
  1550.     'tinymce.plugins.paste.core.Newlines',
  1551.     'tinymce.plugins.paste.core.PasteBin',
  1552.     'tinymce.plugins.paste.core.ProcessFilters',
  1553.     'tinymce.plugins.paste.core.SmartPaste',
  1554.     'tinymce.plugins.paste.core.Utils'
  1555.   ],
  1556.   function (RangeUtils, Env, Delay, Tools, VK, InternalHtml, Newlines, PasteBin, ProcessFilters, SmartPaste, Utils) {
  1557.     return function (editor) {
  1558.       var self = this, keyboardPasteTimeStamp = 0, draggingInternally = false;
  1559.       var pasteBin = new PasteBin(editor);
  1560.       var keyboardPastePlainTextState;
  1561.       var mceInternalUrlPrefix = 'data:text/mce-internal,';
  1562.       var uniqueId = Utils.createIdGenerator("mceclip");
  1563.  
  1564.       /**
  1565.        * Pastes the specified HTML. This means that the HTML is filtered and then
  1566.        * inserted at the current selection in the editor. It will also fire paste events
  1567.        * for custom user filtering.
  1568.        *
  1569.        * @param {String} html HTML code to paste into the current selection.
  1570.        * @param {Boolean?} internalFlag Optional true/false flag if the contents is internal or external.
  1571.        */
  1572.       function pasteHtml(html, internalFlag) {
  1573.         var internal = internalFlag ? internalFlag : InternalHtml.isMarked(html);
  1574.         var args = ProcessFilters.process(editor, InternalHtml.unmark(html), internal);
  1575.  
  1576.         if (args.cancelled === false) {
  1577.           SmartPaste.insertContent(editor, args.content);
  1578.         }
  1579.       }
  1580.  
  1581.       /**
  1582.        * Pastes the specified text. This means that the plain text is processed
  1583.        * and converted into BR and P elements. It will fire paste events for custom filtering.
  1584.        *
  1585.        * @param {String} text Text to paste as the current selection location.
  1586.        */
  1587.       function pasteText(text) {
  1588.         text = editor.dom.encode(text).replace(/\r\n/g, '\n');
  1589.         text = Newlines.convert(text, editor.settings.forced_root_block, editor.settings.forced_root_block_attrs);
  1590.  
  1591.         pasteHtml(text, false);
  1592.       }
  1593.  
  1594.  
  1595.       /**
  1596.        * Gets various content types out of a datatransfer object.
  1597.        *
  1598.        * @param {DataTransfer} dataTransfer Event fired on paste.
  1599.        * @return {Object} Object with mime types and data for those mime types.
  1600.        */
  1601.       function getDataTransferItems(dataTransfer) {
  1602.         var items = {};
  1603.  
  1604.         if (dataTransfer) {
  1605.           // Use old WebKit/IE API
  1606.           if (dataTransfer.getData) {
  1607.             var legacyText = dataTransfer.getData('Text');
  1608.             if (legacyText && legacyText.length > 0) {
  1609.               if (legacyText.indexOf(mceInternalUrlPrefix) == -1) {
  1610.                 items['text/plain'] = legacyText;
  1611.               }
  1612.             }
  1613.           }
  1614.  
  1615.           if (dataTransfer.types) {
  1616.             for (var i = 0; i < dataTransfer.types.length; i++) {
  1617.               var contentType = dataTransfer.types[i];
  1618.               try { // IE11 throws exception when contentType is Files (type is present but data cannot be retrieved via getData())
  1619.                 items[contentType] = dataTransfer.getData(contentType);
  1620.               } catch (ex) {
  1621.                 items[contentType] = ""; // useless in general, but for consistency across browsers
  1622.               }
  1623.             }
  1624.           }
  1625.         }
  1626.  
  1627.         return items;
  1628.       }
  1629.  
  1630.       /**
  1631.        * Gets various content types out of the Clipboard API. It will also get the
  1632.        * plain text using older IE and WebKit API:s.
  1633.        *
  1634.        * @param {ClipboardEvent} clipboardEvent Event fired on paste.
  1635.        * @return {Object} Object with mime types and data for those mime types.
  1636.        */
  1637.       function getClipboardContent(clipboardEvent) {
  1638.         var content = getDataTransferItems(clipboardEvent.clipboardData || editor.getDoc().dataTransfer);
  1639.  
  1640.         // Edge 15 has a broken HTML Clipboard API see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11877517/
  1641.         return Utils.isMsEdge() ? Tools.extend(content, { 'text/html': '' }) : content;
  1642.       }
  1643.  
  1644.       function hasHtmlOrText(content) {
  1645.         return hasContentType(content, 'text/html') || hasContentType(content, 'text/plain');
  1646.       }
  1647.  
  1648.       function getBase64FromUri(uri) {
  1649.         var idx;
  1650.  
  1651.         idx = uri.indexOf(',');
  1652.         if (idx !== -1) {
  1653.           return uri.substr(idx + 1);
  1654.         }
  1655.  
  1656.         return null;
  1657.       }
  1658.  
  1659.       function isValidDataUriImage(settings, imgElm) {
  1660.         return settings.images_dataimg_filter ? settings.images_dataimg_filter(imgElm) : true;
  1661.       }
  1662.  
  1663.       function extractFilename(str) {
  1664.         var m = str.match(/([\s\S]+?)\.(?:jpeg|jpg|png|gif)$/i);
  1665.         return m ? editor.dom.encode(m[1]) : null;
  1666.       }
  1667.  
  1668.       function pasteImage(rng, reader, blob) {
  1669.         if (rng) {
  1670.           editor.selection.setRng(rng);
  1671.           rng = null;
  1672.         }
  1673.  
  1674.         var dataUri = reader.result;
  1675.         var base64 = getBase64FromUri(dataUri);
  1676.         var id = uniqueId();
  1677.         var name = editor.settings.images_reuse_filename && blob.name ? extractFilename(blob.name) : id;
  1678.         var img = new Image();
  1679.  
  1680.         img.src = dataUri;
  1681.  
  1682.         // TODO: Move the bulk of the cache logic to EditorUpload
  1683.         if (isValidDataUriImage(editor.settings, img)) {
  1684.           var blobCache = editor.editorUpload.blobCache;
  1685.           var blobInfo, existingBlobInfo;
  1686.  
  1687.           existingBlobInfo = blobCache.findFirst(function (cachedBlobInfo) {
  1688.             return cachedBlobInfo.base64() === base64;
  1689.           });
  1690.  
  1691.           if (!existingBlobInfo) {
  1692.             blobInfo = blobCache.create(id, blob, base64, name);
  1693.             blobCache.add(blobInfo);
  1694.           } else {
  1695.             blobInfo = existingBlobInfo;
  1696.           }
  1697.  
  1698.           pasteHtml('<img src="' + blobInfo.blobUri() + '">', false);
  1699.         } else {
  1700.           pasteHtml('<img src="' + dataUri + '">', false);
  1701.         }
  1702.       }
  1703.  
  1704.       /**
  1705.        * Checks if the clipboard contains image data if it does it will take that data
  1706.        * and convert it into a data url image and paste that image at the caret location.
  1707.        *
  1708.        * @param  {ClipboardEvent} e Paste/drop event object.
  1709.        * @param  {DOMRange} rng Rng object to move selection to.
  1710.        * @return {Boolean} true/false if the image data was found or not.
  1711.        */
  1712.       function pasteImageData(e, rng) {
  1713.         var dataTransfer = e.clipboardData || e.dataTransfer;
  1714.  
  1715.         function processItems(items) {
  1716.           var i, item, reader, hadImage = false;
  1717.  
  1718.           if (items) {
  1719.             for (i = 0; i < items.length; i++) {
  1720.               item = items[i];
  1721.  
  1722.               if (/^image\/(jpeg|png|gif|bmp)$/.test(item.type)) {
  1723.                 var blob = item.getAsFile ? item.getAsFile() : item;
  1724.  
  1725.                 reader = new FileReader();
  1726.                 reader.onload = pasteImage.bind(null, rng, reader, blob);
  1727.                 reader.readAsDataURL(blob);
  1728.  
  1729.                 e.preventDefault();
  1730.                 hadImage = true;
  1731.               }
  1732.             }
  1733.           }
  1734.  
  1735.           return hadImage;
  1736.         }
  1737.  
  1738.         if (editor.settings.paste_data_images && dataTransfer) {
  1739.           return processItems(dataTransfer.items) || processItems(dataTransfer.files);
  1740.         }
  1741.       }
  1742.  
  1743.       /**
  1744.        * Chrome on Android doesn't support proper clipboard access so we have no choice but to allow the browser default behavior.
  1745.        *
  1746.        * @param {Event} e Paste event object to check if it contains any data.
  1747.        * @return {Boolean} true/false if the clipboard is empty or not.
  1748.        */
  1749.       function isBrokenAndroidClipboardEvent(e) {
  1750.         var clipboardData = e.clipboardData;
  1751.  
  1752.         return navigator.userAgent.indexOf('Android') != -1 && clipboardData && clipboardData.items && clipboardData.items.length === 0;
  1753.       }
  1754.  
  1755.       function getCaretRangeFromEvent(e) {
  1756.         return RangeUtils.getCaretRangeFromPoint(e.clientX, e.clientY, editor.getDoc());
  1757.       }
  1758.  
  1759.       function hasContentType(clipboardContent, mimeType) {
  1760.         return mimeType in clipboardContent && clipboardContent[mimeType].length > 0;
  1761.       }
  1762.  
  1763.       function isKeyboardPasteEvent(e) {
  1764.         return (VK.metaKeyPressed(e) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45);
  1765.       }
  1766.  
  1767.       function registerEventHandlers() {
  1768.         editor.on('keydown', function (e) {
  1769.           function removePasteBinOnKeyUp(e) {
  1770.             // Ctrl+V or Shift+Insert
  1771.             if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) {
  1772.               pasteBin.remove();
  1773.             }
  1774.           }
  1775.  
  1776.           // Ctrl+V or Shift+Insert
  1777.           if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) {
  1778.             keyboardPastePlainTextState = e.shiftKey && e.keyCode == 86;
  1779.  
  1780.             // Edge case on Safari on Mac where it doesn't handle Cmd+Shift+V correctly
  1781.             // it fires the keydown but no paste or keyup so we are left with a paste bin
  1782.             if (keyboardPastePlainTextState && Env.webkit && navigator.userAgent.indexOf('Version/') != -1) {
  1783.               return;
  1784.             }
  1785.  
  1786.             // Prevent undoManager keydown handler from making an undo level with the pastebin in it
  1787.             e.stopImmediatePropagation();
  1788.  
  1789.             keyboardPasteTimeStamp = new Date().getTime();
  1790.  
  1791.             // IE doesn't support Ctrl+Shift+V and it doesn't even produce a paste event
  1792.             // so lets fake a paste event and let IE use the execCommand/dataTransfer methods
  1793.             if (Env.ie && keyboardPastePlainTextState) {
  1794.               e.preventDefault();
  1795.               editor.fire('paste', { ieFake: true });
  1796.               return;
  1797.             }
  1798.  
  1799.             pasteBin.remove();
  1800.             pasteBin.create();
  1801.  
  1802.             // Remove pastebin if we get a keyup and no paste event
  1803.             // For example pasting a file in IE 11 will not produce a paste event
  1804.             editor.once('keyup', removePasteBinOnKeyUp);
  1805.             editor.once('paste', function () {
  1806.               editor.off('keyup', removePasteBinOnKeyUp);
  1807.             });
  1808.           }
  1809.         });
  1810.  
  1811.         function insertClipboardContent(clipboardContent, isKeyBoardPaste, plainTextMode, internal) {
  1812.           var content, isPlainTextHtml;
  1813.  
  1814.           // Grab HTML from Clipboard API or paste bin as a fallback
  1815.           if (hasContentType(clipboardContent, 'text/html')) {
  1816.             content = clipboardContent['text/html'];
  1817.           } else {
  1818.             content = pasteBin.getHtml();
  1819.             internal = internal ? internal : InternalHtml.isMarked(content);
  1820.  
  1821.             // If paste bin is empty try using plain text mode
  1822.             // since that is better than nothing right
  1823.             if (pasteBin.isDefaultContent(content)) {
  1824.               plainTextMode = true;
  1825.             }
  1826.           }
  1827.  
  1828.           content = Utils.trimHtml(content);
  1829.  
  1830.           pasteBin.remove();
  1831.  
  1832.           isPlainTextHtml = (internal === false && Newlines.isPlainText(content));
  1833.  
  1834.           // If we got nothing from clipboard API and pastebin or the content is a plain text (with only
  1835.           // some BRs, Ps or DIVs as newlines) then we fallback to plain/text
  1836.           if (!content.length || isPlainTextHtml) {
  1837.             plainTextMode = true;
  1838.           }
  1839.  
  1840.           // Grab plain text from Clipboard API or convert existing HTML to plain text
  1841.           if (plainTextMode) {
  1842.             // Use plain text contents from Clipboard API unless the HTML contains paragraphs then
  1843.             // we should convert the HTML to plain text since works better when pasting HTML/Word contents as plain text
  1844.             if (hasContentType(clipboardContent, 'text/plain') && isPlainTextHtml) {
  1845.               content = clipboardContent['text/plain'];
  1846.             } else {
  1847.               content = Utils.innerText(content);
  1848.             }
  1849.           }
  1850.  
  1851.           // If the content is the paste bin default HTML then it was
  1852.           // impossible to get the cliboard data out.
  1853.           if (pasteBin.isDefaultContent(content)) {
  1854.             if (!isKeyBoardPaste) {
  1855.               editor.windowManager.alert('Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents.');
  1856.             }
  1857.  
  1858.             return;
  1859.           }
  1860.  
  1861.           if (plainTextMode) {
  1862.             pasteText(content);
  1863.           } else {
  1864.             pasteHtml(content, internal);
  1865.           }
  1866.         }
  1867.  
  1868.         var getLastRng = function () {
  1869.           return pasteBin.getLastRng() || editor.selection.getRng();
  1870.         };
  1871.  
  1872.         editor.on('paste', function (e) {
  1873.           // Getting content from the Clipboard can take some time
  1874.           var clipboardTimer = new Date().getTime();
  1875.           var clipboardContent = getClipboardContent(e);
  1876.           var clipboardDelay = new Date().getTime() - clipboardTimer;
  1877.  
  1878.           var isKeyBoardPaste = (new Date().getTime() - keyboardPasteTimeStamp - clipboardDelay) < 1000;
  1879.           var plainTextMode = self.pasteFormat == "text" || keyboardPastePlainTextState;
  1880.           var internal = hasContentType(clipboardContent, InternalHtml.internalHtmlMime());
  1881.  
  1882.           keyboardPastePlainTextState = false;
  1883.  
  1884.           if (e.isDefaultPrevented() || isBrokenAndroidClipboardEvent(e)) {
  1885.             pasteBin.remove();
  1886.             return;
  1887.           }
  1888.  
  1889.           if (!hasHtmlOrText(clipboardContent) && pasteImageData(e, getLastRng())) {
  1890.             pasteBin.remove();
  1891.             return;
  1892.           }
  1893.  
  1894.           // Not a keyboard paste prevent default paste and try to grab the clipboard contents using different APIs
  1895.           if (!isKeyBoardPaste) {
  1896.             e.preventDefault();
  1897.           }
  1898.  
  1899.           // Try IE only method if paste isn't a keyboard paste
  1900.           if (Env.ie && (!isKeyBoardPaste || e.ieFake) && !hasContentType(clipboardContent, 'text/html')) {
  1901.             pasteBin.create();
  1902.  
  1903.             editor.dom.bind(pasteBin.getEl(), 'paste', function (e) {
  1904.               e.stopPropagation();
  1905.             });
  1906.  
  1907.             editor.getDoc().execCommand('Paste', false, null);
  1908.             clipboardContent["text/html"] = pasteBin.getHtml();
  1909.           }
  1910.  
  1911.           // If clipboard API has HTML then use that directly
  1912.           if (hasContentType(clipboardContent, 'text/html')) {
  1913.             e.preventDefault();
  1914.  
  1915.             // if clipboard lacks internal mime type, inspect html for internal markings
  1916.             if (!internal) {
  1917.               internal = InternalHtml.isMarked(clipboardContent['text/html']);
  1918.             }
  1919.  
  1920.             insertClipboardContent(clipboardContent, isKeyBoardPaste, plainTextMode, internal);
  1921.           } else {
  1922.             Delay.setEditorTimeout(editor, function () {
  1923.               insertClipboardContent(clipboardContent, isKeyBoardPaste, plainTextMode, internal);
  1924.             }, 0);
  1925.           }
  1926.         });
  1927.  
  1928.         editor.on('dragstart dragend', function (e) {
  1929.           draggingInternally = e.type == 'dragstart';
  1930.         });
  1931.  
  1932.         function isPlainTextFileUrl(content) {
  1933.           var plainTextContent = content['text/plain'];
  1934.           return plainTextContent ? plainTextContent.indexOf('file://') === 0 : false;
  1935.         }
  1936.  
  1937.         editor.on('drop', function (e) {
  1938.           var dropContent, rng;
  1939.  
  1940.           rng = getCaretRangeFromEvent(e);
  1941.  
  1942.           if (e.isDefaultPrevented() || draggingInternally) {
  1943.             return;
  1944.           }
  1945.  
  1946.           dropContent = getDataTransferItems(e.dataTransfer);
  1947.           var internal = hasContentType(dropContent, InternalHtml.internalHtmlMime());
  1948.  
  1949.           if ((!hasHtmlOrText(dropContent) || isPlainTextFileUrl(dropContent)) && pasteImageData(e, rng)) {
  1950.             return;
  1951.           }
  1952.  
  1953.           if (rng && editor.settings.paste_filter_drop !== false) {
  1954.             var content = dropContent['mce-internal'] || dropContent['text/html'] || dropContent['text/plain'];
  1955.  
  1956.             if (content) {
  1957.               e.preventDefault();
  1958.  
  1959.               // FF 45 doesn't paint a caret when dragging in text in due to focus call by execCommand
  1960.               Delay.setEditorTimeout(editor, function () {
  1961.                 editor.undoManager.transact(function () {
  1962.                   if (dropContent['mce-internal']) {
  1963.                     editor.execCommand('Delete');
  1964.                   }
  1965.  
  1966.                   editor.selection.setRng(rng);
  1967.  
  1968.                   content = Utils.trimHtml(content);
  1969.  
  1970.                   if (!dropContent['text/html']) {
  1971.                     pasteText(content);
  1972.                   } else {
  1973.                     pasteHtml(content, internal);
  1974.                   }
  1975.                 });
  1976.               });
  1977.             }
  1978.           }
  1979.         });
  1980.  
  1981.         editor.on('dragover dragend', function (e) {
  1982.           if (editor.settings.paste_data_images) {
  1983.             e.preventDefault();
  1984.           }
  1985.         });
  1986.       }
  1987.  
  1988.       self.pasteHtml = pasteHtml;
  1989.       self.pasteText = pasteText;
  1990.       self.pasteImageData = pasteImageData;
  1991.  
  1992.       editor.on('preInit', function () {
  1993.         registerEventHandlers();
  1994.  
  1995.         // Remove all data images from paste for example from Gecko
  1996.         // except internal images like video elements
  1997.         editor.parser.addNodeFilter('img', function (nodes, name, args) {
  1998.           function isPasteInsert(args) {
  1999.             return args.data && args.data.paste === true;
  2000.           }
  2001.  
  2002.           function remove(node) {
  2003.             if (!node.attr('data-mce-object') && src !== Env.transparentSrc) {
  2004.               node.remove();
  2005.             }
  2006.           }
  2007.  
  2008.           function isWebKitFakeUrl(src) {
  2009.             return src.indexOf("webkit-fake-url") === 0;
  2010.           }
  2011.  
  2012.           function isDataUri(src) {
  2013.             return src.indexOf("data:") === 0;
  2014.           }
  2015.  
  2016.           if (!editor.settings.paste_data_images && isPasteInsert(args)) {
  2017.             var i = nodes.length;
  2018.  
  2019.             while (i--) {
  2020.               var src = nodes[i].attributes.map.src;
  2021.  
  2022.               if (!src) {
  2023.                 continue;
  2024.               }
  2025.  
  2026.               // Safari on Mac produces webkit-fake-url see: https://bugs.webkit.org/show_bug.cgi?id=49141
  2027.               if (isWebKitFakeUrl(src)) {
  2028.                 remove(nodes[i]);
  2029.               } else if (!editor.settings.allow_html_data_urls && isDataUri(src)) {
  2030.                 remove(nodes[i]);
  2031.               }
  2032.             }
  2033.           }
  2034.         });
  2035.       });
  2036.     };
  2037.   }
  2038. );
  2039.  
  2040. /**
  2041.  * CutCopy.js
  2042.  *
  2043.  * Released under LGPL License.
  2044.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  2045.  *
  2046.  * License: http://www.tinymce.com/license
  2047.  * Contributing: http://www.tinymce.com/contributing
  2048.  */
  2049.  
  2050. define(
  2051.   'tinymce.plugins.paste.core.CutCopy',
  2052.   [
  2053.     'tinymce.core.Env',
  2054.     'tinymce.plugins.paste.core.InternalHtml',
  2055.     'tinymce.plugins.paste.core.Utils'
  2056.   ],
  2057.   function (Env, InternalHtml, Utils) {
  2058.     var noop = function () {
  2059.     };
  2060.  
  2061.     var hasWorkingClipboardApi = function (clipboardData) {
  2062.       // iOS supports the clipboardData API but it doesn't do anything for cut operations
  2063.       // Edge 15 has a broken HTML Clipboard API see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11780845/
  2064.       return Env.iOS === false && clipboardData !== undefined && typeof clipboardData.setData === 'function' && Utils.isMsEdge() !== true;
  2065.     };
  2066.  
  2067.     var setHtml5Clipboard = function (clipboardData, html, text) {
  2068.       if (hasWorkingClipboardApi(clipboardData)) {
  2069.         try {
  2070.           clipboardData.clearData();
  2071.           clipboardData.setData('text/html', html);
  2072.           clipboardData.setData('text/plain', text);
  2073.           clipboardData.setData(InternalHtml.internalHtmlMime(), html);
  2074.           return true;
  2075.         } catch (e) {
  2076.           return false;
  2077.         }
  2078.       } else {
  2079.         return false;
  2080.       }
  2081.     };
  2082.  
  2083.     var setClipboardData = function (evt, data, fallback, done) {
  2084.       if (setHtml5Clipboard(evt.clipboardData, data.html, data.text)) {
  2085.         evt.preventDefault();
  2086.         done();
  2087.       } else {
  2088.         fallback(data.html, done);
  2089.       }
  2090.     };
  2091.  
  2092.     var fallback = function (editor) {
  2093.       return function (html, done) {
  2094.         var markedHtml = InternalHtml.mark(html);
  2095.         var outer = editor.dom.create('div', {
  2096.           contenteditable: "false",
  2097.           "data-mce-bogus": "all"
  2098.         });
  2099.         var inner = editor.dom.create('div', { contenteditable: "true" }, markedHtml);
  2100.         editor.dom.setStyles(outer, {
  2101.           position: 'fixed',
  2102.           left: '-3000px',
  2103.           width: '1000px',
  2104.           overflow: 'hidden'
  2105.         });
  2106.         outer.appendChild(inner);
  2107.         editor.dom.add(editor.getBody(), outer);
  2108.  
  2109.         var range = editor.selection.getRng();
  2110.         inner.focus();
  2111.  
  2112.         var offscreenRange = editor.dom.createRng();
  2113.         offscreenRange.selectNodeContents(inner);
  2114.         editor.selection.setRng(offscreenRange);
  2115.  
  2116.         setTimeout(function () {
  2117.           outer.parentNode.removeChild(outer);
  2118.           editor.selection.setRng(range);
  2119.           done();
  2120.         }, 0);
  2121.       };
  2122.     };
  2123.  
  2124.     var getData = function (editor) {
  2125.       return {
  2126.         html: editor.selection.getContent({ contextual: true }),
  2127.         text: editor.selection.getContent({ format: 'text' })
  2128.       };
  2129.     };
  2130.  
  2131.     var cut = function (editor) {
  2132.       return function (evt) {
  2133.         if (editor.selection.isCollapsed() === false) {
  2134.           setClipboardData(evt, getData(editor), fallback(editor), function () {
  2135.             // Chrome fails to execCommand from another execCommand with this message:
  2136.             // "We don't execute document.execCommand() this time, because it is called recursively.""
  2137.             setTimeout(function () { // detach
  2138.               editor.execCommand('Delete');
  2139.             }, 0);
  2140.           });
  2141.         }
  2142.       };
  2143.     };
  2144.  
  2145.     var copy = function (editor) {
  2146.       return function (evt) {
  2147.         if (editor.selection.isCollapsed() === false) {
  2148.           setClipboardData(evt, getData(editor), fallback(editor), noop);
  2149.         }
  2150.       };
  2151.     };
  2152.  
  2153.     var register = function (editor) {
  2154.       editor.on('cut', cut(editor));
  2155.       editor.on('copy', copy(editor));
  2156.     };
  2157.  
  2158.     return {
  2159.       register: register
  2160.     };
  2161.   }
  2162. );
  2163. /**
  2164.  * Quirks.js
  2165.  *
  2166.  * Released under LGPL License.
  2167.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  2168.  *
  2169.  * License: http://www.tinymce.com/license
  2170.  * Contributing: http://www.tinymce.com/contributing
  2171.  */
  2172.  
  2173. /**
  2174.  * This class contains various fixes for browsers. These issues can not be feature
  2175.  * detected since we have no direct control over the clipboard. However we might be able
  2176.  * to remove some of these fixes once the browsers gets updated/fixed.
  2177.  *
  2178.  * @class tinymce.pasteplugin.Quirks
  2179.  * @private
  2180.  */
  2181. define(
  2182.   'tinymce.plugins.paste.core.Quirks',
  2183.   [
  2184.     'tinymce.core.Env',
  2185.     'tinymce.core.util.Tools',
  2186.     'tinymce.plugins.paste.core.WordFilter',
  2187.     'tinymce.plugins.paste.core.Utils'
  2188.   ],
  2189.   function (Env, Tools, WordFilter, Utils) {
  2190.     function addPreProcessFilter(editor, filterFunc) {
  2191.       editor.on('PastePreProcess', function (e) {
  2192.         e.content = filterFunc(editor, e.content, e.internal, e.wordContent);
  2193.       });
  2194.     }
  2195.  
  2196.     function addPostProcessFilter(editor, filterFunc) {
  2197.       editor.on('PastePostProcess', function (e) {
  2198.         filterFunc(editor, e.node);
  2199.       });
  2200.     }
  2201.  
  2202.     /**
  2203.      * Removes BR elements after block elements. IE9 has a nasty bug where it puts a BR element after each
  2204.      * block element when pasting from word. This removes those elements.
  2205.      *
  2206.      * This:
  2207.      *  <p>a</p><br><p>b</p>
  2208.      *
  2209.      * Becomes:
  2210.      *  <p>a</p><p>b</p>
  2211.      */
  2212.     function removeExplorerBrElementsAfterBlocks(editor, html) {
  2213.       // Only filter word specific content
  2214.       if (!WordFilter.isWordContent(html)) {
  2215.         return html;
  2216.       }
  2217.  
  2218.       // Produce block regexp based on the block elements in schema
  2219.       var blockElements = [];
  2220.  
  2221.       Tools.each(editor.schema.getBlockElements(), function (block, blockName) {
  2222.         blockElements.push(blockName);
  2223.       });
  2224.  
  2225.       var explorerBlocksRegExp = new RegExp(
  2226.         '(?:<br> [\\s\\r\\n]+|<br>)*(<\\/?(' + blockElements.join('|') + ')[^>]*>)(?:<br> [\\s\\r\\n]+|<br>)*',
  2227.         'g'
  2228.       );
  2229.  
  2230.       // Remove BR:s from: <BLOCK>X</BLOCK><BR>
  2231.       html = Utils.filter(html, [
  2232.         [explorerBlocksRegExp, '$1']
  2233.       ]);
  2234.  
  2235.       // IE9 also adds an extra BR element for each soft-linefeed and it also adds a BR for each word wrap break
  2236.       html = Utils.filter(html, [
  2237.         [/<br><br>/g, '<BR><BR>'], // Replace multiple BR elements with uppercase BR to keep them intact
  2238.         [/<br>/g, ' '],            // Replace single br elements with space since they are word wrap BR:s
  2239.         [/<BR><BR>/g, '<br>']      // Replace back the double brs but into a single BR
  2240.       ]);
  2241.  
  2242.       return html;
  2243.     }
  2244.  
  2245.     /**
  2246.      * WebKit has a nasty bug where the all computed styles gets added to style attributes when copy/pasting contents.
  2247.      * This fix solves that by simply removing the whole style attribute.
  2248.      *
  2249.      * The paste_webkit_styles option can be set to specify what to keep:
  2250.      *  paste_webkit_styles: "none" // Keep no styles
  2251.      *  paste_webkit_styles: "all", // Keep all of them
  2252.      *  paste_webkit_styles: "font-weight color" // Keep specific ones
  2253.      */
  2254.     function removeWebKitStyles(editor, content, internal, isWordHtml) {
  2255.       // WordFilter has already processed styles at this point and internal doesn't need any processing
  2256.       if (isWordHtml || internal) {
  2257.         return content;
  2258.       }
  2259.  
  2260.       // Filter away styles that isn't matching the target node
  2261.       var webKitStyles = editor.settings.paste_webkit_styles;
  2262.  
  2263.       if (editor.settings.paste_remove_styles_if_webkit === false || webKitStyles == "all") {
  2264.         return content;
  2265.       }
  2266.  
  2267.       if (webKitStyles) {
  2268.         webKitStyles = webKitStyles.split(/[, ]/);
  2269.       }
  2270.  
  2271.       // Keep specific styles that doesn't match the current node computed style
  2272.       if (webKitStyles) {
  2273.         var dom = editor.dom, node = editor.selection.getNode();
  2274.  
  2275.         content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, function (all, before, value, after) {
  2276.           var inputStyles = dom.parseStyle(dom.decode(value), 'span');
  2277.           var outputStyles = {};
  2278.  
  2279.           if (webKitStyles === "none") {
  2280.             return before + after;
  2281.           }
  2282.  
  2283.           for (var i = 0; i < webKitStyles.length; i++) {
  2284.             var inputValue = inputStyles[webKitStyles[i]], currentValue = dom.getStyle(node, webKitStyles[i], true);
  2285.  
  2286.             if (/color/.test(webKitStyles[i])) {
  2287.               inputValue = dom.toHex(inputValue);
  2288.               currentValue = dom.toHex(currentValue);
  2289.             }
  2290.  
  2291.             if (currentValue != inputValue) {
  2292.               outputStyles[webKitStyles[i]] = inputValue;
  2293.             }
  2294.           }
  2295.  
  2296.           outputStyles = dom.serializeStyle(outputStyles, 'span');
  2297.           if (outputStyles) {
  2298.             return before + ' style="' + outputStyles + '"' + after;
  2299.           }
  2300.  
  2301.           return before + after;
  2302.         });
  2303.       } else {
  2304.         // Remove all external styles
  2305.         content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, '$1$3');
  2306.       }
  2307.  
  2308.       // Keep internal styles
  2309.       content = content.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi, function (all, before, value, after) {
  2310.         return before + ' style="' + value + '"' + after;
  2311.       });
  2312.  
  2313.       return content;
  2314.     }
  2315.  
  2316.     function removeUnderlineAndFontInAnchor(editor, root) {
  2317.       editor.$('a', root).find('font,u').each(function (i, node) {
  2318.         editor.dom.remove(node, true);
  2319.       });
  2320.     }
  2321.  
  2322.     var setup = function (editor) {
  2323.       if (Env.webkit) {
  2324.         addPreProcessFilter(editor, removeWebKitStyles);
  2325.       }
  2326.  
  2327.       if (Env.ie) {
  2328.         addPreProcessFilter(editor, removeExplorerBrElementsAfterBlocks);
  2329.         addPostProcessFilter(editor, removeUnderlineAndFontInAnchor);
  2330.       }
  2331.     };
  2332.  
  2333.     return {
  2334.       setup: setup
  2335.     };
  2336.   }
  2337. );
  2338. /**
  2339.  * Plugin.js
  2340.  *
  2341.  * Released under LGPL License.
  2342.  * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
  2343.  *
  2344.  * License: http://www.tinymce.com/license
  2345.  * Contributing: http://www.tinymce.com/contributing
  2346.  */
  2347.  
  2348. /**
  2349.  * This class contains all core logic for the paste plugin.
  2350.  *
  2351.  * @class tinymce.paste.Plugin
  2352.  * @private
  2353.  */
  2354. define(
  2355.   'tinymce.plugins.paste.Plugin',
  2356.   [
  2357.     'tinymce.core.PluginManager',
  2358.     'tinymce.plugins.paste.api.Events',
  2359.     'tinymce.plugins.paste.core.Clipboard',
  2360.     'tinymce.plugins.paste.core.CutCopy',
  2361.     'tinymce.plugins.paste.core.Quirks'
  2362.   ],
  2363.   function (PluginManager, Events, Clipboard, CutCopy, Quirks) {
  2364.     var userIsInformed;
  2365.  
  2366.     PluginManager.add('paste', function (editor) {
  2367.       var self = this, clipboard, settings = editor.settings;
  2368.  
  2369.       function isUserInformedAboutPlainText() {
  2370.         return userIsInformed || editor.settings.paste_plaintext_inform === false;
  2371.       }
  2372.  
  2373.       function togglePlainTextPaste() {
  2374.         if (clipboard.pasteFormat == "text") {
  2375.           clipboard.pasteFormat = "html";
  2376.           Events.firePastePlainTextToggle(editor, false);
  2377.         } else {
  2378.           clipboard.pasteFormat = "text";
  2379.           Events.firePastePlainTextToggle(editor, true);
  2380.  
  2381.           if (!isUserInformedAboutPlainText()) {
  2382.             var message = editor.translate('Paste is now in plain text mode. Contents will now ' +
  2383.               'be pasted as plain text until you toggle this option off.');
  2384.  
  2385.             editor.notificationManager.open({
  2386.               text: message,
  2387.               type: 'info'
  2388.             });
  2389.  
  2390.             userIsInformed = true;
  2391.           }
  2392.         }
  2393.  
  2394.         editor.focus();
  2395.       }
  2396.  
  2397.       function stateChange() {
  2398.         var self = this;
  2399.  
  2400.         self.active(clipboard.pasteFormat === 'text');
  2401.  
  2402.         editor.on('PastePlainTextToggle', function (e) {
  2403.           self.active(e.state);
  2404.         });
  2405.       }
  2406.  
  2407.       // draw back if power version is requested and registered
  2408.       if (/(^|[ ,])powerpaste([, ]|$)/.test(settings.plugins) && PluginManager.get('powerpaste')) {
  2409.         /*eslint no-console:0 */
  2410.         if (typeof console !== "undefined" && console.log) {
  2411.           console.log("PowerPaste is incompatible with Paste plugin! Remove 'paste' from the 'plugins' option.");
  2412.         }
  2413.         return;
  2414.       }
  2415.  
  2416.       self.clipboard = clipboard = new Clipboard(editor);
  2417.       self.quirks = Quirks.setup(editor);
  2418.  
  2419.       if (editor.settings.paste_as_text) {
  2420.         self.clipboard.pasteFormat = "text";
  2421.       }
  2422.  
  2423.       if (settings.paste_preprocess) {
  2424.         editor.on('PastePreProcess', function (e) {
  2425.           settings.paste_preprocess.call(self, self, e);
  2426.         });
  2427.       }
  2428.  
  2429.       if (settings.paste_postprocess) {
  2430.         editor.on('PastePostProcess', function (e) {
  2431.           settings.paste_postprocess.call(self, self, e);
  2432.         });
  2433.       }
  2434.  
  2435.       editor.addCommand('mceInsertClipboardContent', function (ui, value) {
  2436.         if (value.content) {
  2437.           self.clipboard.pasteHtml(value.content, value.internal);
  2438.         }
  2439.  
  2440.         if (value.text) {
  2441.           self.clipboard.pasteText(value.text);
  2442.         }
  2443.       });
  2444.  
  2445.       // Block all drag/drop events
  2446.       if (editor.settings.paste_block_drop) {
  2447.         editor.on('dragend dragover draggesture dragdrop drop drag', function (e) {
  2448.           e.preventDefault();
  2449.           e.stopPropagation();
  2450.         });
  2451.       }
  2452.  
  2453.       // Prevent users from dropping data images on Gecko
  2454.       if (!editor.settings.paste_data_images) {
  2455.         editor.on('drop', function (e) {
  2456.           var dataTransfer = e.dataTransfer;
  2457.  
  2458.           if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
  2459.             e.preventDefault();
  2460.           }
  2461.         });
  2462.       }
  2463.  
  2464.       editor.addCommand('mceTogglePlainTextPaste', togglePlainTextPaste);
  2465.  
  2466.       editor.addButton('pastetext', {
  2467.         icon: 'pastetext',
  2468.         tooltip: 'Paste as text',
  2469.         onclick: togglePlainTextPaste,
  2470.         onPostRender: stateChange
  2471.       });
  2472.  
  2473.       editor.addMenuItem('pastetext', {
  2474.         text: 'Paste as text',
  2475.         selectable: true,
  2476.         active: clipboard.pasteFormat,
  2477.         onclick: togglePlainTextPaste,
  2478.         onPostRender: stateChange
  2479.       });
  2480.  
  2481.       CutCopy.register(editor);
  2482.     });
  2483.  
  2484.     return function () { };
  2485.   }
  2486. );
  2487. dem('tinymce.plugins.paste.Plugin')();
  2488. })();
  2489.