Aleksander Machniak
2012-07-24 0afe27901189a5416dc696eef32e2bc2e5fe3844
program/js/tiny_mce/plugins/paste/editor_plugin_src.js
@@ -27,7 +27,7 @@
         paste_text_sticky : false,
         paste_text_sticky_default : false,
         paste_text_notifyalways : false,
         paste_text_linebreaktype : "p",
         paste_text_linebreaktype : "combined",
         paste_text_replacements : [
            [/\u2026/g, "..."],
            [/[\x93\x94\u201c\u201d]/g, '"'],
@@ -76,7 +76,7 @@
         // This function executes the process handlers and inserts the contents
         // force_rich overrides plain text mode set by user, important for pasting with execCommand
         function process(o, force_rich) {
            var dom = ed.dom, rng, nodes;
            var dom = ed.dom, rng;
            // Execute pre process handlers
            t.onPreProcess.dispatch(t, o);
@@ -89,11 +89,9 @@
            if (tinymce.isGecko) {
               rng = ed.selection.getRng(true);
               if (rng.startContainer == rng.endContainer && rng.startContainer.nodeType == 3) {
                  nodes = dom.select('p,h1,h2,h3,h4,h5,h6,pre', o.node);
                  // Is only one block node and it doesn't contain word stuff
                  if (nodes.length == 1 && o.content.indexOf('__MCE_ITEM__') === -1)
                     dom.remove(nodes.reverse(), true);
                  if (o.node.childNodes.length === 1 && /^(p|h[1-6]|pre)$/i.test(o.node.firstChild.nodeName) && o.content.indexOf('__MCE_ITEM__') === -1)
                     dom.remove(o.node.firstChild, true);
               }
            }
@@ -101,11 +99,11 @@
            t.onPostProcess.dispatch(t, o);
            // Serialize content
            o.content = ed.serializer.serialize(o.node, {getInner : 1});
            o.content = ed.serializer.serialize(o.node, {getInner : 1, forced_root_block : ''});
            // Plain text option active?
            if ((!force_rich) && (ed.pasteAsPlainText)) {
               t._insertPlainText(ed, dom, o.content);
               t._insertPlainText(o.content);
               if (!getParam(ed, "paste_text_sticky")) {
                  ed.pasteAsPlainText = false;
@@ -132,7 +130,7 @@
                  if (getParam(ed, "paste_text_sticky")) {
                     ed.windowManager.alert(ed.translate('paste.plaintext_mode_sticky'));
                  } else {
                     ed.windowManager.alert(ed.translate('paste.plaintext_mode_sticky'));
                     ed.windowManager.alert(ed.translate('paste.plaintext_mode'));
                  }
                  if (!getParam(ed, "paste_text_notifyalways")) {
@@ -157,7 +155,7 @@
               if (ed.pasteAsPlainText) {
                  e.preventDefault();
                  process({content : textContent.replace(/\r?\n/g, '<br />')});
                  process({content : dom.encode(textContent).replace(/\r?\n/g, '<br />')});
                  return;
               }
            }
@@ -172,13 +170,14 @@
            if (body != ed.getDoc().body)
               posY = dom.getPos(ed.selection.getStart(), body).y;
            else
               posY = body.scrollTop + dom.getViewPort().y;
               posY = body.scrollTop + dom.getViewPort(ed.getWin()).y;
            // Styles needs to be applied after the element is added to the document since WebKit will otherwise remove all styles
            // If also needs to be in view on IE or the paste would fail
            dom.setStyles(n, {
               position : 'absolute',
               left : -10000,
               top : posY,
               left : tinymce.isGecko ? -40 : 0, // Need to move it out of site on Gecko since it will othewise display a ghost resize rect for the div
               top : posY - 25,
               width : 1,
               height : 1,
               overflow : 'hidden'
@@ -268,8 +267,9 @@
                           h += n.innerHTML;
                     });
                  } else {
                     // Found WebKit weirdness so force the content into plain text mode
                     h = '<pre>' + dom.encode(textContent).replace(/\r?\n/g, '<br />') + '</pre>';
                     // Found WebKit weirdness so force the content into paragraphs this seems to happen when you paste plain text from Nodepad etc
                     // So this logic will replace double enter with paragraphs and single enter with br so it kind of looks the same
                     h = '<p>' + dom.encode(textContent).replace(/\r?\n\r?\n/g, '</p><p>').replace(/\r?\n/g, '<br />') + '</p>';
                  }
                  // Remove the nodes
@@ -359,8 +359,17 @@
         }
         // IE9 adds BRs before/after block elements when contents is pasted from word or for example another browser
         if (tinymce.isIE && document.documentMode >= 9)
         if (tinymce.isIE && document.documentMode >= 9 && /<(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)/.test(o.content)) {
            // IE9 adds BRs before/after block elements when contents is pasted from word or for example another browser
            process([[/(?:<br>&nbsp;[\s\r\n]+|<br>)*(<\/?(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br>&nbsp;[\s\r\n]+|<br>)*/g, '$1']]);
            // IE9 also adds an extra BR element for each soft-linefeed and it also adds a BR for each word wrap break
            process([
               [/<br><br>/g, '<BR><BR>'], // Replace multiple BR elements with uppercase BR to keep them intact
               [/<br>/g, ' '], // Replace single br elements with space since they are word wrap BR:s
               [/<BR><BR>/g, '<br>'] // Replace back the double brs but into a single BR
            ]);
         }
         // Detect Word content and process it more aggressive
         if (/class="?Mso|style="[^"]*\bmso-|w:WordDocument/i.test(h) || o.wordContent) {
@@ -747,28 +756,24 @@
       * plugin, and requires minimal changes to add the new functionality.
       * Speednet - June 2009
       */
      _insertPlainText : function(ed, dom, h) {
         var i, len, pos, rpos, node, breakElms, before, after,
            w = ed.getWin(),
            d = ed.getDoc(),
            sel = ed.selection,
            is = tinymce.is,
            inArray = tinymce.inArray,
      _insertPlainText : function(content) {
         var ed = this.editor,
            linebr = getParam(ed, "paste_text_linebreaktype"),
            rl = getParam(ed, "paste_text_replacements");
            rl = getParam(ed, "paste_text_replacements"),
            is = tinymce.is;
         function process(items) {
            each(items, function(v) {
               if (v.constructor == RegExp)
                  h = h.replace(v, "");
                  content = content.replace(v, "");
               else
                  h = h.replace(v[0], v[1]);
                  content = content.replace(v[0], v[1]);
            });
         };
         if ((typeof(h) === "string") && (h.length > 0)) {
         if ((typeof(content) === "string") && (content.length > 0)) {
            // If HTML content with line-breaking tags, then remove all cr/lf chars because only tags will break a line
            if (/<(?:p|br|h[1-6]|ul|ol|dl|table|t[rdh]|div|blockquote|fieldset|pre|address|center)[^>]*>/i.test(h)) {
            if (/<(?:p|br|h[1-6]|ul|ol|dl|table|t[rdh]|div|blockquote|fieldset|pre|address|center)[^>]*>/i.test(content)) {
               process([
                  /[\n\r]+/g
               ]);
@@ -785,114 +790,47 @@
               [/<\/t[dh]>\s*<t[dh][^>]*>/gi, "\t"],      // Table cells get tabs betweem them
               /<[a-z!\/?][^>]*>/gi,                  // Delete all remaining tags
               [/&nbsp;/gi, " "],                     // Convert non-break spaces to regular spaces (remember, *plain text*)
               [/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi, "$1"],   // Cool little RegExp deletes whitespace around linebreak chars.
               [/\n{3,}/g, "\n\n"],                     // Max. 2 consecutive linebreaks
               /^\s+|\s+$/g                           // Trim the front & back
               [/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi, "$1"],// Cool little RegExp deletes whitespace around linebreak chars.
               [/\n{3,}/g, "\n\n"]                     // Max. 2 consecutive linebreaks
            ]);
            h = dom.decode(tinymce.html.Entities.encodeRaw(h));
            // Delete any highlighted text before pasting
            if (!sel.isCollapsed()) {
               d.execCommand("Delete", false, null);
            }
            content = ed.dom.decode(tinymce.html.Entities.encodeRaw(content));
            // Perform default or custom replacements
            if (is(rl, "array") || (is(rl, "array"))) {
            if (is(rl, "array")) {
               process(rl);
            }
            else if (is(rl, "string")) {
            } else if (is(rl, "string")) {
               process(new RegExp(rl, "gi"));
            }
            // Treat paragraphs as specified in the config
            if (linebr == "none") {
               // Convert all line breaks to space
               process([
                  [/\n+/g, " "]
               ]);
            }
            else if (linebr == "br") {
            } else if (linebr == "br") {
               // Convert all line breaks to <br />
               process([
                  [/\n/g, "<br />"]
               ]);
            }
            else {
            } else if (linebr == "p") {
               // Convert all line breaks to <p>...</p>
               process([
                  /^\s+|\s+$/g,
                  [/\n+/g, "</p><p>"],
                  [/^(.*<\/p>)(<p>)$/, '<p>$1']
               ]);
            } else {
               // defaults to "combined"
               // Convert single line breaks to <br /> and double line breaks to <p>...</p>
               process([
                  [/\n\n/g, "</p><p>"],
                  [/^(.*<\/p>)(<p>)$/, '<p>$1'],
                  [/\n/g, "<br />"]
               ]);
            }
            // This next piece of code handles the situation where we're pasting more than one paragraph of plain
            // text, and we are pasting the content into the middle of a block node in the editor.  The block
            // node gets split at the selection point into "Para A" and "Para B" (for the purposes of explaining).
            // The first paragraph of the pasted text is appended to "Para A", and the last paragraph of the
            // pasted text is prepended to "Para B".  Any other paragraphs of pasted text are placed between
            // "Para A" and "Para B".  This code solves a host of problems with the original plain text plugin and
            // now handles styles correctly.  (Pasting plain text into a styled paragraph is supposed to make the
            // plain text take the same style as the existing paragraph.)
            if ((pos = h.indexOf("</p><p>")) != -1) {
               rpos = h.lastIndexOf("</p><p>");
               node = sel.getNode();
               breakElms = [];      // Get list of elements to break
               do {
                  if (node.nodeType == 1) {
                     // Don't break tables and break at body
                     if (node.nodeName == "TD" || node.nodeName == "BODY") {
                        break;
                     }
                     breakElms[breakElms.length] = node;
                  }
               } while (node = node.parentNode);
               // Are we in the middle of a block node?
               if (breakElms.length > 0) {
                  before = h.substring(0, pos);
                  after = "";
                  for (i=0, len=breakElms.length; i<len; i++) {
                     before += "</" + breakElms[i].nodeName.toLowerCase() + ">";
                     after += "<" + breakElms[breakElms.length-i-1].nodeName.toLowerCase() + ">";
                  }
                  if (pos == rpos) {
                     h = before + after + h.substring(pos+7);
                  }
                  else {
                     h = before + h.substring(pos+4, rpos+4) + after + h.substring(rpos+7);
                  }
               }
            }
            // Insert content at the caret, plus add a marker for repositioning the caret
            ed.execCommand("mceInsertRawHTML", false, h + '<span id="_plain_text_marker">&nbsp;</span>');
            // Reposition the caret to the marker, which was placed immediately after the inserted content.
            // Needs to be done asynchronously (in window.setTimeout) or else it doesn't work in all browsers.
            // The second part of the code scrolls the content up if the caret is positioned off-screen.
            // This is only necessary for WebKit browsers, but it doesn't hurt to use for all.
            window.setTimeout(function() {
               var marker = dom.get('_plain_text_marker'),
                  elm, vp, y, elmHeight;
               sel.select(marker, false);
               d.execCommand("Delete", false, null);
               marker = null;
               // Get element, position and height
               elm = sel.getStart();
               vp = dom.getViewPort(w);
               y = dom.getPos(elm).y;
               elmHeight = elm.clientHeight;
               // Is element within viewport if not then scroll it into view
               if ((y < vp.y) || (y + elmHeight > vp.y + vp.h)) {
                  d.body.scrollTop = y < vp.y ? y : y - vp.h + 25;
               }
            }, 0);
            ed.execCommand('mceInsertContent', false, content);
         }
      },