From 14ece193ca0ea1d0061d5b3480d97083438a1f64 Mon Sep 17 00:00:00 2001 From: alec Date: Mon, 27 Apr 2009 07:36:26 +0000 Subject: - Updated TinyMCE to 3.2.3 version git-svn-id: https://svn.roundcube.net/trunk@2432 208e9e7b-5314-0410-a742-e7e81cd9613c --- roundcubemail/program/js/tiny_mce/tiny_mce_src.js | 5115 +++++++++++++++------ 1 file changed, 3677 insertions(+), 1438 deletions(-) (limited to 'roundcubemail/program/js/tiny_mce/tiny_mce_src.js') diff --git a/roundcubemail/program/js/tiny_mce/tiny_mce_src.js b/roundcubemail/program/js/tiny_mce/tiny_mce_src.js index eeaab3b1e..483849915 100644 --- a/roundcubemail/program/js/tiny_mce/tiny_mce_src.js +++ b/roundcubemail/program/js/tiny_mce/tiny_mce_src.js @@ -1,10 +1,7 @@ - -/* file:jscripts/tiny_mce/classes/tinymce.js */ - var tinymce = { majorVersion : '3', - minorVersion : '1.0.1', - releaseDate : '2008-06-18', + minorVersion : '2.3', + releaseDate : '2009-04-23', _init : function() { var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base, p, v; @@ -12,11 +9,11 @@ var tinymce = { // Browser checks t.isOpera = w.opera && opera.buildNumber; t.isWebKit = /WebKit/.test(ua); - t.isOldWebKit = t.isWebKit && !w.getSelection().getRangeAt; t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName); t.isIE6 = t.isIE && /MSIE [56]/.test(ua); t.isGecko = !t.isWebKit && /Gecko/.test(ua); t.isMac = ua.indexOf('Mac') != -1; + t.isAir = /adobeair/i.test(ua); // TinyMCE .NET webcontrol might be setting the values for TinyMCE if (w.tinyMCEPreInit) { @@ -87,13 +84,12 @@ var tinymce = { if (!t) return n != 'undefined'; - if (t == 'array' && (o instanceof Array)) + if (t == 'array' && (o.hasOwnProperty && o instanceof Array)) return true; return n == t; }, - // #if !jquery each : function(o, cb, s) { var n, l; @@ -175,7 +171,6 @@ var tinymce = { return (s ? '' + s : '').replace(/^\s*|\s*$/g, ''); }, - // #endif create : function(s, p) { var t = this, sp, ns, cn, scn, c, de = 0; @@ -337,7 +332,7 @@ var tinymce = { w.removeEventListener('unload', unload, false); // Destroy references - t.unloads = o = li = w = unload = null; + t.unloads = o = li = w = unload = 0; // Run garbarge collector on IE if (window.CollectGarbage) @@ -355,19 +350,22 @@ var tinymce = { d.detachEvent('onstop', stop); // Call unload handler - unload(); + if (unload) + unload(); - d = null; + d = 0; }; // Fire unload when the currently loading page is stopped - d.attachEvent('onstop', stop); + if (d) + d.attachEvent('onstop', stop); // Remove onstop listener after a while to prevent the unload function // to execute if the user presses cancel in an onbeforeunload // confirm dialog and then presses the browser stop button window.setTimeout(function() { - d.detachEvent('onstop', stop); + if (d) + d.detachEvent('onstop', stop); }, 0); } }; @@ -426,15 +424,6 @@ window.tinymce = tinymce; // Initialize the API tinymce._init(); - -/* file:jscripts/tiny_mce/classes/adapter/jquery/adapter.js */ - - -/* file:jscripts/tiny_mce/classes/adapter/prototype/adapter.js */ - - -/* file:jscripts/tiny_mce/classes/util/Dispatcher.js */ - tinymce.create('tinymce.util.Dispatcher', { scope : null, listeners : null, @@ -487,9 +476,6 @@ tinymce.create('tinymce.util.Dispatcher', { } }); - -/* file:jscripts/tiny_mce/classes/util/URI.js */ - (function() { var each = tinymce.each; @@ -501,7 +487,7 @@ tinymce.create('tinymce.util.Dispatcher', { s = t.settings = s || {}; // Strange app protocol or local anchor - if (/^(mailto|news|javascript|about):/i.test(u) || /^\s*#/.test(u)) { + if (/^(mailto|tel|news|javascript|about):/i.test(u) || /^\s*#/.test(u)) { t.source = u; return; } @@ -511,7 +497,7 @@ tinymce.create('tinymce.util.Dispatcher', { u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u; // Relative path - if (u.indexOf('://') === -1 && u.indexOf('//') !== 0) + if (u.indexOf(':/') === -1 && u.indexOf('//') !== 0) u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u); // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri) @@ -564,6 +550,9 @@ tinymce.create('tinymce.util.Dispatcher', { toRelative : function(u) { var t = this, o; + if (u === "./") + return u; + u = new tinymce.util.URI(u, {base_uri : t}); // Not on same domain/port or protocol @@ -590,7 +579,7 @@ tinymce.create('tinymce.util.Dispatcher', { }, toRelPath : function(base, path) { - var items, bp = 0, out = '', i; + var items, bp = 0, out = '', i, l; // Split the paths base = base.substring(0, base.lastIndexOf('/')); @@ -598,7 +587,7 @@ tinymce.create('tinymce.util.Dispatcher', { items = path.split('/'); if (base.length >= items.length) { - for (i = 0; i < base.length; i++) { + for (i = 0, l = base.length; i < l; i++) { if (i >= items.length || base[i] != items[i]) { bp = i + 1; break; @@ -607,7 +596,7 @@ tinymce.create('tinymce.util.Dispatcher', { } if (base.length < items.length) { - for (i = 0; i < items.length; i++) { + for (i = 0, l = items.length; i < l; i++) { if (i >= base.length || base[i] != items[i]) { bp = i + 1; break; @@ -618,10 +607,10 @@ tinymce.create('tinymce.util.Dispatcher', { if (bp == 1) return path; - for (i = 0; i < base.length - (bp - 1); i++) + for (i = 0, l = base.length - (bp - 1); i < l; i++) out += "../"; - for (i = bp - 1; i < items.length; i++) { + for (i = bp - 1, l = items.length; i < l; i++) { if (i != bp - 1) out += "/" + items[i]; else @@ -632,9 +621,10 @@ tinymce.create('tinymce.util.Dispatcher', { }, toAbsPath : function(base, path) { - var i, nb = 0, o = []; + var i, nb = 0, o = [], tr; // Split paths + tr = /\/$/.test(path) ? '/' : ''; base = base.split('/'); path = path.split('/'); @@ -671,9 +661,9 @@ tinymce.create('tinymce.util.Dispatcher', { // If /a/b/c or / if (i <= 0) - return '/' + o.reverse().join('/'); + return '/' + o.reverse().join('/') + tr; - return '/' + base.slice(0, i).join('/') + '/' + o.reverse().join('/'); + return '/' + base.slice(0, i).join('/') + '/' + o.reverse().join('/') + tr; }, getURI : function(nh) { @@ -714,9 +704,6 @@ tinymce.create('tinymce.util.Dispatcher', { }); })(); - -/* file:jscripts/tiny_mce/classes/util/Cookie.js */ - (function() { var each = tinymce.each; @@ -788,9 +775,6 @@ tinymce.create('tinymce.util.Dispatcher', { }); })(); - -/* file:jscripts/tiny_mce/classes/util/JSON.js */ - tinymce.create('static tinymce.util.JSON', { serialize : function(o) { var i, v, s = tinymce.util.JSON.serialize, t; @@ -803,7 +787,7 @@ tinymce.create('static tinymce.util.JSON', { if (t == 'string') { v = '\bb\tt\nn\ff\rr\""\'\'\\\\'; - return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'])/g, function(a, b) { + return '"' + o.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g, function(a, b) { i = v.indexOf(b); if (i + 1) @@ -816,7 +800,7 @@ tinymce.create('static tinymce.util.JSON', { } if (t == 'object') { - if (o instanceof Array) { + if (o.hasOwnProperty && o instanceof Array) { for (i=0, v = '['; i 0 ? ',' : '') + s(o[i]); @@ -843,9 +827,6 @@ tinymce.create('static tinymce.util.JSON', { } }); - -/* file:jscripts/tiny_mce/classes/util/XHR.js */ - tinymce.create('static tinymce.util.XHR', { send : function(o) { var x, t, w = window, c = 0; @@ -903,9 +884,6 @@ tinymce.create('static tinymce.util.XHR', { } }); - -/* file:jscripts/tiny_mce/classes/util/JSONRequest.js */ - (function() { var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR; @@ -959,10 +937,7 @@ tinymce.create('static tinymce.util.XHR', { } }); -}()); -/* file:jscripts/tiny_mce/classes/dom/DOMUtils.js */ - -(function() { +}());(function(tinymce) { // Shorten names var each = tinymce.each, is = tinymce.is; var isWebKit = tinymce.isWebKit, isIE = tinymce.isIE; @@ -971,12 +946,21 @@ tinymce.create('static tinymce.util.XHR', { doc : null, root : null, files : null, - listeners : {}, pixelStyles : /^(top|left|bottom|right|width|height|borderWidth)$/, - cache : {}, - idPattern : /^#[\w]+$/, - elmPattern : /^[\w_*]+$/, - elmClassPattern : /^([\w_]*)\.([\w_]+)$/, + props : { + "for" : "htmlFor", + "class" : "className", + className : "className", + checked : "checked", + disabled : "disabled", + maxlength : "maxLength", + readonly : "readOnly", + selected : "selected", + value : "value", + id : "id", + name : "name", + type : "type" + }, DOMUtils : function(d, s) { var t = this; @@ -1030,10 +1014,24 @@ tinymce.create('static tinymce.util.XHR', { }, getRect : function(e) { - var p, t = this, w, h; + var p, t = this, sr; e = t.get(e); p = t.getPos(e); + sr = t.getSize(e); + + return { + x : p.x, + y : p.y, + w : sr.w, + h : sr.h + }; + }, + + getSize : function(e) { + var t = this, w, h; + + e = t.get(e); w = t.getStyle(e, 'width'); h = t.getStyle(e, 'height'); @@ -1046,56 +1044,56 @@ tinymce.create('static tinymce.util.XHR', { h = 0; return { - x : p.x, - y : p.y, w : parseInt(w) || e.offsetWidth || e.clientWidth, h : parseInt(h) || e.offsetHeight || e.clientHeight }; }, + is : function(n, patt) { + return tinymce.dom.Sizzle.matches(patt, n.nodeType ? [n] : n).length > 0; + }, + getParent : function(n, f, r) { - var na, se = this.settings; + return this.getParents(n, f, r, false); + }, - n = this.get(n); + getParents : function(n, f, r, c) { + var t = this, na, se = t.settings, o = []; + + n = t.get(n); + c = c === undefined; if (se.strict_root) - r = r || this.getRoot(); + r = r || t.getRoot(); // Wrap node name as func if (is(f, 'string')) { - na = f.toUpperCase(); - - f = function(n) { - var s = false; - - // Any element - if (n.nodeType == 1 && na === '*') { - s = true; - return false; - } - - each(na.split(','), function(v) { - if (n.nodeType == 1 && ((se.strict && n.nodeName.toUpperCase() == v) || n.nodeName.toUpperCase() == v)) { - s = true; - return false; // Break loop - } - }); + na = f; - return s; - }; + if (f === '*') { + f = function(n) {return n.nodeType == 1;}; + } else { + f = function(n) { + return t.is(n, na); + }; + } } while (n) { - if (n == r) - return null; + if (n == r || !n.nodeType || n.nodeType === 9) + break; - if (f(n)) - return n; + if (!f || f(n)) { + if (c) + o.push(n); + else + return n; + } n = n.parentNode; } - return null; + return c ? o : null; }, get : function(e) { @@ -1113,169 +1111,13 @@ tinymce.create('static tinymce.util.XHR', { return e; }, - // #if !jquery select : function(pa, s) { - var t = this, cs, c, pl, o = [], x, i, l, n; - - s = t.get(s) || t.doc; - - // Look for native support and use that if it's found - if (s.querySelectorAll) { - // Element scope then use temp id - // We need to do this to be compatible with other implementations - // See bug report: http://bugs.webkit.org/show_bug.cgi?id=17461 - if (s != t.doc) { - i = s.id; - s.id = '_mc_tmp'; - pa = '#_mc_tmp ' + pa; - } - - // Select elements - l = tinymce.grep(s.querySelectorAll(pa)); - - // Restore old id - s.id = i; - - return l; - } - - if (t.settings.strict) { - function get(s, n) { - return s.getElementsByTagName(n.toLowerCase()); - }; - } else { - function get(s, n) { - return s.getElementsByTagName(n); - }; - } - - // Simple element pattern. For example: "p" or "*" - if (t.elmPattern.test(pa)) { - x = get(s, pa); - - for (i = 0, l = x.length; i= 0; i--) - cs += '}, ' + (i ? 'n' : 's') + ');'; - - cs += '})'; - - // Compile CSS pattern function - t.cache[pa] = cs = eval(cs); - } - - // Run selector function - cs(isIE ? collectIE : collect, s); - }); - - // Cleanup - each(o, function(n) { - if (isIE) - n.removeAttribute('mce_save'); - else - delete n.mce_save; - }); + var t = this; - return o; + return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []); }, - // #endif add : function(p, n, a, h, c) { var t = this; @@ -1284,19 +1126,7 @@ tinymce.create('static tinymce.util.XHR', { var e, k; e = is(n, 'string') ? t.doc.createElement(n) : n; - - if (a) { - for (k in a) { - if (a.hasOwnProperty(k) && !is(a[k], 'object')) - t.setAttrib(e, k, '' + a[k]); - } - - if (a.style && !is(a.style, 'string')) { - each(a.style, function(v, n) { - t.setStyle(e, n, v); - }); - } - } + t.setAttribs(e, a); if (h) { if (h.nodeType) @@ -1330,8 +1160,10 @@ tinymce.create('static tinymce.util.XHR', { }, remove : function(n, k) { + var t = this; + return this.run(n, function(n) { - var p, g; + var p, g, i; p = n.parentNode; @@ -1339,24 +1171,29 @@ tinymce.create('static tinymce.util.XHR', { return null; if (k) { - each (n.childNodes, function(c) { - p.insertBefore(c.cloneNode(true), n); - }); + for (i = n.childNodes.length - 1; i >= 0; i--) + t.insertAfter(n.childNodes[i], n); + + //each(n.childNodes, function(c) { + // p.insertBefore(c.cloneNode(true), n); + //}); } // Fix IE psuedo leak - /* if (isIE) { + if (t.fixPsuedoLeaks) { p = n.cloneNode(true); - n.outerHTML = ''; + k = 'IELeakGarbageBin'; + g = t.get(k) || t.add(t.doc.body, 'div', {id : k, style : 'display:none'}); + g.appendChild(n); + g.innerHTML = ''; return p; - }*/ + } return p.removeChild(n); }); }, - // #if !jquery setStyle : function(n, na, v) { var t = this; @@ -1458,6 +1295,10 @@ tinymce.create('static tinymce.util.XHR', { setAttrib : function(e, n, v) { var t = this; + // Whats the point + if (!e || !n) + return; + // Strict XML mode if (t.settings.strict) n = n.toLowerCase(); @@ -1467,6 +1308,14 @@ tinymce.create('static tinymce.util.XHR', { switch (n) { case "style": + if (!is(v, 'string')) { + each(v, function(v, n) { + t.setStyle(e, n, v); + }); + + return; + } + // No mce_style for elements with these since they might get resized by the user if (s.keep_values) { if (v && !t._isRes(v)) @@ -1515,7 +1364,6 @@ tinymce.create('static tinymce.util.XHR', { }); }, - // #endif getAttrib : function(e, n, dv) { var v, t = this; @@ -1526,7 +1374,7 @@ tinymce.create('static tinymce.util.XHR', { return false; if (!is(dv)) - dv = ""; + dv = ''; // Try the mce variant for these if (/^(src|href|style|coords|shape)$/.test(n)) { @@ -1536,38 +1384,23 @@ tinymce.create('static tinymce.util.XHR', { return v; } - v = e.getAttribute(n, 2); - - if (!v) { - switch (n) { - case 'class': - v = e.className; - break; - - default: - // Fix for IE crash Bug: #1884376 probably due to invalid DOM structure - if (isIE && n === 'name' && e.nodeName === 'A') { - v = e.name; - break; - } - - v = e.attributes[n]; - v = v && is(v.nodeValue) ? v.nodeValue : v; - } + if (isIE && t.props[n]) { + v = e[t.props[n]]; + v = v && v.nodeValue ? v.nodeValue : v; } - switch (n) { - case 'style': - v = v || e.style.cssText; + if (!v) + v = e.getAttribute(n, 2); - if (v) { - v = t.serializeStyle(t.parseStyle(v)); + if (n === 'style') { + v = v || e.style.cssText; - if (t.settings.keep_values && !t._isRes(v)) - e.setAttribute('mce_style', v); - } + if (v) { + v = t.serializeStyle(t.parseStyle(v)); - break; + if (t.settings.keep_values && !t._isRes(v)) + e.setAttribute('mce_style', v); + } } // Remove Apple and WebKit stuff @@ -1587,7 +1420,18 @@ tinymce.create('static tinymce.util.XHR', { case 'size': // IE returns +0 as default value for size - if (v === '+0') + if (v === '+0' || v === 20 || v === 0) + v = ''; + + break; + + case 'width': + case 'height': + case 'vspace': + case 'checked': + case 'disabled': + case 'readonly': + if (v === 0) v = ''; break; @@ -1599,19 +1443,22 @@ tinymce.create('static tinymce.util.XHR', { break; + case 'maxlength': case 'tabindex': // IE returns default value - if (v === 32768) + if (v === 32768 || v === 2147483647 || v === '32768') v = ''; break; - case 'maxlength': - // IE returns default value - if (v === 2147483647) - v = ''; + case 'multiple': + case 'compact': + case 'noshade': + case 'nowrap': + if (v === 65535) + return n; - break; + return dv; case 'shape': v = v.toLowerCase(); @@ -1624,44 +1471,40 @@ tinymce.create('static tinymce.util.XHR', { } } - return (v && v != '') ? '' + v : dv; + return (v !== undefined && v !== null && v !== '') ? '' + v : dv; }, - getPos : function(n) { + getPos : function(n, ro) { var t = this, x = 0, y = 0, e, d = t.doc, r; n = t.get(n); + ro = ro || d.body; - // Use getBoundingClientRect on IE, Opera has it but it's not perfect - if (n && isIE) { - n = n.getBoundingClientRect(); - e = t.boxModel ? d.documentElement : d.body; - x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border - x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x; - n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset + if (n) { + // Use getBoundingClientRect on IE, Opera has it but it's not perfect + if (isIE && !t.stdMode) { + n = n.getBoundingClientRect(); + e = t.boxModel ? d.documentElement : d.body; + x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border + x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x; + n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset - return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x}; - } + return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x}; + } - r = n; - while (r) { - x += r.offsetLeft || 0; - y += r.offsetTop || 0; - r = r.offsetParent; - } + r = n; + while (r && r != ro && r.nodeType) { + x += r.offsetLeft || 0; + y += r.offsetTop || 0; + r = r.offsetParent; + } - r = n; - while (r) { - // Opera 9.25 bug fix, fixed in 9.50 - if (!/^table-row|inline.*/i.test(t.getStyle(r, "display", 1))) { + r = n.parentNode; + while (r && r != ro && r.nodeType) { x -= r.scrollLeft || 0; y -= r.scrollTop || 0; + r = r.parentNode; } - - r = r.parentNode; - - if (r == d.body) - break; } return {x : x, y : y}; @@ -1803,7 +1646,6 @@ tinymce.create('static tinymce.util.XHR', { }); }, - // #if !jquery addClass : function(e, c) { return this.run(e, function(e) { @@ -1860,10 +1702,9 @@ tinymce.create('static tinymce.util.XHR', { isHidden : function(e) { e = this.get(e); - return e.style.display == 'none' || this.getStyle(e, 'display') == 'none'; + return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none'; }, - // #endif uniqueId : function(p) { return (!p ? 'mce_' : p) + (this.counter++); @@ -1934,7 +1775,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(/

]+)>|

/g, '

'); h = h.replace(/<\/p>/g, '
'); // Set the new HTML with DIVs @@ -1994,8 +1835,10 @@ tinymce.create('static tinymce.util.XHR', { if (tinymce.isGecko) { h = h.replace(/<(\/?)strong>|]+)>/gi, '<$1b$2>'); h = h.replace(/<(\/?)em>|]+)>/gi, '<$1i$2>'); - } else if (isIE) + } else if (isIE) { h = h.replace(/'/g, '''); // IE can't handle apos + h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct + } // Fix some issues h = h.replace(/]+)\/>|/gi, ''); // Force open @@ -2006,9 +1849,10 @@ tinymce.create('static tinymce.util.XHR', { if (/)/g, '\n'); s = s.replace(/^[\r\n]*|[\r\n]*$/g, ''); - s = s.replace(/^\s*(\/\/\s*|\]\]>|-->)\s*$/g, ''); + s = s.replace(/^\s*(\/\/\s*|\]\]>|-->|\]\]-->)\s*$/g, ''); return s; }; @@ -2037,6 +1881,8 @@ tinymce.create('static tinymce.util.XHR', { }); } + h = h.replace(//g, ''); + // 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) { @@ -2047,10 +1893,6 @@ tinymce.create('static tinymce.util.XHR', { return m; if (b == 'style') { - // Why did I need this one? - //if (isIE) - // u = t.serializeStyle(t.parseStyle(u)); - // No mce_style for elements with these since they might get resized by the user if (t._isRes(c)) return m; @@ -2092,7 +1934,7 @@ tinymce.create('static tinymce.util.XHR', { if (!e) return null; - if (isIE) + if (e.outerHTML !== undefined) return e.outerHTML; d = (e.ownerDocument || this.doc).createElement("body"); @@ -2128,15 +1970,23 @@ tinymce.create('static tinymce.util.XHR', { }, decode : function(s) { - var e; + var e, n, v; // Look for entities to decode if (/&[^;]+;/.test(s)) { // Decode the entities using a div element not super efficient but less code e = this.doc.createElement("div"); e.innerHTML = s; + n = e.firstChild; + v = ''; + + if (n) { + do { + v += n.nodeValue; + } while (n.nextSibling); + } - return !e.firstChild ? s : e.firstChild.nodeValue; + return v || s; } return s; @@ -2162,7 +2012,6 @@ tinymce.create('static tinymce.util.XHR', { }) : s; }, - // #if !jquery insertAfter : function(n, r) { var t = this; @@ -2184,7 +2033,6 @@ tinymce.create('static tinymce.util.XHR', { }); }, - // #endif isBlock : function(n) { if (n.nodeType && n.nodeType !== 1) @@ -2192,16 +2040,17 @@ tinymce.create('static tinymce.util.XHR', { n = n.nodeName || n; - return /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|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|TR|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(n); }, - // #if !jquery replace : function(n, o, k) { + var t = this; + if (is(o, 'array')) n = n.cloneNode(true); - return this.run(o, function(o) { + return t.run(o, function(o) { if (k) { each(o.childNodes, function(c) { n.appendChild(c.cloneNode(true)); @@ -2210,17 +2059,37 @@ tinymce.create('static tinymce.util.XHR', { // Fix IE psuedo leak for elements since replacing elements if fairly common // Will break parentNode for some unknown reason - /* if (isIE && o.nodeType === 1) { + if (t.fixPsuedoLeaks && o.nodeType === 1) { o.parentNode.insertBefore(n, o); - o.outerHTML = ''; + t.remove(o); return n; - }*/ + } return o.parentNode.replaceChild(n, o); }); }, - // #endif + + findCommonAncestor : function(a, b) { + var ps = a, pe; + + while (ps) { + pe = b; + + while (pe && ps != pe) + pe = pe.parentNode; + + if (ps == pe) + break; + + ps = ps.parentNode; + } + + if (!ps && a.ownerDocument) + return a.ownerDocument.documentElement; + + return ps; + }, toHex : function(s) { var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s); @@ -2305,7 +2174,7 @@ tinymce.create('static tinymce.util.XHR', { var t = this, o; if (t.doc && typeof(e) === 'string') - e = t.doc.getElementById(e); + e = t.get(e); if (!e) return false; @@ -2365,6 +2234,77 @@ tinymce.create('static tinymce.util.XHR', { tinymce.removeUnload(t.destroy); }, + createRng : function() { + var d = this.doc; + + return d.createRange ? d.createRange() : new tinymce.dom.Range(this); + }, + + split : function(pe, e, re) { + var t = this, r = t.createRng(), bef, aft, pa; + + // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sence + // but we don't want that in our code since it serves no purpose + // For example if this is chopped: + //

text 1CHOPtext 2

+ // would produce: + //

text 1

CHOP

text 2

+ // this function will then trim of empty edges and produce: + //

text 1

CHOP

text 2

+ function trimEdge(n, na) { + n = n[na]; + + if (n && n[na] && n[na].nodeType == 1 && isEmpty(n[na])) + t.remove(n[na]); + }; + + function isEmpty(n) { + n = t.getOuterHTML(n); + n = n.replace(/<(img|hr|table)/gi, '-'); // Keep these convert them to - chars + n = n.replace(/<[^>]+>/g, ''); // Remove all tags + + return n.replace(/[ \t\r\n]+| | /g, '') == ''; + }; + + if (pe && e) { + // Get before chunk + r.setStartBefore(pe); + r.setEndBefore(e); + bef = r.extractContents(); + + // Get after chunk + r = t.createRng(); + r.setStartAfter(e); + r.setEndAfter(pe); + aft = r.extractContents(); + + // Insert chunks and remove parent + pa = pe.parentNode; + + // Remove right side edge of the before contents + trimEdge(bef, 'lastChild'); + + if (!isEmpty(bef)) + pa.insertBefore(bef, pe); + + if (re) + pa.replaceChild(re, e); + else + pa.insertBefore(e, pe); + + // Remove left site edge of the after contents + trimEdge(aft, 'firstChild'); + + if (!isEmpty(aft)) + pa.insertBefore(aft, pe); + + t.remove(pe); + + return re || e; + } + }, + + _isRes : function(c) { // Is live resizble element return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c); @@ -2404,47 +2344,1978 @@ tinymce.create('static tinymce.util.XHR', { // Setup page DOM tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0}); -})(); +})(tinymce); +(function(ns) { + // Traverse constants + var EXTRACT = 0, CLONE = 1, DELETE = 2, extend = tinymce.extend; -/* file:jscripts/tiny_mce/classes/dom/Event.js */ + function indexOf(child, parent) { + var i, node; -(function() { - // Shorten names - var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event; + if (child.parentNode != parent) + return -1; - tinymce.create('static tinymce.dom.Event', { - inits : [], - events : [], + for (node = parent.firstChild, i = 0; node != child; node = node.nextSibling) + i++; - // #if !jquery + return i; + }; - add : function(o, n, f, s) { - var cb, t = this, el = t.events, r; + function nodeIndex(n) { + var i = 0; - // Handle array - if (o && o instanceof Array) { - r = []; + while (n.previousSibling) { + i++; + n = n.previousSibling; + } - each(o, function(o) { - o = DOM.get(o); - r.push(t.add(o, n, f, s)); - }); + return i; + }; - return r; - } + function getSelectedNode(container, offset) { + var child; - o = DOM.get(o); + if (container.nodeType == 3 /* TEXT_NODE */) + return container; - if (!o) - return; + if (offset < 0) + return container; - // Setup event callback - cb = function(e) { - e = e || window.event; + child = container.firstChild; + while (child != null && offset > 0) { + --offset; + child = child.nextSibling; + } - // Patch in target in IE it's W3C valid - if (e && !e.target && isIE) - e.target = e.srcElement; + if (child != null) + return child; + + return container; + }; + + // Range constructor + function Range(dom) { + var d = dom.doc; + + extend(this, { + dom : dom, + + // Inital states + startContainer : d, + startOffset : 0, + endContainer : d, + endOffset : 0, + collapsed : true, + commonAncestorContainer : d, + + // Range constants + START_TO_START : 0, + START_TO_END : 1, + END_TO_END : 2, + END_TO_START : 3 + }); + }; + + // Add range methods + extend(Range.prototype, { + setStart : function(n, o) { + this._setEndPoint(true, n, o); + }, + + setEnd : function(n, o) { + this._setEndPoint(false, n, o); + }, + + setStartBefore : function(n) { + this.setStart(n.parentNode, nodeIndex(n)); + }, + + setStartAfter : function(n) { + this.setStart(n.parentNode, nodeIndex(n) + 1); + }, + + setEndBefore : function(n) { + this.setEnd(n.parentNode, nodeIndex(n)); + }, + + setEndAfter : function(n) { + this.setEnd(n.parentNode, nodeIndex(n) + 1); + }, + + collapse : function(ts) { + var t = this; + + if (ts) { + t.endContainer = t.startContainer; + t.endOffset = t.startOffset; + } else { + t.startContainer = t.endContainer; + t.startOffset = t.endOffset; + } + + t.collapsed = true; + }, + + selectNode : function(n) { + this.setStartBefore(n); + this.setEndAfter(n); + }, + + selectNodeContents : function(n) { + this.setStart(n, 0); + this.setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length); + }, + + compareBoundaryPoints : function(h, r) { + var t = this, sc = t.startContainer, so = t.startOffset, ec = t.endContainer, eo = t.endOffset; + + // Check START_TO_START + if (h === 0) + return t._compareBoundaryPoints(sc, so, sc, so); + + // Check START_TO_END + if (h === 1) + return t._compareBoundaryPoints(sc, so, ec, eo); + + // Check END_TO_END + if (h === 2) + return t._compareBoundaryPoints(ec, eo, ec, eo); + + // Check END_TO_START + if (h === 3) + return t._compareBoundaryPoints(ec, eo, sc, so); + }, + + deleteContents : function() { + this._traverse(DELETE); + }, + + extractContents : function() { + return this._traverse(EXTRACT); + }, + + cloneContents : function() { + return this._traverse(CLONE); + }, + + insertNode : function(n) { + var t = this, nn, o; + + // Node is TEXT_NODE or CDATA + if (n.nodeType === 3 || n.nodeType === 4) { + nn = t.startContainer.splitText(t.startOffset); + t.startContainer.parentNode.insertBefore(n, nn); + } else { + // Insert element node + if (t.startContainer.childNodes.length > 0) + o = t.startContainer.childNodes[t.startOffset]; + + t.startContainer.insertBefore(n, o); + } + }, + + surroundContents : function(n) { + var t = this, f = t.extractContents(); + + t.insertNode(n); + n.appendChild(f); + t.selectNode(n); + }, + + cloneRange : function() { + var t = this; + + return extend(new Range(t.dom), { + startContainer : t.startContainer, + startOffset : t.startOffset, + endContainer : t.endContainer, + endOffset : t.endOffset, + collapsed : t.collapsed, + commonAncestorContainer : t.commonAncestorContainer + }); + }, + +/* + detach : function() { + // Not implemented + }, +*/ + // Internal methods + + _isCollapsed : function() { + return (this.startContainer == this.endContainer && this.startOffset == this.endOffset); + }, + + _compareBoundaryPoints : function (containerA, offsetA, containerB, offsetB) { + var c, offsetC, n, cmnRoot, childA, childB; + + // In the first case the boundary-points have the same container. A is before B + // if its offset is less than the offset of B, A is equal to B if its offset is + // equal to the offset of B, and A is after B if its offset is greater than the + // offset of B. + if (containerA == containerB) { + if (offsetA == offsetB) { + return 0; // equal + } else if (offsetA < offsetB) { + return -1; // before + } else { + return 1; // after + } + } + + // In the second case a child node C of the container of A is an ancestor + // container of B. In this case, A is before B if the offset of A is less than or + // equal to the index of the child node C and A is after B otherwise. + c = containerB; + while (c && c.parentNode != containerA) { + c = c.parentNode; + } + if (c) { + offsetC = 0; + n = containerA.firstChild; + + while (n != c && offsetC < offsetA) { + offsetC++; + n = n.nextSibling; + } + + if (offsetA <= offsetC) { + return -1; // before + } else { + return 1; // after + } + } + + // In the third case a child node C of the container of B is an ancestor container + // of A. In this case, A is before B if the index of the child node C is less than + // the offset of B and A is after B otherwise. + c = containerA; + while (c && c.parentNode != containerB) { + c = c.parentNode; + } + + if (c) { + offsetC = 0; + n = containerB.firstChild; + + while (n != c && offsetC < offsetB) { + offsetC++; + n = n.nextSibling; + } + + if (offsetC < offsetB) { + return -1; // before + } else { + return 1; // after + } + } + + // In the fourth case, none of three other cases hold: the containers of A and B + // are siblings or descendants of sibling nodes. In this case, A is before B if + // the container of A is before the container of B in a pre-order traversal of the + // Ranges' context tree and A is after B otherwise. + cmnRoot = this.dom.findCommonAncestor(containerA, containerB); + childA = containerA; + + while (childA && childA.parentNode != cmnRoot) { + childA = childA.parentNode; + } + + if (!childA) { + childA = cmnRoot; + } + + childB = containerB; + while (childB && childB.parentNode != cmnRoot) { + childB = childB.parentNode; + } + + if (!childB) { + childB = cmnRoot; + } + + if (childA == childB) { + return 0; // equal + } + + n = cmnRoot.firstChild; + while (n) { + if (n == childA) { + return -1; // before + } + + if (n == childB) { + return 1; // after + } + + n = n.nextSibling; + } + }, + + _setEndPoint : function(st, n, o) { + var t = this, ec, sc; + + if (st) { + t.startContainer = n; + t.startOffset = o; + } else { + t.endContainer = n; + t.endOffset = o; + } + + // If one boundary-point of a Range is set to have a root container + // other than the current one for the Range, the Range is collapsed to + // the new position. This enforces the restriction that both boundary- + // points of a Range must have the same root container. + ec = t.endContainer; + while (ec.parentNode) + ec = ec.parentNode; + + sc = t.startContainer; + while (sc.parentNode) + sc = sc.parentNode; + + if (sc != ec) { + t.collapse(st); + } else { + // The start position of a Range is guaranteed to never be after the + // end position. To enforce this restriction, if the start is set to + // be at a position after the end, the Range is collapsed to that + // position. + if (t._compareBoundaryPoints(t.startContainer, t.startOffset, t.endContainer, t.endOffset) > 0) + t.collapse(st); + } + + t.collapsed = t._isCollapsed(); + t.commonAncestorContainer = t.dom.findCommonAncestor(t.startContainer, t.endContainer); + }, + + // This code is heavily "inspired" by the Apache Xerces implementation. I hope they don't mind. :) + + _traverse : function(how) { + var t = this, c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep; + + if (t.startContainer == t.endContainer) + return t._traverseSameContainer(how); + + for (c = t.endContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) { + if (p == t.startContainer) + return t._traverseCommonStartContainer(c, how); + + ++endContainerDepth; + } + + for (c = t.startContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) { + if (p == t.endContainer) + return t._traverseCommonEndContainer(c, how); + + ++startContainerDepth; + } + + depthDiff = startContainerDepth - endContainerDepth; + + startNode = t.startContainer; + while (depthDiff > 0) { + startNode = startNode.parentNode; + depthDiff--; + } + + endNode = t.endContainer; + while (depthDiff < 0) { + endNode = endNode.parentNode; + depthDiff++; + } + + // ascend the ancestor hierarchy until we have a common parent. + for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) { + startNode = sp; + endNode = ep; + } + + return t._traverseCommonAncestors(startNode, endNode, how); + }, + + _traverseSameContainer : function(how) { + var t = this, frag, s, sub, n, cnt, sibling, xferNode; + + if (how != DELETE) + frag = t.dom.doc.createDocumentFragment(); + + // If selection is empty, just return the fragment + if (t.startOffset == t.endOffset) + return frag; + + // Text node needs special case handling + if (t.startContainer.nodeType == 3 /* TEXT_NODE */) { + // get the substring + s = t.startContainer.nodeValue; + sub = s.substring(t.startOffset, t.endOffset); + + // set the original text node to its new value + if (how != CLONE) { + t.startContainer.deleteData(t.startOffset, t.endOffset - t.startOffset); + + // Nothing is partially selected, so collapse to start point + t.collapse(true); + } + + if (how == DELETE) + return null; + + frag.appendChild(t.dom.doc.createTextNode(sub)); + return frag; + } + + // Copy nodes between the start/end offsets. + n = getSelectedNode(t.startContainer, t.startOffset); + cnt = t.endOffset - t.startOffset; + + while (cnt > 0) { + sibling = n.nextSibling; + xferNode = t._traverseFullySelected(n, how); + + if (frag) + frag.appendChild( xferNode ); + + --cnt; + n = sibling; + } + + // Nothing is partially selected, so collapse to start point + if (how != CLONE) + t.collapse(true); + + return frag; + }, + + _traverseCommonStartContainer : function(endAncestor, how) { + var t = this, frag, n, endIdx, cnt, sibling, xferNode; + + if (how != DELETE) + frag = t.dom.doc.createDocumentFragment(); + + n = t._traverseRightBoundary(endAncestor, how); + + if (frag) + frag.appendChild(n); + + endIdx = indexOf(endAncestor, t.startContainer); + cnt = endIdx - t.startOffset; + + if (cnt <= 0) { + // Collapse to just before the endAncestor, which + // is partially selected. + if (how != CLONE) { + t.setEndBefore(endAncestor); + t.collapse(false); + } + + return frag; + } + + n = endAncestor.previousSibling; + while (cnt > 0) { + sibling = n.previousSibling; + xferNode = t._traverseFullySelected(n, how); + + if (frag) + frag.insertBefore(xferNode, frag.firstChild); + + --cnt; + n = sibling; + } + + // Collapse to just before the endAncestor, which + // is partially selected. + if (how != CLONE) { + t.setEndBefore(endAncestor); + t.collapse(false); + } + + return frag; + }, + + _traverseCommonEndContainer : function(startAncestor, how) { + var t = this, frag, startIdx, n, cnt, sibling, xferNode; + + if (how != DELETE) + frag = t.dom.doc.createDocumentFragment(); + + n = t._traverseLeftBoundary(startAncestor, how); + if (frag) + frag.appendChild(n); + + startIdx = indexOf(startAncestor, t.endContainer); + ++startIdx; // Because we already traversed it.... + + cnt = t.endOffset - startIdx; + n = startAncestor.nextSibling; + while (cnt > 0) { + sibling = n.nextSibling; + xferNode = t._traverseFullySelected(n, how); + + if (frag) + frag.appendChild(xferNode); + + --cnt; + n = sibling; + } + + if (how != CLONE) { + t.setStartAfter(startAncestor); + t.collapse(true); + } + + return frag; + }, + + _traverseCommonAncestors : function(startAncestor, endAncestor, how) { + var t = this, n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling; + + if (how != DELETE) + frag = t.dom.doc.createDocumentFragment(); + + n = t._traverseLeftBoundary(startAncestor, how); + if (frag) + frag.appendChild(n); + + commonParent = startAncestor.parentNode; + startOffset = indexOf(startAncestor, commonParent); + endOffset = indexOf(endAncestor, commonParent); + ++startOffset; + + cnt = endOffset - startOffset; + sibling = startAncestor.nextSibling; + + while (cnt > 0) { + nextSibling = sibling.nextSibling; + n = t._traverseFullySelected(sibling, how); + + if (frag) + frag.appendChild(n); + + sibling = nextSibling; + --cnt; + } + + n = t._traverseRightBoundary(endAncestor, how); + + if (frag) + frag.appendChild(n); + + if (how != CLONE) { + t.setStartAfter(startAncestor); + t.collapse(true); + } + + return frag; + }, + + _traverseRightBoundary : function(root, how) { + var t = this, next = getSelectedNode(t.endContainer, t.endOffset - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent; + var isFullySelected = next != t.endContainer; + + if (next == root) + return t._traverseNode(next, isFullySelected, false, how); + + parent = next.parentNode; + clonedParent = t._traverseNode(parent, false, false, how); + + while (parent != null) { + while (next != null) { + prevSibling = next.previousSibling; + clonedChild = t._traverseNode(next, isFullySelected, false, how); + + if (how != DELETE) + clonedParent.insertBefore(clonedChild, clonedParent.firstChild); + + isFullySelected = true; + next = prevSibling; + } + + if (parent == root) + return clonedParent; + + next = parent.previousSibling; + parent = parent.parentNode; + + clonedGrandParent = t._traverseNode(parent, false, false, how); + + if (how != DELETE) + clonedGrandParent.appendChild(clonedParent); + + clonedParent = clonedGrandParent; + } + + // should never occur + return null; + }, + + _traverseLeftBoundary : function(root, how) { + var t = this, next = getSelectedNode(t.startContainer, t.startOffset); + var isFullySelected = next != t.startContainer, parent, clonedParent, nextSibling, clonedChild, clonedGrandParent; + + if (next == root) + return t._traverseNode(next, isFullySelected, true, how); + + parent = next.parentNode; + clonedParent = t._traverseNode(parent, false, true, how); + + while (parent != null) { + while (next != null) { + nextSibling = next.nextSibling; + clonedChild = t._traverseNode(next, isFullySelected, true, how); + + if (how != DELETE) + clonedParent.appendChild(clonedChild); + + isFullySelected = true; + next = nextSibling; + } + + if (parent == root) + return clonedParent; + + next = parent.nextSibling; + parent = parent.parentNode; + + clonedGrandParent = t._traverseNode(parent, false, true, how); + + if (how != DELETE) + clonedGrandParent.appendChild(clonedParent); + + clonedParent = clonedGrandParent; + } + + // should never occur + return null; + }, + + _traverseNode : function(n, isFullySelected, isLeft, how) { + var t = this, txtValue, newNodeValue, oldNodeValue, offset, newNode; + + if (isFullySelected) + return t._traverseFullySelected(n, how); + + if (n.nodeType == 3 /* TEXT_NODE */) { + txtValue = n.nodeValue; + + if (isLeft) { + offset = t.startOffset; + newNodeValue = txtValue.substring(offset); + oldNodeValue = txtValue.substring(0, offset); + } else { + offset = t.endOffset; + newNodeValue = txtValue.substring(0, offset); + oldNodeValue = txtValue.substring(offset); + } + + if (how != CLONE) + n.nodeValue = oldNodeValue; + + if (how == DELETE) + return null; + + newNode = n.cloneNode(false); + newNode.nodeValue = newNodeValue; + + return newNode; + } + + if (how == DELETE) + return null; + + return n.cloneNode(false); + }, + + _traverseFullySelected : function(n, how) { + var t = this; + + if (how != DELETE) + return how == CLONE ? n.cloneNode(true) : n; + + n.parentNode.removeChild(n); + return null; + } + }); + + ns.Range = Range; +})(tinymce.dom); +(function() { + function Selection(selection) { + var t = this, invisibleChar = '\uFEFF', range, lastIERng; + + function compareRanges(rng1, rng2) { + if (rng1 && rng2) { + // Both are control ranges and the selected element matches + if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0)) + return 1; + + // Both are text ranges and the range matches + if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) + return 1; + } + + return 0; + }; + + function getRange() { + var dom = selection.dom, ieRange = selection.getRng(), domRange = dom.createRng(), startPos, endPos, element, sc, ec, collapsed; + + function findIndex(element) { + var nl = element.parentNode.childNodes, i; + + for (i = nl.length - 1; i >= 0; i--) { + if (nl[i] == element) + return i; + } + + return -1; + }; + + function findEndPoint(start) { + var rng = ieRange.duplicate(), parent, i, nl, n, offset = 0, index = 0, pos, tmpRng; + + // Insert marker character + rng.collapse(start); + parent = rng.parentElement(); + rng.pasteHTML(invisibleChar); // Needs to be a pasteHTML instead of .text = since IE has a bug with nodeValue + + // Find marker character + nl = parent.childNodes; + for (i = 0; i < nl.length; i++) { + n = nl[i]; + + // Calculate node index excluding text node fragmentation + if (i > 0 && (n.nodeType !== 3 || nl[i - 1].nodeType !== 3)) + index++; + + // If text node then calculate offset + if (n.nodeType === 3) { + // Look for marker + pos = n.nodeValue.indexOf(invisibleChar); + if (pos !== -1) { + offset += pos; + break; + } + + offset += n.nodeValue.length; + } else + offset = 0; + } + + // Remove marker character + rng.moveStart('character', -1); + rng.text = ''; + + return {index : index, offset : offset, parent : parent}; + }; + + // If selection is outside the current document just return an empty range + element = ieRange.item ? ieRange.item(0) : ieRange.parentElement(); + if (element.ownerDocument != dom.doc) + return domRange; + + // Handle control selection or text selection of a image + if (ieRange.item || !element.hasChildNodes()) { + domRange.setStart(element.parentNode, findIndex(element)); + domRange.setEnd(domRange.startContainer, domRange.startOffset + 1); + + return domRange; + } + + // Check collapsed state + collapsed = selection.isCollapsed(); + + // Find start and end pos index and offset + startPos = findEndPoint(true); + endPos = findEndPoint(false); + + // Normalize the elements to avoid fragmented dom + startPos.parent.normalize(); + endPos.parent.normalize(); + + // Set start container and offset + sc = startPos.parent.childNodes[Math.min(startPos.index, startPos.parent.childNodes.length - 1)]; + + if (sc.nodeType != 3) + domRange.setStart(startPos.parent, startPos.index); + else + domRange.setStart(startPos.parent.childNodes[startPos.index], startPos.offset); + + // Set end container and offset + ec = endPos.parent.childNodes[Math.min(endPos.index, endPos.parent.childNodes.length - 1)]; + + if (ec.nodeType != 3) { + if (!collapsed) + endPos.index++; + + domRange.setEnd(endPos.parent, endPos.index); + } else + domRange.setEnd(endPos.parent.childNodes[endPos.index], endPos.offset); + + // If not collapsed then make sure offsets are valid + if (!collapsed) { + sc = domRange.startContainer; + if (sc.nodeType == 1) + domRange.setStart(sc, Math.min(domRange.startOffset, sc.childNodes.length)); + + ec = domRange.endContainer; + if (ec.nodeType == 1) + domRange.setEnd(ec, Math.min(domRange.endOffset, ec.childNodes.length)); + } + + // Restore selection to new range + t.addRange(domRange); + + return domRange; + }; + + this.addRange = function(rng) { + var ieRng, body = selection.dom.doc.body, startPos, endPos, sc, so, ec, eo; + + // Setup some shorter versions + sc = rng.startContainer; + so = rng.startOffset; + ec = rng.endContainer; + eo = rng.endOffset; + ieRng = body.createTextRange(); + + // Find element + sc = sc.nodeType == 1 ? sc.childNodes[Math.min(so, sc.childNodes.length - 1)] : sc; + ec = ec.nodeType == 1 ? ec.childNodes[Math.min(so == eo ? eo : eo - 1, ec.childNodes.length - 1)] : ec; + + // Single element selection + if (sc == ec && sc.nodeType == 1) { + // Make control selection for some elements + if (/^(IMG|TABLE)$/.test(sc.nodeName) && so != eo) { + ieRng = body.createControlRange(); + ieRng.addElement(sc); + } else { + ieRng = body.createTextRange(); + + // Padd empty elements with invisible character + if (!sc.hasChildNodes() && sc.canHaveHTML) + sc.innerHTML = invisibleChar; + + // Select element contents + ieRng.moveToElementText(sc); + + // If it's only containing a padding remove it so the caret remains + if (sc.innerHTML == invisibleChar) { + ieRng.collapse(true); + sc.removeChild(sc.firstChild); + } + } + + if (so == eo) + ieRng.collapse(eo <= rng.endContainer.childNodes.length - 1); + + ieRng.select(); + + return; + } + + function getCharPos(container, offset) { + var nodeVal, rng, pos; + + if (container.nodeType != 3) + return -1; + + nodeVal = container.nodeValue; + rng = body.createTextRange(); + + // Insert marker at offset position + container.nodeValue = nodeVal.substring(0, offset) + invisibleChar + nodeVal.substring(offset); + + // Find char pos of marker and remove it + rng.moveToElementText(container.parentNode); + rng.findText(invisibleChar); + pos = Math.abs(rng.moveStart('character', -0xFFFFF)); + container.nodeValue = nodeVal; + + return pos; + }; + + // Collapsed range + if (rng.collapsed) { + pos = getCharPos(sc, so); + + ieRng = body.createTextRange(); + ieRng.move('character', pos); + ieRng.select(); + + return; + } else { + // If same text container + if (sc == ec && sc.nodeType == 3) { + startPos = getCharPos(sc, so); + + ieRng.move('character', startPos); + ieRng.moveEnd('character', eo - so); + ieRng.select(); + + return; + } + + // Get caret positions + startPos = getCharPos(sc, so); + endPos = getCharPos(ec, eo); + ieRng = body.createTextRange(); + + // Move start of range to start character position or start element + if (startPos == -1) { + ieRng.moveToElementText(sc); + startPos = 0; + } else + ieRng.move('character', startPos); + + // Move end of range to end character position or end element + tmpRng = body.createTextRange(); + + if (endPos == -1) + tmpRng.moveToElementText(ec); + else + tmpRng.move('character', endPos); + + ieRng.setEndPoint('EndToEnd', tmpRng); + ieRng.select(); + + return; + } + }; + + this.getRangeAt = function() { + // Setup new range if the cache is empty + if (!range || !compareRanges(lastIERng, selection.getRng())) { + range = getRange(); + + // Store away text range for next call + lastIERng = selection.getRng(); + } + + // Return cached range + return range; + }; + + this.destroy = function() { + // Destroy cached range and last IE range to avoid memory leaks + lastIERng = range = null; + }; + }; + + // Expose the selection object + tinymce.dom.TridentSelection = Selection; +})(); + +/* + * Sizzle CSS Selector Engine - v1.0 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false; + +var Sizzle = function(selector, context, results, seed) { + results = results || []; + var origContext = context = context || document; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context); + + // Reset the position of the chunker regexp (start from head) + chunker.lastIndex = 0; + + while ( (m = chunker.exec(selector)) !== null ) { + parts.push( m[1] ); + + if ( m[2] ) { + extra = RegExp.rightContext; + break; + } + } + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) + selector += parts.shift(); + + set = posProcess( selector, set ); + } + } + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + var ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; + } + + if ( context ) { + var ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray(set); + } else { + prune = false; + } + + while ( parts.length ) { + var cur = parts.pop(), pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + throw "Syntax error, unrecognized expression: " + (cur || selector); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + } else if ( context && context.nodeType === 1 ) { + for ( var i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + } else { + for ( var i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function(results){ + if ( sortOrder ) { + hasDuplicate = false; + results.sort(sortOrder); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[i-1] ) { + results.splice(i--, 1); + } + } + } + } +}; + +Sizzle.matches = function(expr, set){ + return Sizzle(expr, null, null, set); +}; + +Sizzle.find = function(expr, context, isXML){ + var set, match; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var type = Expr.order[i], match; + + if ( (match = Expr.match[ type ].exec( expr )) ) { + var left = RegExp.leftContext; + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace(/\\/g, ""); + set = Expr.find[ type ]( match, context, isXML ); + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = context.getElementsByTagName("*"); + } + + return {set: set, expr: expr}; +}; + +Sizzle.filter = function(expr, set, inplace, not){ + var old = expr, result = [], curLoop = set, match, anyFound, + isXMLFilter = set && set[0] && isXML(set[0]); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.match[ type ].exec( expr )) != null ) { + var filter = Expr.filter[ type ], found, item; + anyFound = false; + + if ( curLoop == result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + } else { + curLoop[i] = false; + } + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr == old ) { + if ( anyFound == null ) { + throw "Syntax error, unrecognized expression: " + expr; + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + match: { + ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/ + }, + attrMap: { + "class": "className", + "for": "htmlFor" + }, + attrHandle: { + href: function(elem){ + return elem.getAttribute("href"); + } + }, + relative: { + "+": function(checkSet, part, isXML){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !/\W/.test(part), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag && !isXML ) { + part = part.toUpperCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + ">": function(checkSet, part, isXML){ + var isPartStr = typeof part === "string"; + + if ( isPartStr && !/\W/.test(part) ) { + part = isXML ? part : part.toUpperCase(); + + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName === part ? parent : false; + } + } + } else { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + "": function(checkSet, part, isXML){ + var doneName = done++, checkFn = dirCheck; + + if ( !part.match(/\W/) ) { + var nodeCheck = part = isXML ? part : part.toUpperCase(); + checkFn = dirNodeCheck; + } + + checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); + }, + "~": function(checkSet, part, isXML){ + var doneName = done++, checkFn = dirCheck; + + if ( typeof part === "string" && !part.match(/\W/) ) { + var nodeCheck = part = isXML ? part : part.toUpperCase(); + checkFn = dirNodeCheck; + } + + checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); + } + }, + find: { + ID: function(match, context, isXML){ + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m ? [m] : []; + } + }, + NAME: function(match, context, isXML){ + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], results = context.getElementsByName(match[1]); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + TAG: function(match, context){ + return context.getElementsByTagName(match[1]); + } + }, + preFilter: { + CLASS: function(match, curLoop, inplace, result, not, isXML){ + match = " " + match[1].replace(/\\/g, "") + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) { + if ( !inplace ) + result.push( elem ); + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + ID: function(match){ + return match[1].replace(/\\/g, ""); + }, + TAG: function(match, curLoop){ + for ( var i = 0; curLoop[i] === false; i++ ){} + return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase(); + }, + CHILD: function(match){ + if ( match[1] == "nth" ) { + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( + match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + ATTR: function(match, curLoop, inplace, result, not, isXML){ + var name = match[1].replace(/\\/g, ""); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + PSEUDO: function(match, curLoop, inplace, result, not){ + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + if ( !inplace ) { + result.push.apply( result, ret ); + } + return false; + } + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + POS: function(match){ + match.unshift( true ); + return match; + } + }, + filters: { + enabled: function(elem){ + return elem.disabled === false && elem.type !== "hidden"; + }, + disabled: function(elem){ + return elem.disabled === true; + }, + checked: function(elem){ + return elem.checked === true; + }, + selected: function(elem){ + // Accessing this property makes selected-by-default + // options in Safari work properly + elem.parentNode.selectedIndex; + return elem.selected === true; + }, + parent: function(elem){ + return !!elem.firstChild; + }, + empty: function(elem){ + return !elem.firstChild; + }, + has: function(elem, i, match){ + return !!Sizzle( match[3], elem ).length; + }, + header: function(elem){ + return /h\d/i.test( elem.nodeName ); + }, + text: function(elem){ + return "text" === elem.type; + }, + radio: function(elem){ + return "radio" === elem.type; + }, + checkbox: function(elem){ + return "checkbox" === elem.type; + }, + file: function(elem){ + return "file" === elem.type; + }, + password: function(elem){ + return "password" === elem.type; + }, + submit: function(elem){ + return "submit" === elem.type; + }, + image: function(elem){ + return "image" === elem.type; + }, + reset: function(elem){ + return "reset" === elem.type; + }, + button: function(elem){ + return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON"; + }, + input: function(elem){ + return /input|select|textarea|button/i.test(elem.nodeName); + } + }, + setFilters: { + first: function(elem, i){ + return i === 0; + }, + last: function(elem, i, match, array){ + return i === array.length - 1; + }, + even: function(elem, i){ + return i % 2 === 0; + }, + odd: function(elem, i){ + return i % 2 === 1; + }, + lt: function(elem, i, match){ + return i < match[3] - 0; + }, + gt: function(elem, i, match){ + return i > match[3] - 0; + }, + nth: function(elem, i, match){ + return match[3] - 0 == i; + }, + eq: function(elem, i, match){ + return match[3] - 0 == i; + } + }, + filter: { + PSEUDO: function(elem, match, i, array){ + var name = match[1], filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0; + } else if ( name === "not" ) { + var not = match[3]; + + for ( var i = 0, l = not.length; i < l; i++ ) { + if ( not[i] === elem ) { + return false; + } + } + + return true; + } + }, + CHILD: function(elem, match){ + var type = match[1], node = elem; + switch (type) { + case 'only': + case 'first': + while (node = node.previousSibling) { + if ( node.nodeType === 1 ) return false; + } + if ( type == 'first') return true; + node = elem; + case 'last': + while (node = node.nextSibling) { + if ( node.nodeType === 1 ) return false; + } + return true; + case 'nth': + var first = match[2], last = match[3]; + + if ( first == 1 && last == 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + if ( first == 0 ) { + return diff == 0; + } else { + return ( diff % first == 0 && diff / first >= 0 ); + } + } + }, + ID: function(elem, match){ + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + TAG: function(elem, match){ + return (match === "*" && elem.nodeType === 1) || elem.nodeName === match; + }, + CLASS: function(elem, match){ + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + ATTR: function(elem, match){ + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value != check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + POS: function(elem, match, i, array){ + var name = match[2], filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); +} + +var makeArray = function(array, results) { + array = Array.prototype.slice.call( array ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +try { + Array.prototype.slice.call( document.documentElement.childNodes ); + +// Provide a fallback method if it does not work +} catch(e){ + makeArray = function(array, results) { + var ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + } else { + if ( typeof array.length === "number" ) { + for ( var i = 0, l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + } else { + for ( var i = 0; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( "sourceIndex" in document.documentElement ) { + sortOrder = function( a, b ) { + var ret = a.sourceIndex - b.sourceIndex; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( document.createRange ) { + sortOrder = function( a, b ) { + var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); + aRange.setStart(a, 0); + aRange.setEnd(a, 0); + bRange.setStart(b, 0); + bRange.setEnd(b, 0); + var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date).getTime(); + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + var root = document.documentElement; + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( !!document.getElementById( id ) ) { + Expr.find.ID = function(match, context, isXML){ + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; + } + }; + + Expr.filter.ID = function(elem, match){ + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function(match, context){ + var results = context.getElementsByTagName(match[1]); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + Expr.attrHandle.href = function(elem){ + return elem.getAttribute("href", 2); + }; + } +})(); + +if ( document.querySelectorAll ) (function(){ + var oldSizzle = Sizzle, div = document.createElement("div"); + div.innerHTML = "

"; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function(query, context, extra, seed){ + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && context.nodeType === 9 && !isXML(context) ) { + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(e){} + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } +})(); + +if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){ + var div = document.createElement("div"); + div.innerHTML = "
"; + + // Opera can't find a second classname (in 9.6) + if ( div.getElementsByClassName("e").length === 0 ) + return; + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) + return; + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function(match, context, isXML) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + var sibDir = dir == "previousSibling" && !isXML; + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + if ( sibDir && elem.nodeType === 1 ){ + elem.sizcache = doneName; + elem.sizset = i; + } + elem = elem[dir]; + var match = false; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + var sibDir = dir == "previousSibling" && !isXML; + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + if ( sibDir && elem.nodeType === 1 ) { + elem.sizcache = doneName; + elem.sizset = i; + } + elem = elem[dir]; + var match = false; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +var contains = document.compareDocumentPosition ? function(a, b){ + return a.compareDocumentPosition(b) & 16; +} : function(a, b){ + return a !== b && (a.contains ? a.contains(b) : true); +}; + +var isXML = function(elem){ + return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || + !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML"; +}; + +var posProcess = function(selector, context){ + var tmpSet = [], later = "", match, + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE + +window.tinymce.dom.Sizzle = Sizzle; + +})(); + +(function(tinymce) { + // Shorten names + var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event; + + tinymce.create('static tinymce.dom.Event', { + inits : [], + events : [], + + + add : function(o, n, f, s) { + var cb, t = this, el = t.events, r; + + // Handle array + if (o && o.hasOwnProperty && o instanceof Array) { + r = []; + + each(o, function(o) { + o = DOM.get(o); + r.push(t.add(o, n, f, s)); + }); + + return r; + } + + o = DOM.get(o); + + if (!o) + return; + + // Setup event callback + cb = function(e) { + e = e || window.event; + + // Patch in target in IE it's W3C valid + if (e && !e.target && isIE) + e.target = e.srcElement; if (!s) return f(e); @@ -2484,7 +4355,7 @@ tinymce.create('static tinymce.util.XHR', { var t = this, a = t.events, s = false, r; // Handle array - if (o && o instanceof Array) { + if (o && o.hasOwnProperty && o instanceof Array) { r = []; each(o, function(o) { @@ -2527,7 +4398,6 @@ tinymce.create('static tinymce.util.XHR', { } }, - // #endif cancel : function(e) { if (!e) @@ -2594,6 +4464,10 @@ tinymce.create('static tinymce.util.XHR', { _pageInit : function() { var e = Event; + // Keep it from running more than once + if (e.domLoaded) + return; + e._remove(window, 'DOMContentLoaded', e._pageInit); e.domLoaded = true; @@ -2605,35 +4479,42 @@ tinymce.create('static tinymce.util.XHR', { }, _wait : function() { - var t; - // No need since the document is already loaded if (window.tinyMCE_GZ && tinyMCE_GZ.loaded) { Event.domLoaded = 1; return; } - if (isIE && document.location.protocol != 'https:') { - // Fake DOMContentLoaded on IE - document.write('