From c4b8b7f61d0c455aa77f72b96de4ba02ed03aa41 Mon Sep 17 00:00:00 2001 From: hukl Date: Mon, 16 Nov 2009 22:41:37 +0100 Subject: tinymce update. activated safari and paste plugin to prevent messy markup after copy and paste. hope that helps *sigh* --- public/javascripts/tiny_mce/tiny_mce_src.js | 222 ++++++++++++++++++++-------- 1 file changed, 159 insertions(+), 63 deletions(-) (limited to 'public/javascripts/tiny_mce/tiny_mce_src.js') diff --git a/public/javascripts/tiny_mce/tiny_mce_src.js b/public/javascripts/tiny_mce/tiny_mce_src.js index aa402d5..3fe768b 100644 --- a/public/javascripts/tiny_mce/tiny_mce_src.js +++ b/public/javascripts/tiny_mce/tiny_mce_src.js @@ -1,7 +1,7 @@ var tinymce = { majorVersion : '3', - minorVersion : '2.6', - releaseDate : '2009-08-19', + minorVersion : '2.7', + releaseDate : '2009-09-22', _init : function() { var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base, p, v; @@ -766,7 +766,7 @@ tinymce.create('tinymce.util.Dispatcher', { toAbsolute : function(u, nh) { var u = new tinymce.util.URI(u, {base_uri : this}); - return u.getURI(this.host == u.host ? nh : 0); + return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0); }, toRelPath : function(base, path) { @@ -812,7 +812,7 @@ tinymce.create('tinymce.util.Dispatcher', { }, toAbsPath : function(base, path) { - var i, nb = 0, o = [], tr; + var i, nb = 0, o = [], tr, outPath; // Split paths tr = /\/$/.test(path) ? '/' : ''; @@ -852,9 +852,19 @@ tinymce.create('tinymce.util.Dispatcher', { // If /a/b/c or / if (i <= 0) - return '/' + o.reverse().join('/') + tr; + outPath = o.reverse().join('/'); + else + outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/'); + + // Add front / if it's needed + if (outPath.indexOf('/') !== 0) + outPath = '/' + outPath; + + // Add traling / if it's needed + if (tr && outPath.lastIndexOf('/') !== outPath.length - 1) + outPath += tr; - return '/' + base.slice(0, i).join('/') + '/' + o.reverse().join('/') + tr; + return outPath; }, getURI : function(nh) { @@ -1576,6 +1586,18 @@ tinymce.create('static tinymce.util.XHR', { if (!v) v = e.getAttribute(n, 2); + // Check boolean attribs + if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) { + if (e[t.props[n]] === true && v === '') + return n; + + return v ? n : ''; + } + + // Inner input elements will override attributes on form elements + if (e.nodeName === "FORM" && e.getAttributeNode(n)) + return e.getAttributeNode(n).nodeValue; + if (n === 'style') { v = v || e.style.cssText; @@ -1973,7 +1995,7 @@ tinymce.create('static tinymce.util.XHR', { if (x) { // So if we replace the p elements with divs and mark them and then replace them back to paragraphs // after we use innerHTML we can fix the DOM tree - h = h.replace(/

]+)>|

/g, '

'); + h = h.replace(/

]+)>|

/ig, '

'); h = h.replace(/<\/p>/g, '
'); // Set the new HTML with DIVs @@ -2024,7 +2046,7 @@ tinymce.create('static tinymce.util.XHR', { }, processHTML : function(h) { - var t = this, s = t.settings; + var t = this, s = t.settings, codeBlocks = []; if (!s.process_html) return h; @@ -2070,8 +2092,10 @@ tinymce.create('static tinymce.util.XHR', { }); // Wrap text contents - if (tinymce.trim(text)) - text = ''; + if (tinymce.trim(text)) { + codeBlocks.push(trim(text)); + text = ''; + } return '' + text + ''; }); @@ -2079,8 +2103,10 @@ tinymce.create('static tinymce.util.XHR', { // Wrap style elements h = h.replace(/]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) { // Wrap text contents - if (text) - text = ''; + if (text) { + codeBlocks.push(trim(text)); + text = ''; + } return '' + text + ''; }); @@ -2093,6 +2119,24 @@ tinymce.create('static tinymce.util.XHR', { h = h.replace(//g, ''); + // Remove false bool attributes and force attributes into xhtml style attr="attr" + h = h.replace(/<([\w:]+) [^>]*(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)[^>]*>/gi, function(val) { + function handle(val, name, value) { + // Remove false/0 attribs + if (value === 'false' || value === '0') + return ''; + + return ' ' + name + '="' + name + '"'; + }; + + val = val.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)=[\"]([^\"]+)[\"]/gi, handle); // W3C + val = val.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)=[\']([^\']+)[\']/gi, handle); // W3C + val = val.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)=([^\s\"\'>]+)/gi, handle); // IE + val = val.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)([\s>])/gi, ' $1="$1"$2'); // Force attr="attr" + + return val; + }); + // Process all tags with src, href or style h = h.replace(/<([\w:]+) [^>]*(src|href|style|shape|coords)[^>]*>/gi, function(a, n) { function handle(m, b, c) { @@ -2107,17 +2151,8 @@ tinymce.create('static tinymce.util.XHR', { if (t._isRes(c)) return m; - if (s.hex_colors) { - u = u.replace(/rgb\([^\)]+\)/g, function(v) { - return t.toHex(v); - }); - } - - if (s.url_converter) { - u = u.replace(/url\([\'\"]?([^\)\'\"]+)\)/g, function(x, c) { - return 'url(' + t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n)) + ')'; - }); - } + // Parse and serialize the style to convert for example uppercase styles like "BORDER: 1px" + u = t.encode(t.serializeStyle(t.parseStyle(u))); } else if (b != 'coords' && b != 'shape') { if (s.url_converter) u = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n)); @@ -2131,6 +2166,11 @@ tinymce.create('static tinymce.util.XHR', { return a.replace(/ (src|href|style|coords|shape)=([^\s\"\'>]+)/gi, handle); // IE }); + + // Restore script blocks + h = h.replace(/MCE_SCRIPT:([0-9]+)/g, function(val, idx) { + return codeBlocks[idx]; + }); } return h; @@ -2264,7 +2304,7 @@ tinymce.create('static tinymce.util.XHR', { n = n.nodeName || n; - return /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TR|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(n); + return /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TH|TBODY|TR|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(n); }, replace : function(n, o, k) { @@ -2435,9 +2475,13 @@ tinymce.create('static tinymce.util.XHR', { if (n.nodeName == 'OBJECT') return n.attributes; + // IE doesn't keep the selected attribute if you clone option elements + if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected')) + o.push({specified : 1, nodeName : 'selected'}); + // It's crazy that this is faster in IE but it's because it returns all attributes all the time - n.cloneNode(false).outerHTML.replace(/([a-z0-9\:\-_]+)=/gi, function(a, b) { - o.push({specified : 1, nodeName : b}); + n.cloneNode(false).outerHTML.replace(/<\/?[\w:]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=\w+|>/gi, '').replace(/[\w:]+/gi, function(a) { + o.push({specified : 1, nodeName : a}); }); return o; @@ -2491,16 +2535,29 @@ tinymce.create('static tinymce.util.XHR', { return n.replace(/[ \t\r\n]+| | /g, '') == ''; }; + // Added until Gecko can create real HTML documents using implementation.createHTMLDocument + // this is to future proof it if Gecko decides to implement the error checking for range methods. + function nodeIndex(n) { + var i = 0; + + while (n.previousSibling) { + i++; + n = n.previousSibling; + } + + return i; + }; + if (pe && e) { // Get before chunk - r.setStartBefore(pe); - r.setEndBefore(e); + r.setStart(pe.parentNode, nodeIndex(pe)); + r.setEnd(e.parentNode, nodeIndex(e)); bef = r.extractContents(); // Get after chunk r = t.createRng(); - r.setStartAfter(e); - r.setEndAfter(pe); + r.setStart(e.parentNode, nodeIndex(e) + 1); + r.setEnd(pe.parentNode, nodeIndex(pe) + 1); aft = r.extractContents(); // Insert chunks and remove parent @@ -3513,6 +3570,7 @@ tinymce.create('static tinymce.util.XHR', { if (sc == ec && sc.nodeType == 3) { startPos = getCharPos(sc, so); + ieRng = body.createTextRange(); ieRng.move('character', startPos); ieRng.moveEnd('character', eo - so); ieRng.select(); @@ -4316,6 +4374,8 @@ tinymce.create('static tinymce.util.XHR', { // Handle explorer if (isIE) { + t.tridentSel.destroy(); + // Handle simple if (r = b.rng) { try { @@ -4866,7 +4926,6 @@ tinymce.create('static tinymce.util.XHR', { closed : /^(br|hr|input|meta|img|link|param|area)$/, entity_encoding : 'named', entities : '160,nbsp,161,iexcl,162,cent,163,pound,164,curren,165,yen,166,brvbar,167,sect,168,uml,169,copy,170,ordf,171,laquo,172,not,173,shy,174,reg,175,macr,176,deg,177,plusmn,178,sup2,179,sup3,180,acute,181,micro,182,para,183,middot,184,cedil,185,sup1,186,ordm,187,raquo,188,frac14,189,frac12,190,frac34,191,iquest,192,Agrave,193,Aacute,194,Acirc,195,Atilde,196,Auml,197,Aring,198,AElig,199,Ccedil,200,Egrave,201,Eacute,202,Ecirc,203,Euml,204,Igrave,205,Iacute,206,Icirc,207,Iuml,208,ETH,209,Ntilde,210,Ograve,211,Oacute,212,Ocirc,213,Otilde,214,Ouml,215,times,216,Oslash,217,Ugrave,218,Uacute,219,Ucirc,220,Uuml,221,Yacute,222,THORN,223,szlig,224,agrave,225,aacute,226,acirc,227,atilde,228,auml,229,aring,230,aelig,231,ccedil,232,egrave,233,eacute,234,ecirc,235,euml,236,igrave,237,iacute,238,icirc,239,iuml,240,eth,241,ntilde,242,ograve,243,oacute,244,ocirc,245,otilde,246,ouml,247,divide,248,oslash,249,ugrave,250,uacute,251,ucirc,252,uuml,253,yacute,254,thorn,255,yuml,402,fnof,913,Alpha,914,Beta,915,Gamma,916,Delta,917,Epsilon,918,Zeta,919,Eta,920,Theta,921,Iota,922,Kappa,923,Lambda,924,Mu,925,Nu,926,Xi,927,Omicron,928,Pi,929,Rho,931,Sigma,932,Tau,933,Upsilon,934,Phi,935,Chi,936,Psi,937,Omega,945,alpha,946,beta,947,gamma,948,delta,949,epsilon,950,zeta,951,eta,952,theta,953,iota,954,kappa,955,lambda,956,mu,957,nu,958,xi,959,omicron,960,pi,961,rho,962,sigmaf,963,sigma,964,tau,965,upsilon,966,phi,967,chi,968,psi,969,omega,977,thetasym,978,upsih,982,piv,8226,bull,8230,hellip,8242,prime,8243,Prime,8254,oline,8260,frasl,8472,weierp,8465,image,8476,real,8482,trade,8501,alefsym,8592,larr,8593,uarr,8594,rarr,8595,darr,8596,harr,8629,crarr,8656,lArr,8657,uArr,8658,rArr,8659,dArr,8660,hArr,8704,forall,8706,part,8707,exist,8709,empty,8711,nabla,8712,isin,8713,notin,8715,ni,8719,prod,8721,sum,8722,minus,8727,lowast,8730,radic,8733,prop,8734,infin,8736,ang,8743,and,8744,or,8745,cap,8746,cup,8747,int,8756,there4,8764,sim,8773,cong,8776,asymp,8800,ne,8801,equiv,8804,le,8805,ge,8834,sub,8835,sup,8836,nsub,8838,sube,8839,supe,8853,oplus,8855,otimes,8869,perp,8901,sdot,8968,lceil,8969,rceil,8970,lfloor,8971,rfloor,9001,lang,9002,rang,9674,loz,9824,spades,9827,clubs,9829,hearts,9830,diams,338,OElig,339,oelig,352,Scaron,353,scaron,376,Yuml,710,circ,732,tilde,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm,8211,ndash,8212,mdash,8216,lsquo,8217,rsquo,8218,sbquo,8220,ldquo,8221,rdquo,8222,bdquo,8224,dagger,8225,Dagger,8240,permil,8249,lsaquo,8250,rsaquo,8364,euro', - bool_attrs : /(checked|disabled|readonly|selected|nowrap)/, valid_elements : '*[*]', extended_valid_elements : 0, valid_child_elements : 0, @@ -4950,18 +5009,21 @@ tinymce.create('static tinymce.util.XHR', { if (s.fix_table_elements) { t.onPreProcess.add(function(se, o) { - each(t.dom.select('p table', o.node).reverse(), function(n) { - var parent = t.dom.getParent(n.parentNode, 'table,p'); - - if (parent.nodeName != 'TABLE') { - // IE has a odd bug where tables inside paragraphs sometimes gets wrapped in a BODY and documentFragement element - // This hack seems to resolve that issue. This will normally not happed since your contents should be valid in the first place - if (isIE) - t.dom.setOuterHTML(n, n.outerHTML); - - t.dom.split(parent, n); - } - }); + // Since Opera will crash if you attach the node to a dynamic document we need to brrowser sniff a specific build + // so Opera users with an older version will have to live with less compaible output not much we can do here + if (!tinymce.isOpera || opera.buildNumber() >= 1767) { + each(t.dom.select('p table', o.node).reverse(), function(n) { + var parent = t.dom.getParent(n.parentNode, 'table,p'); + + if (parent.nodeName != 'TABLE') { + try { + t.dom.split(parent, n); + } catch (ex) { + // IE can sometimes fire an unknown runtime error so we just ignore it + } + } + }); + } }); } }, @@ -5277,18 +5339,56 @@ tinymce.create('static tinymce.util.XHR', { }, serialize : function(n, o) { - var h, t = this, doc; + var h, t = this, doc, oldDoc, impl, selected; t._setup(); o = o || {}; o.format = o.format || 'html'; - n = n.cloneNode(true); t.processObj = o; - // Nodes needs to be attached to something in WebKit due to a bug https://bugs.webkit.org/show_bug.cgi?id=25571 - if (tinymce.isWebKit) { - doc = n.ownerDocument.implementation.createHTMLDocument(""); - doc.body.appendChild(doc.importNode(n)); + // IE looses the selected attribute on option elements so we need to store it + // See: http://support.microsoft.com/kb/829907 + if (isIE) { + selected = []; + each(n.getElementsByTagName('option'), function(n) { + var v = t.dom.getAttrib(n, 'selected'); + + selected.push(v ? v : null); + }); + } + + n = n.cloneNode(true); + + // IE looses the selected attribute on option elements so we need to restore it + if (isIE) { + each(n.getElementsByTagName('option'), function(n, i) { + t.dom.setAttrib(n, 'selected', selected[i]); + }); + } + + // Nodes needs to be attached to something in WebKit/Opera + // Older builds of Opera crashes if you attach the node to an document created dynamically + // and since we can't feature detect a crash we need to sniff the acutal build number + // This fix will make DOM ranges and make Sizzle happy! + impl = n.ownerDocument.implementation; + if (impl.createHTMLDocument && (tinymce.isOpera && opera.buildNumber() >= 1767)) { + // Create an empty HTML document + doc = impl.createHTMLDocument(""); + + // Add the element or it's children if it's a body element to the new document + each(n.nodeName == 'BODY' ? n.childNodes : [n], function(node) { + doc.body.appendChild(doc.importNode(node, true)); + }); + + // Grab first child or body element for serialization + if (n.nodeName != 'BODY') + n = doc.body.firstChild; + else + n = doc.body; + + // set the new document in DOMUtils so createElement etc works + oldDoc = t.dom.doc; + t.dom.doc = doc; } t.key = '' + (parseInt(t.key) + 1); @@ -5306,6 +5406,10 @@ tinymce.create('static tinymce.util.XHR', { // Post process o.content = t.writer.getContent(); + // Restore the old document if it was changed + if (oldDoc) + t.dom.doc = oldDoc; + if (!o.no_events) t.onPostProcess.dispatch(t, o); @@ -5679,16 +5783,6 @@ tinymce.create('static tinymce.util.XHR', { v = this.dom.getAttrib(n, na); - // Bool attr - if (this.settings.bool_attrs.test(na) && v) { - v = ('' + v).toLowerCase(); - - if (v === 'false' || v === '0') - return null; - - v = na; - } - switch (na) { case 'rowspan': case 'colspan': @@ -7948,7 +8042,7 @@ var tinyMCE = window.tinyMCE = tinymce.EditorManager; apply_source_formatting : 1, directionality : 'ltr', forced_root_block : 'p', - valid_elements : '@[id|class|style|title|dir