From 58fb6502e3550e59afa8799d36dfce61a18f8b1b Mon Sep 17 00:00:00 2001 From: alecpl Date: Tue, 15 Dec 2009 08:48:45 +0000 Subject: - Updated TinyMCE to 3.2.7 --- .../js/tiny_mce/plugins/paste/editor_plugin_src.js | 230 ++++++++++++++++----- 1 file changed, 180 insertions(+), 50 deletions(-) (limited to 'program/js/tiny_mce/plugins/paste/editor_plugin_src.js') diff --git a/program/js/tiny_mce/plugins/paste/editor_plugin_src.js b/program/js/tiny_mce/plugins/paste/editor_plugin_src.js index 14d86a957..8b6cae3d1 100644 --- a/program/js/tiny_mce/plugins/paste/editor_plugin_src.js +++ b/program/js/tiny_mce/plugins/paste/editor_plugin_src.js @@ -1,5 +1,5 @@ /** - * $Id: editor_plugin_src.js 1104 2009-04-22 12:16:47Z spocke $ + * $Id: editor_plugin_src.js 1225 2009-09-07 19:06:19Z spocke $ * * @author Moxiecode * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved. @@ -34,8 +34,8 @@ }); // This function executes the process handlers and inserts the contents - function process(h) { - var dom = ed.dom, o = {content : h}; + function process(o) { + var dom = ed.dom; // Execute pre process handlers t.onPreProcess.dispatch(t, o); @@ -49,7 +49,7 @@ // Serialize content o.content = ed.serializer.serialize(o.node, {getInner : 1}); - // Insert cleaned content. We need to handle insertion of contents containing block elements separatly + // Insert cleaned content. We need to handle insertion of contents containing block elements separately if (/<(p|h[1-6]|ul|ol)/.test(o.content)) t._insertBlockContent(ed, dom, o.content); else @@ -57,8 +57,8 @@ }; // Add command for external usage - ed.addCommand('mceInsertClipboardContent', function(u, v) { - process(v); + ed.addCommand('mceInsertClipboardContent', function(u, o) { + process(o); }); // This function grabs the contents from the clipboard by adding a @@ -71,7 +71,7 @@ return; // Create container to paste into - n = dom.add(body, 'div', {id : '_mcePaste'}, ' '); + n = dom.add(body, 'div', {id : '_mcePaste'}, '\uFEFF'); // If contentEditable mode we need to find out the position of the closest element if (body != ed.getDoc().body) @@ -98,9 +98,18 @@ // Remove container dom.remove(n); + // Check if the contents was changed, if it wasn't then clipboard extraction failed probably due + // to IE security settings so we pass the junk though better than nothing right + if (n.innerHTML === '\uFEFF') { + ed.execCommand('mcePasteWord'); + e.preventDefault(); + return; + } + // Process contents - process(n.innerHTML); + process({content : n.innerHTML}); + // Block the real paste event return tinymce.dom.Event.cancel(e); } else { or = ed.selection.getRng(); @@ -114,26 +123,23 @@ // Wait a while and grab the pasted contents window.setTimeout(function() { - var n = dom.get('_mcePaste'), h; - - // Webkit clones the _mcePaste div for some odd reason so this will ensure that we get the real new div not the old empty one - n.id = '_mceRemoved'; - dom.remove(n); - n = dom.get('_mcePaste') || n; + var h = '', nl = dom.select('div[id=_mcePaste]'); - // Grab the HTML contents - // We need to look for a apple style wrapper on webkit it also adds a div wrapper if you copy/paste the body of the editor - // It's amazing how strange the contentEditable mode works in WebKit - h = (dom.select('> span.Apple-style-span div', n)[0] || dom.select('> span.Apple-style-span', n)[0] || n).innerHTML; + // WebKit will split the div into multiple ones so this will loop through then all and join them to get the whole HTML string + each(nl, function(n) { + h += (dom.select('> span.Apple-style-span div', n)[0] || dom.select('> span.Apple-style-span', n)[0] || n).innerHTML; + }); - // Remove hidden div and restore selection - dom.remove(n); + // Remove the nodes + each(nl, function(n) { + dom.remove(n); + }); // Restore the old selection if (or) sel.setRng(or); - process(h); + process({content : h}); }, 0); } }; @@ -154,6 +160,18 @@ } } + // Block all drag/drop events + if (ed.getParam('paste_block_drop')) { + ed.onInit.add(function() { + ed.dom.bind(ed.getBody(), ['dragend', 'dragover', 'draggesture', 'dragdrop', 'drop', 'drag'], function(e) { + e.preventDefault(); + e.stopPropagation(); + + return false; + }); + }); + } + // Add legacy support t._legacySupport(); }, @@ -169,7 +187,7 @@ }, _preProcess : function(pl, o) { - var h = o.content, process; + var ed = this.editor, h = o.content, process, stripClass; //console.log('Before preprocess:' + o.content); @@ -183,28 +201,79 @@ }); }; - // Process away some basic content - process([ - /^\s*( )+/g, // nbsp entities at the start of contents - /( |]*>)+\s*$/g // nbsp entities at the end of contents - ]); - - // Detect Word content and process it more agressive - if (/(class=\"?Mso|style=\"[^\"]*\bmso\-|w:WordDocument)/.test(h)) { + // Detect Word content and process it more aggressive + if (/(class=\"?Mso|style=\"[^\"]*\bmso\-|w:WordDocument)/.test(h) || o.wordContent) { o.wordContent = true; // Mark the pasted contents as word specific content //console.log('Word contents detected.'); + // Process away some basic content + process([ + /^\s*( )+/g, // nbsp entities at the start of contents + /( |]*>)+\s*$/g // nbsp entities at the end of contents + ]); + + if (ed.getParam('paste_convert_middot_lists', true)) { + process([ + [//gi, '$&__MCE_ITEM__'], // Convert supportLists to a list item marker + [/(]+:\s*symbol[^>]+>)/gi, '$1__MCE_ITEM__'], // Convert symbol spans to list items + [/(]+mso-list:[^>]+>)/gi, '$1__MCE_ITEM__'] // Convert mso-list to item marker + ]); + } + process([ //gi, // Word comments - /<\/?(img|font|meta|link|style|span|div|v:\w+)[^>]*>/gi, // Remove some tags including VML content + /<\/?(img|font|meta|link|style|div|v:\w+)[^>]*>/gi, // Remove some tags including VML content /<\\?\?xml[^>]*>/gi, // XML namespace declarations /<\/?o:[^>]*>/gi, // MS namespaced elements - / (id|name|class|language|type|on\w+|v:\w+)=\"([^\"]*)\"/gi, // on.., class, style and language attributes with quotes - / (id|name|class|language|type|on\w+|v:\w+)=(\w+)/gi, // on.., class, style and language attributes without quotes (IE) + / (id|name|language|type|on\w+|v:\w+)=\"([^\"]*)\"/gi, // on.., class, style and language attributes with quotes + / (id|name|language|type|on\w+|v:\w+)=(\w+)/gi, // on.., class, style and language attributes without quotes (IE) [/<(\/?)s>/gi, '<$1strike>'], // Convert into for line-though /]+>[\s\S]*?<\/script>/gi, // All scripts elements for msoShowComment for example [/ /g, '\u00a0'] // Replace nsbp entites to char since it's easier to handle ]); + + // Remove all spans if no styles is to be retained + if (!ed.getParam('paste_retain_style_properties')) { + process([ + /<\/?(span)[^>]*>/gi + ]); + } + } + + // Allow for class names to be retained if desired; either all, or just the ones from Word + // Note that the paste_strip_class_attributes: 'none, verify_css_classes: true is also a good variation. + stripClass = ed.getParam('paste_strip_class_attributes'); + if (stripClass != 'none') { + // Cleans everything but mceItem... classes + function cleanClasses(str, cls) { + var i, out = ''; + + // Remove all classes + if (stripClass == 'all') + return ''; + + cls = tinymce.explode(cls, ' '); + + for (i = cls.length - 1; i >= 0; i--) { + // Remove Mso classes + if (!/^(Mso)/i.test(cls[i])) + out += (!out ? '' : ' ') + cls[i]; + } + + return ' class="' + out + '"'; + }; + + process([ + [/ class=\"([^\"]*)\"/gi, cleanClasses], // class attributes with quotes + [/ class=(\w+)/gi, cleanClasses] // class attributes without quotes (IE) + ]); + } + + // Remove spans option + if (ed.getParam('paste_remove_spans')) { + process([ + /<\/?(span)[^>]*>/gi + ]); } //console.log('After preprocess:' + h); @@ -216,7 +285,7 @@ * Various post process items. */ _postProcess : function(pl, o) { - var t = this, dom = t.editor.dom; + var t = this, ed = t.editor, dom = ed.dom, styleProps; if (o.wordContent) { // Remove named anchors or TOC links @@ -228,18 +297,55 @@ if (t.editor.getParam('paste_convert_middot_lists', true)) t._convertLists(pl, o); - // Remove all styles + // Process styles + styleProps = ed.getParam('paste_retain_style_properties'); // retained properties + + // If string property then split it + if (tinymce.is(styleProps, 'string')) + styleProps = tinymce.explode(styleProps); + + // Retains some style properties each(dom.select('*', o.node), function(el) { + var newStyle = {}, npc = 0, i, sp, sv; + + // Store a subset of the existing styles + if (styleProps) { + for (i = 0; i < styleProps.length; i++) { + sp = styleProps[i]; + sv = dom.getStyle(el, sp); + + if (sv) { + newStyle[sp] = sv; + npc++; + } + } + } + + // Remove all of the existing styles dom.setAttrib(el, 'style', ''); + + if (styleProps && npc > 0) + dom.setStyles(el, newStyle); // Add back the stored subset of styles + else // Remove empty span tags that do not have class attributes + if (el.nodeName == 'SPAN' && !el.className) + dom.remove(el, true); }); } - if (tinymce.isWebKit) { - // We need to compress the styles on WebKit since if you paste it will become - // Removing the mce_style that contains the real value will force the Serializer engine to compress the styles - each(dom.select('*', o.node), function(el) { + // Remove all style information or only specifically on WebKit to avoid the style bug on that browser + if (ed.getParam("paste_remove_styles") || (ed.getParam("paste_remove_styles_if_webkit") && tinymce.isWebKit)) { + each(dom.select('*[style]', o.node), function(el) { + el.removeAttribute('style'); el.removeAttribute('mce_style'); }); + } else { + if (tinymce.isWebKit) { + // We need to compress the styles on WebKit since if you paste it will become + // Removing the mce_style that contains the real value will force the Serializer engine to compress the styles + each(dom.select('*', o.node), function(el) { + el.removeAttribute('mce_style'); + }); + } } }, @@ -247,9 +353,9 @@ * Converts the most common bullet and number formats in Office into a real semantic UL/LI list. */ _convertLists : function(pl, o) { - var dom = pl.editor.dom, listElm, li, lastMargin = -1, margin, levels = [], lastType; + var dom = pl.editor.dom, listElm, li, lastMargin = -1, margin, levels = [], lastType, html; - // Convert middot lists into real scemantic lists + // Convert middot lists into real semantic lists each(dom.select('p', o.node), function(p) { var sib, val = '', type, html, idx, parents; @@ -257,12 +363,14 @@ for (sib = p.firstChild; sib && sib.nodeType == 3; sib = sib.nextSibling) val += sib.nodeValue; + val = p.innerHTML.replace(/<\/?\w+[^>]*>/gi, '').replace(/ /g, '\u00a0'); + // Detect unordered lists look for bullets - if (/^[\u2022\u00b7\u00a7\u00d8o]\s*\u00a0\u00a0*/.test(val)) + if (/^(__MCE_ITEM__)+[\u2022\u00b7\u00a7\u00d8o]\s*\u00a0*/.test(val)) type = 'ul'; // Detect ordered lists 1., a. or ixv. - if (/^[\s\S]*\w+\.[\s\S]*\u00a0{2,}/.test(val)) + if (/^__MCE_ITEM__\s*\w+\.\s*\u00a0{2,}/.test(val)) type = 'ol'; // Check if node value matches the list pattern: o   @@ -287,11 +395,26 @@ } } + // Remove middot or number spans if they exists + each(dom.select('span', p), function(span) { + var html = span.innerHTML.replace(/<\/?\w+[^>]*>/gi, ''); + + // Remove span with the middot or the number + if (type == 'ul' && /^[\u2022\u00b7\u00a7\u00d8o]/.test(html)) + dom.remove(span); + else if (/^[\s\S]*\w+\.( |\u00a0)*\s*/.test(html)) + dom.remove(span); + }); + + html = p.innerHTML; + + // Remove middot/list items if (type == 'ul') - html = p.innerHTML.replace(/^[\u2022\u00b7\u00a7\u00d8o]\s*( |\u00a0)+\s*/, ''); + html = p.innerHTML.replace(/__MCE_ITEM__/g, '').replace(/^[\u2022\u00b7\u00a7\u00d8o]\s*( |\u00a0)+\s*/, ''); else - html = p.innerHTML.replace(/^[\s\S]*\w+\.( |\u00a0)+\s*/, ''); + html = p.innerHTML.replace(/__MCE_ITEM__/g, '').replace(/^\s*\w+\.( |\u00a0)+\s*/, ''); + // Create li and add paragraph data into the new li li = listElm.appendChild(dom.create('li', 0, html)); dom.remove(p); @@ -300,6 +423,11 @@ } else listElm = lastMargin = 0; // End list element }); + + // Remove any left over makers + html = o.node.innerHTML; + if (html.indexOf('__MCE_ITEM__') != -1) + o.node.innerHTML = html.replace(/__MCE_ITEM__/g, ''); }, /** @@ -326,9 +454,10 @@ // Insert a marker for the caret position this._insert(' ', 1); marker = dom.get('_marker'); - parentBlock = dom.getParent(marker, 'p,h1,h2,h3,h4,h5,h6,ul,ol'); + parentBlock = dom.getParent(marker, 'p,h1,h2,h3,h4,h5,h6,ul,ol,th,td'); - if (parentBlock) { + // If it's a parent block but not a table cell + if (parentBlock && !/TD|TH/.test(parentBlock.nodeName)) { // Split parent block marker = dom.split(parentBlock, marker); @@ -365,7 +494,8 @@ var ed = this.editor; // First delete the contents seems to work better on WebKit - ed.execCommand('Delete'); + if (!ed.selection.isCollapsed()) + ed.getDoc().execCommand('Delete', false, null); // It's better to use the insertHTML method on Gecko since it will combine paragraphs correctly before inserting the contents ed.execCommand(tinymce.isGecko ? 'insertHTML' : 'mceInsertContent', false, h, {skip_undo : skip_undo}); @@ -382,8 +512,8 @@ ed.addCommand(cmd, function() { ed.windowManager.open({ file : t.url + (cmd == 'mcePasteText' ? '/pastetext.htm' : '/pasteword.htm'), - width : 450, - height : 400, + width : parseInt(ed.getParam("paste_dialog_width", "450")), + height : parseInt(ed.getParam("paste_dialog_height", "400")), inline : 1 }); }); -- cgit v1.2.3