+ // Remove all style information or only specifically on WebKit to avoid the style bug on that browser\r
+ if (getParam(ed, "paste_remove_styles") || (getParam(ed, "paste_remove_styles_if_webkit") && tinymce.isWebKit)) {\r
+ each(dom.select('*[style]', o.node), function(el) {\r
+ el.removeAttribute('style');\r
+ el.removeAttribute('data-mce-style');\r
+ });\r
+ } else {\r
+ if (tinymce.isWebKit) {\r
+ // We need to compress the styles on WebKit since if you paste <img border="0" /> it will become <img border="0" style="... lots of junk ..." />\r
+ // Removing the mce_style that contains the real value will force the Serializer engine to compress the styles\r
+ each(dom.select('*', o.node), function(el) {\r
+ el.removeAttribute('data-mce-style');\r
+ });\r
+ }\r
+ }\r
+ },\r
+\r
+ /**\r
+ * Converts the most common bullet and number formats in Office into a real semantic UL/LI list.\r
+ */\r
+ _convertLists : function(pl, o) {\r
+ var dom = pl.editor.dom, listElm, li, lastMargin = -1, margin, levels = [], lastType, html;\r
+\r
+ // Convert middot lists into real semantic lists\r
+ each(dom.select('p', o.node), function(p) {\r
+ var sib, val = '', type, html, idx, parents;\r
+\r
+ // Get text node value at beginning of paragraph\r
+ for (sib = p.firstChild; sib && sib.nodeType == 3; sib = sib.nextSibling)\r
+ val += sib.nodeValue;\r
+\r
+ val = p.innerHTML.replace(/<\/?\w+[^>]*>/gi, '').replace(/ /g, '\u00a0');\r
+\r
+ // Detect unordered lists look for bullets\r
+ if (/^(__MCE_ITEM__)+[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*\u00a0*/.test(val))\r
+ type = 'ul';\r
+\r
+ // Detect ordered lists 1., a. or ixv.\r
+ if (/^__MCE_ITEM__\s*\w+\.\s*\u00a0+/.test(val))\r
+ type = 'ol';\r
+\r
+ // Check if node value matches the list pattern: o \r
+ if (type) {\r
+ margin = parseFloat(p.style.marginLeft || 0);\r
+\r
+ if (margin > lastMargin)\r
+ levels.push(margin);\r
+\r
+ if (!listElm || type != lastType) {\r
+ listElm = dom.create(type);\r
+ dom.insertAfter(listElm, p);\r
+ } else {\r
+ // Nested list element\r
+ if (margin > lastMargin) {\r
+ listElm = li.appendChild(dom.create(type));\r
+ } else if (margin < lastMargin) {\r
+ // Find parent level based on margin value\r
+ idx = tinymce.inArray(levels, margin);\r
+ parents = dom.getParents(listElm.parentNode, type);\r
+ listElm = parents[parents.length - 1 - idx] || listElm;\r
+ }\r
+ }\r
+\r
+ // Remove middot or number spans if they exists\r
+ each(dom.select('span', p), function(span) {\r
+ var html = span.innerHTML.replace(/<\/?\w+[^>]*>/gi, '');\r
+\r
+ // Remove span with the middot or the number\r
+ if (type == 'ul' && /^__MCE_ITEM__[\u2022\u00b7\u00a7\u00d8o\u25CF]/.test(html))\r
+ dom.remove(span);\r
+ else if (/^__MCE_ITEM__[\s\S]*\w+\.( |\u00a0)*\s*/.test(html))\r
+ dom.remove(span);\r
+ });\r
+\r
+ html = p.innerHTML;\r
+\r
+ // Remove middot/list items\r
+ if (type == 'ul')\r
+ html = p.innerHTML.replace(/__MCE_ITEM__/g, '').replace(/^[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*( |\u00a0)+\s*/, '');\r
+ else\r
+ html = p.innerHTML.replace(/__MCE_ITEM__/g, '').replace(/^\s*\w+\.( |\u00a0)+\s*/, '');\r
+\r
+ // Create li and add paragraph data into the new li\r
+ li = listElm.appendChild(dom.create('li', 0, html));\r
+ dom.remove(p);\r
+\r
+ lastMargin = margin;\r
+ lastType = type;\r
+ } else\r
+ listElm = lastMargin = 0; // End list element\r
+ });\r
+\r
+ // Remove any left over makers\r
+ html = o.node.innerHTML;\r
+ if (html.indexOf('__MCE_ITEM__') != -1)\r
+ o.node.innerHTML = html.replace(/__MCE_ITEM__/g, '');\r
+ },\r
+\r
+ /**\r
+ * Inserts the specified contents at the caret position.\r
+ */\r
+ _insert : function(h, skip_undo) {\r
+ var ed = this.editor, r = ed.selection.getRng();\r
+\r
+ // First delete the contents seems to work better on WebKit when the selection spans multiple list items or multiple table cells.\r
+ if (!ed.selection.isCollapsed() && r.startContainer != r.endContainer)\r
+ ed.getDoc().execCommand('Delete', false, null);\r
+\r
+ ed.execCommand('mceInsertContent', false, h, {skip_undo : skip_undo});\r
+ },\r
+\r
+ /**\r
+ * Instead of the old plain text method which tried to re-create a paste operation, the\r
+ * new approach adds a plain text mode toggle switch that changes the behavior of paste.\r
+ * This function is passed the same input that the regular paste plugin produces.\r
+ * It performs additional scrubbing and produces (and inserts) the plain text.\r
+ * This approach leverages all of the great existing functionality in the paste\r
+ * plugin, and requires minimal changes to add the new functionality.\r
+ * Speednet - June 2009\r
+ */\r
+ _insertPlainText : function(content) {\r
+ var ed = this.editor,\r
+ linebr = getParam(ed, "paste_text_linebreaktype"),\r
+ rl = getParam(ed, "paste_text_replacements"),\r
+ is = tinymce.is;\r
+\r
+ function process(items) {\r
+ each(items, function(v) {\r
+ if (v.constructor == RegExp)\r
+ content = content.replace(v, "");\r
+ else\r
+ content = content.replace(v[0], v[1]);\r
+ });\r
+ };\r
+\r
+ if ((typeof(content) === "string") && (content.length > 0)) {\r
+ // If HTML content with line-breaking tags, then remove all cr/lf chars because only tags will break a line\r
+ if (/<(?:p|br|h[1-6]|ul|ol|dl|table|t[rdh]|div|blockquote|fieldset|pre|address|center)[^>]*>/i.test(content)) {\r
+ process([\r
+ /[\n\r]+/g\r
+ ]);\r
+ } else {\r
+ // Otherwise just get rid of carriage returns (only need linefeeds)\r
+ process([\r
+ /\r+/g\r
+ ]);\r
+ }\r
+\r
+ process([\r
+ [/<\/(?:p|h[1-6]|ul|ol|dl|table|div|blockquote|fieldset|pre|address|center)>/gi, "\n\n"], // Block tags get a blank line after them\r
+ [/<br[^>]*>|<\/tr>/gi, "\n"], // Single linebreak for <br /> tags and table rows\r
+ [/<\/t[dh]>\s*<t[dh][^>]*>/gi, "\t"], // Table cells get tabs betweem them\r
+ /<[a-z!\/?][^>]*>/gi, // Delete all remaining tags\r
+ [/ /gi, " "], // Convert non-break spaces to regular spaces (remember, *plain text*)\r
+ [/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi, "$1"],// Cool little RegExp deletes whitespace around linebreak chars.\r
+ [/\n{3,}/g, "\n\n"] // Max. 2 consecutive linebreaks\r
+ ]);\r
+\r
+ content = ed.dom.decode(tinymce.html.Entities.encodeRaw(content));\r
+\r
+ // Perform default or custom replacements\r
+ if (is(rl, "array")) {\r
+ process(rl);\r
+ } else if (is(rl, "string")) {\r
+ process(new RegExp(rl, "gi"));\r
+ }\r
+\r
+ // Treat paragraphs as specified in the config\r
+ if (linebr == "none") {\r
+ process([\r
+ [/\n+/g, " "]\r
+ ]);\r
+ } else if (linebr == "br") {\r
+ process([\r
+ [/\n/g, "<br />"]\r
+ ]);\r
+ } else {\r
+ process([\r
+ [/\n\n/g, "</p><p>"],\r
+ [/^(.*<\/p>)(<p>)$/, '<p>$1'],\r
+ [/\n/g, "<br />"]\r
+ ]);\r
+ }\r
+\r
+ ed.execCommand('mceInsertContent', false, content);\r
+ }\r
+ },\r
+\r
+ /**\r
+ * This method will open the old style paste dialogs. Some users might want the old behavior but still use the new cleanup engine.\r
+ */\r
+ _legacySupport : function() {\r
+ var t = this, ed = t.editor;\r
+\r
+ // Register command(s) for backwards compatibility\r
+ ed.addCommand("mcePasteWord", function() {\r
+ ed.windowManager.open({\r
+ file: t.url + "/pasteword.htm",\r
+ width: parseInt(getParam(ed, "paste_dialog_width")),\r
+ height: parseInt(getParam(ed, "paste_dialog_height")),\r
+ inline: 1\r
+ });\r
+ });\r
+\r
+ if (getParam(ed, "paste_text_use_dialog")) {\r
+ ed.addCommand("mcePasteText", function() {\r
+ ed.windowManager.open({\r
+ file : t.url + "/pastetext.htm",\r
+ width: parseInt(getParam(ed, "paste_dialog_width")),\r
+ height: parseInt(getParam(ed, "paste_dialog_height")),\r
+ inline : 1\r
+ });\r
+ });\r
+ }\r
+\r
+ // Register button for backwards compatibility\r
+ ed.addButton("pasteword", {title : "paste.paste_word_desc", cmd : "mcePasteWord"});\r
+ }\r
+ });\r