From 84f59ab451c2cfd38ab4955c609580fe1630524a Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Sat, 9 Oct 2010 08:04:25 +0000 Subject: [PATCH] Run TinyMCE as content rather than chrome (and now without needing any modifications) Also upgraded to latest version Popup windows currently show location and status bars, which needs to be fixed --- .../tinymce/css}/integration-content.css | 0 .../zotero/tinymce/css}/note-content.css | 0 .../zotero/tinymce/css}/note-ui.css | 3 + chrome/content/zotero/tinymce/note.html | 4 +- .../plugins/contextmenu/editor_plugin.js | 71 +- .../tinymce/plugins/paste/editor_plugin.js | 688 +- .../zotero/tinymce/themes/advanced/about.htm | 6 +- .../zotero/tinymce/themes/advanced/anchor.htm | 12 +- .../tinymce/themes/advanced/charmap.htm | 4 +- .../tinymce/themes/advanced/color_picker.htm | 7 +- .../themes/advanced/editor_template.js | 1148 +- .../zotero/tinymce/themes/advanced/image.htm | 24 +- .../tinymce/themes/advanced/img/icons.gif | Bin 11505 -> 11794 bytes .../tinymce/themes/advanced/js/anchor.js | 14 +- .../tinymce/themes/advanced/js/charmap.js | 10 + .../tinymce/themes/advanced/js/image.js | 4 +- .../zotero/tinymce/themes/advanced/js/link.js | 11 +- .../themes/advanced/js/source_editor.js | 22 +- .../zotero/tinymce/themes/advanced/link.htm | 14 +- .../themes/advanced/skins/default/content.css | 10 +- .../themes/advanced/skins/default/dialog.css | 11 +- .../themes/advanced/skins/default/ui.css | 30 +- .../tinymce/themes/advanced/source_editor.htm | 11 +- chrome/content/zotero/tinymce/tiny_mce.js | 9326 ++++++++++------- .../content/zotero/tinymce/tiny_mce_popup.js | 303 +- .../zotero/tinymce/utils/editable_selects.js | 9 +- .../zotero/tinymce/utils/form_utils.js | 15 +- chrome/content/zotero/tinymce/utils/mctabs.js | 9 +- .../content/zotero/tinymce/utils/validate.js | 9 +- 29 files changed, 5997 insertions(+), 5778 deletions(-) rename chrome/{skin/default/zotero/tinymce => content/zotero/tinymce/css}/integration-content.css (100%) rename chrome/{skin/default/zotero/tinymce => content/zotero/tinymce/css}/note-content.css (100%) rename chrome/{skin/default/zotero/tinymce => content/zotero/tinymce/css}/note-ui.css (91%) diff --git a/chrome/skin/default/zotero/tinymce/integration-content.css b/chrome/content/zotero/tinymce/css/integration-content.css similarity index 100% rename from chrome/skin/default/zotero/tinymce/integration-content.css rename to chrome/content/zotero/tinymce/css/integration-content.css diff --git a/chrome/skin/default/zotero/tinymce/note-content.css b/chrome/content/zotero/tinymce/css/note-content.css similarity index 100% rename from chrome/skin/default/zotero/tinymce/note-content.css rename to chrome/content/zotero/tinymce/css/note-content.css diff --git a/chrome/skin/default/zotero/tinymce/note-ui.css b/chrome/content/zotero/tinymce/css/note-ui.css similarity index 91% rename from chrome/skin/default/zotero/tinymce/note-ui.css rename to chrome/content/zotero/tinymce/css/note-ui.css index 3c04ff60e3..99a742dfcb 100644 --- a/chrome/skin/default/zotero/tinymce/note-ui.css +++ b/chrome/content/zotero/tinymce/css/note-ui.css @@ -30,3 +30,6 @@ td.mceIframeContainer { width: 100% !important; } +#tinymce_formatselect_text { + width: 65px; +} diff --git a/chrome/content/zotero/tinymce/note.html b/chrome/content/zotero/tinymce/note.html index 7608442bd2..e7d59700d1 100755 --- a/chrome/content/zotero/tinymce/note.html +++ b/chrome/content/zotero/tinymce/note.html @@ -2,7 +2,7 @@ TinyMCE - + -
@@ -13,19 +12,14 @@ {#advanced_dlg.anchor_title} - {#advanced_dlg.anchor_name}: + {#advanced_dlg.anchor_name}:
-
- -
- -
- -
+ +
diff --git a/chrome/content/zotero/tinymce/themes/advanced/charmap.htm b/chrome/content/zotero/tinymce/themes/advanced/charmap.htm index e4c7344848..3991b8141b 100755 --- a/chrome/content/zotero/tinymce/themes/advanced/charmap.htm +++ b/chrome/content/zotero/tinymce/themes/advanced/charmap.htm @@ -1,11 +1,9 @@ - + {#advanced_dlg.charmap_title} - - diff --git a/chrome/content/zotero/tinymce/themes/advanced/color_picker.htm b/chrome/content/zotero/tinymce/themes/advanced/color_picker.htm index a8f297c603..096e7550c3 100755 --- a/chrome/content/zotero/tinymce/themes/advanced/color_picker.htm +++ b/chrome/content/zotero/tinymce/themes/advanced/color_picker.htm @@ -5,7 +5,6 @@ - @@ -22,7 +21,7 @@
{#advanced_dlg.colorpicker_picker_title}
- +
@@ -61,9 +60,7 @@
-
- -
+
diff --git a/chrome/content/zotero/tinymce/themes/advanced/editor_template.js b/chrome/content/zotero/tinymce/themes/advanced/editor_template.js index 2d8c3aa7dd..c5a1719a7d 100755 --- a/chrome/content/zotero/tinymce/themes/advanced/editor_template.js +++ b/chrome/content/zotero/tinymce/themes/advanced/editor_template.js @@ -1,1147 +1 @@ -/** - * $Id: editor_template_src.js 925 2008-09-11 11:25:26Z spocke $ - * - * @author Moxiecode - * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved. - */ - -(function() { - var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, each = tinymce.each, Cookie = tinymce.util.Cookie, lastExtID, explode = tinymce.explode; - - // Tell it to load theme specific language pack(s) - tinymce.ThemeManager.requireLangPack('advanced'); - - tinymce.create('tinymce.themes.AdvancedTheme', { - sizes : [8, 10, 12, 14, 18, 24, 36], - - // Control name lookup, format: title, command - controls : { - bold : ['bold_desc', 'Bold'], - italic : ['italic_desc', 'Italic'], - underline : ['underline_desc', 'Underline'], - strikethrough : ['striketrough_desc', 'Strikethrough'], - justifyleft : ['justifyleft_desc', 'JustifyLeft'], - justifycenter : ['justifycenter_desc', 'JustifyCenter'], - justifyright : ['justifyright_desc', 'JustifyRight'], - justifyfull : ['justifyfull_desc', 'JustifyFull'], - bullist : ['bullist_desc', 'InsertUnorderedList'], - numlist : ['numlist_desc', 'InsertOrderedList'], - outdent : ['outdent_desc', 'Outdent'], - indent : ['indent_desc', 'Indent'], - cut : ['cut_desc', 'Cut'], - copy : ['copy_desc', 'Copy'], - paste : ['paste_desc', 'Paste'], - undo : ['undo_desc', 'Undo'], - redo : ['redo_desc', 'Redo'], - link : ['link_desc', 'mceLink'], - unlink : ['unlink_desc', 'unlink'], - image : ['image_desc', 'mceImage'], - cleanup : ['cleanup_desc', 'mceCleanup'], - help : ['help_desc', 'mceHelp'], - code : ['code_desc', 'mceCodeEditor'], - hr : ['hr_desc', 'InsertHorizontalRule'], - removeformat : ['removeformat_desc', 'RemoveFormat'], - sub : ['sub_desc', 'subscript'], - sup : ['sup_desc', 'superscript'], - forecolor : ['forecolor_desc', 'ForeColor'], - forecolorpicker : ['forecolor_desc', 'mceForeColor'], - backcolor : ['backcolor_desc', 'HiliteColor'], - backcolorpicker : ['backcolor_desc', 'mceBackColor'], - charmap : ['charmap_desc', 'mceCharMap'], - visualaid : ['visualaid_desc', 'mceToggleVisualAid'], - anchor : ['anchor_desc', 'mceInsertAnchor'], - newdocument : ['newdocument_desc', 'mceNewDocument'], - blockquote : ['blockquote_desc', 'mceBlockQuote'] - }, - - stateControls : ['bold', 'italic', 'underline', 'strikethrough', 'bullist', 'numlist', 'justifyleft', 'justifycenter', 'justifyright', 'justifyfull', 'sub', 'sup', 'blockquote'], - - init : function(ed, url) { - var t = this, s, v, o; - - t.editor = ed; - t.url = url; - t.onResolveName = new tinymce.util.Dispatcher(this); - - // Default settings - t.settings = s = extend({ - theme_advanced_path : true, - theme_advanced_toolbar_location : 'bottom', - theme_advanced_buttons1 : "bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect", - theme_advanced_buttons2 : "bullist,numlist,|,outdent,indent,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code", - theme_advanced_buttons3 : "hr,removeformat,visualaid,|,sub,sup,|,charmap", - theme_advanced_blockformats : "p,address,pre,h1,h2,h3,h4,h5,h6", - theme_advanced_toolbar_align : "center", - theme_advanced_fonts : "Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats", - theme_advanced_more_colors : 1, - theme_advanced_row_height : 23, - theme_advanced_resize_horizontal : 1, - theme_advanced_resizing_use_cookie : 1, - theme_advanced_font_sizes : "1,2,3,4,5,6,7", - readonly : ed.settings.readonly - }, ed.settings); - - // Setup default font_size_style_values - if (!s.font_size_style_values) - s.font_size_style_values = "8pt,10pt,12pt,14pt,18pt,24pt,36pt"; - - if (tinymce.is(s.theme_advanced_font_sizes, 'string')) { - s.font_size_style_values = tinymce.explode(s.font_size_style_values); - s.font_size_classes = tinymce.explode(s.font_size_classes || ''); - - // Parse string value - o = {}; - ed.settings.theme_advanced_font_sizes = s.theme_advanced_font_sizes; - each(ed.getParam('theme_advanced_font_sizes', '', 'hash'), function(v, k) { - var cl; - - if (k == v && v >= 1 && v <= 7) { - k = v + ' (' + t.sizes[v - 1] + 'pt)'; - - if (ed.settings.convert_fonts_to_spans) { - cl = s.font_size_classes[v - 1]; - v = s.font_size_style_values[v - 1] || (t.sizes[v - 1] + 'pt'); - } - } - - if (/\s*\./.test(v)) - cl = v.replace(/\./g, ''); - - o[k] = cl ? {'class' : cl} : {fontSize : v}; - }); - - s.theme_advanced_font_sizes = o; - } - - if ((v = s.theme_advanced_path_location) && v != 'none') - s.theme_advanced_statusbar_location = s.theme_advanced_path_location; - - if (s.theme_advanced_statusbar_location == 'none') - s.theme_advanced_statusbar_location = 0; - - // Init editor - ed.onInit.add(function() { - ed.onNodeChange.add(t._nodeChanged, t); - - if (ed.settings.content_css !== false) - ed.dom.loadCSS(ed.baseURI.toAbsolute("themes/advanced/skins/" + ed.settings.skin + "/content.css")); - }); - - ed.onSetProgressState.add(function(ed, b, ti) { - var co, id = ed.id, tb; - - if (b) { - t.progressTimer = setTimeout(function() { - co = ed.getContainer(); - co = co.insertBefore(DOM.create('DIV', {style : 'position:relative'}), co.firstChild); - tb = DOM.get(ed.id + '_tbl'); - - DOM.add(co, 'div', {id : id + '_blocker', 'class' : 'mceBlocker', style : {width : tb.clientWidth + 2, height : tb.clientHeight + 2}}); - DOM.add(co, 'div', {id : id + '_progress', 'class' : 'mceProgress', style : {left : tb.clientWidth / 2, top : tb.clientHeight / 2}}); - }, ti || 0); - } else { - DOM.remove(id + '_blocker'); - DOM.remove(id + '_progress'); - clearTimeout(t.progressTimer); - } - }); - - DOM.loadCSS(s.editor_css ? ed.documentBaseURI.toAbsolute(s.editor_css) : url + "/skins/" + ed.settings.skin + "/ui.css"); - - if (s.skin_variant) - DOM.loadCSS(url + "/skins/" + ed.settings.skin + "/ui_" + s.skin_variant + ".css"); - }, - - createControl : function(n, cf) { - var cd, c; - - if (c = cf.createControl(n)) - return c; - - switch (n) { - case "styleselect": - return this._createStyleSelect(); - - case "formatselect": - return this._createBlockFormats(); - - case "fontselect": - return this._createFontSelect(); - - case "fontsizeselect": - return this._createFontSizeSelect(); - - case "forecolor": - return this._createForeColorMenu(); - - case "backcolor": - return this._createBackColorMenu(); - } - - if ((cd = this.controls[n])) - return cf.createButton(n, {title : "advanced." + cd[0], cmd : cd[1], ui : cd[2], value : cd[3]}); - }, - - execCommand : function(cmd, ui, val) { - var f = this['_' + cmd]; - - if (f) { - f.call(this, ui, val); - return true; - } - - return false; - }, - - _importClasses : function(e) { - var ed = this.editor, c = ed.controlManager.get('styleselect'); - - if (c.getLength() == 0) { - each(ed.dom.getClasses(), function(o) { - c.add(o['class'], o['class']); - }); - } - }, - - _createStyleSelect : function(n) { - var t = this, ed = t.editor, cf = ed.controlManager, c = cf.createListBox('styleselect', { - title : 'advanced.style_select', - onselect : function(v) { - if (c.selectedValue === v) { - ed.execCommand('mceSetStyleInfo', 0, {command : 'removeformat'}); - c.select(); - return false; - } else - ed.execCommand('mceSetCSSClass', 0, v); - } - }); - - if (c) { - each(ed.getParam('theme_advanced_styles', '', 'hash'), function(v, k) { - if (v) - c.add(t.editor.translate(k), v); - }); - - c.onPostRender.add(function(ed, n) { - if (!c.NativeListBox) { - Event.add(n.id + '_text', 'focus', t._importClasses, t); - Event.add(n.id + '_text', 'mousedown', t._importClasses, t); - Event.add(n.id + '_open', 'focus', t._importClasses, t); - Event.add(n.id + '_open', 'mousedown', t._importClasses, t); - } else - Event.add(n.id, 'focus', t._importClasses, t); - }); - } - - return c; - }, - - _createFontSelect : function() { - var c, t = this, ed = t.editor; - - c = ed.controlManager.createListBox('fontselect', {title : 'advanced.fontdefault', cmd : 'FontName'}); - if (c) { - each(ed.getParam('theme_advanced_fonts', t.settings.theme_advanced_fonts, 'hash'), function(v, k) { - c.add(ed.translate(k), v, {style : v.indexOf('dings') == -1 ? 'font-family:' + v : ''}); - }); - } - - return c; - }, - - _createFontSizeSelect : function() { - var t = this, ed = t.editor, c, i = 0, cl = []; - - c = ed.controlManager.createListBox('fontsizeselect', {title : 'advanced.font_size', onselect : function(v) { - if (v.fontSize) - ed.execCommand('FontSize', false, v.fontSize); - else { - each(t.settings.theme_advanced_font_sizes, function(v, k) { - if (v['class']) - cl.push(v['class']); - }); - - ed.editorCommands._applyInlineStyle('span', {'class' : v['class']}, {check_classes : cl}); - } - }}); - - if (c) { - each(t.settings.theme_advanced_font_sizes, function(v, k) { - var fz = v.fontSize; - - if (fz >= 1 && fz <= 7) - fz = t.sizes[parseInt(fz) - 1] + 'pt'; - - c.add(k, v, {'style' : 'font-size:' + fz, 'class' : 'mceFontSize' + (i++) + (' ' + (v['class'] || ''))}); - }); - } - - return c; - }, - - _createBlockFormats : function() { - var c, fmts = { - p : 'advanced.paragraph', - address : 'advanced.address', - pre : 'advanced.pre', - h1 : 'advanced.h1', - h2 : 'advanced.h2', - h3 : 'advanced.h3', - h4 : 'advanced.h4', - h5 : 'advanced.h5', - h6 : 'advanced.h6', - div : 'advanced.div', - blockquote : 'advanced.blockquote', - code : 'advanced.code', - dt : 'advanced.dt', - dd : 'advanced.dd', - samp : 'advanced.samp' - }, t = this; - - c = t.editor.controlManager.createListBox('formatselect', {title : 'advanced.block', cmd : 'FormatBlock'}); - if (c) { - each(t.editor.getParam('theme_advanced_blockformats', t.settings.theme_advanced_blockformats, 'hash'), function(v, k) { - c.add(t.editor.translate(k != v ? k : fmts[v]), v, {'class' : 'mce_formatPreview mce_' + v}); - }); - } - - return c; - }, - - _createForeColorMenu : function() { - var c, t = this, s = t.settings, o = {}, v; - - if (s.theme_advanced_more_colors) { - o.more_colors_func = function() { - t._mceColorPicker(0, { - color : c.value, - func : function(co) { - c.setColor(co); - } - }); - }; - } - - if (v = s.theme_advanced_text_colors) - o.colors = v; - - o.title = 'advanced.forecolor_desc'; - o.cmd = 'ForeColor'; - o.scope = this; - - c = t.editor.controlManager.createColorSplitButton('forecolor', o); - - return c; - }, - - _createBackColorMenu : function() { - var c, t = this, s = t.settings, o = {}, v; - - if (s.theme_advanced_more_colors) { - o.more_colors_func = function() { - t._mceColorPicker(0, { - color : c.value, - func : function(co) { - c.setColor(co); - } - }); - }; - } - - if (v = s.theme_advanced_background_colors) - o.colors = v; - - o.title = 'advanced.backcolor_desc'; - o.cmd = 'HiliteColor'; - o.scope = this; - - c = t.editor.controlManager.createColorSplitButton('backcolor', o); - - return c; - }, - - renderUI : function(o) { - var n, ic, tb, t = this, ed = t.editor, s = t.settings, sc, p, nl; - - n = p = DOM.create('span', {id : ed.id + '_parent', 'class' : 'mceEditor ' + ed.settings.skin + 'Skin' + (s.skin_variant ? ' ' + ed.settings.skin + 'Skin' + t._ufirst(s.skin_variant) : '')}); - - if (!DOM.boxModel) - n = DOM.add(n, 'div', {'class' : 'mceOldBoxModel'}); - - n = sc = DOM.add(n, 'table', {id : ed.id + '_tbl', 'class' : 'mceLayout', cellSpacing : 0, cellPadding : 0}); - n = tb = DOM.add(n, 'tbody'); - - switch ((s.theme_advanced_layout_manager || '').toLowerCase()) { - case "rowlayout": - ic = t._rowLayout(s, tb, o); - break; - - case "customlayout": - ic = ed.execCallback("theme_advanced_custom_layout", s, tb, o, p); - break; - - default: - ic = t._simpleLayout(s, tb, o, p); - } - - n = o.targetNode; - - // Add classes to first and last TRs - nl = DOM.stdMode ? sc.getElementsByTagName('tr') : sc.rows; // Quick fix for IE 8 - DOM.addClass(nl[0], 'mceFirst'); - DOM.addClass(nl[nl.length - 1], 'mceLast'); - - // Add classes to first and last TDs - each(DOM.select('tr', tb), function(n) { - DOM.addClass(n.firstChild, 'mceFirst'); - DOM.addClass(n.childNodes[n.childNodes.length - 1], 'mceLast'); - }); - - if (DOM.get(s.theme_advanced_toolbar_container)) - DOM.get(s.theme_advanced_toolbar_container).appendChild(p); - else - DOM.insertAfter(p, n); - - Event.add(ed.id + '_path_row', 'click', function(e) { - e = e.target; - - if (e.nodeName == 'A') { - t._sel(e.className.replace(/^.*mcePath_([0-9]+).*$/, '$1')); - - return Event.cancel(e); - } - }); -/* - if (DOM.get(ed.id + '_path_row')) { - Event.add(ed.id + '_tbl', 'mouseover', function(e) { - var re; - - e = e.target; - - if (e.nodeName == 'SPAN' && DOM.hasClass(e.parentNode, 'mceButton')) { - re = DOM.get(ed.id + '_path_row'); - t.lastPath = re.innerHTML; - DOM.setHTML(re, e.parentNode.title); - } - }); - - Event.add(ed.id + '_tbl', 'mouseout', function(e) { - if (t.lastPath) { - DOM.setHTML(ed.id + '_path_row', t.lastPath); - t.lastPath = 0; - } - }); - } -*/ - - if (!ed.getParam('accessibility_focus') || ed.getParam('tab_focus')) - Event.add(DOM.add(p, 'a', {href : '#'}, ''), 'focus', function() {tinyMCE.get(ed.id).focus();}); - - if (s.theme_advanced_toolbar_location == 'external') - o.deltaHeight = 0; - - t.deltaHeight = o.deltaHeight; - o.targetNode = null; - - return { - iframeContainer : ic, - editorContainer : ed.id + '_parent', - sizeContainer : sc, - deltaHeight : o.deltaHeight - }; - }, - - getInfo : function() { - return { - longname : 'Advanced theme', - author : 'Moxiecode Systems AB', - authorurl : 'http://tinymce.moxiecode.com', - version : tinymce.majorVersion + "." + tinymce.minorVersion - } - }, - - resizeBy : function(dw, dh) { - var e = DOM.get(this.editor.id + '_tbl'); - - this.resizeTo(e.clientWidth + dw, e.clientHeight + dh); - }, - - resizeTo : function(w, h) { - var ed = this.editor, s = ed.settings, e = DOM.get(ed.id + '_tbl'), ifr = DOM.get(ed.id + '_ifr'), dh; - - // Boundery fix box - w = Math.max(s.theme_advanced_resizing_min_width || 100, w); - h = Math.max(s.theme_advanced_resizing_min_height || 100, h); - w = Math.min(s.theme_advanced_resizing_max_width || 0xFFFF, w); - h = Math.min(s.theme_advanced_resizing_max_height || 0xFFFF, h); - - // Calc difference between iframe and container - dh = e.clientHeight - ifr.clientHeight; - - // Resize iframe and container - DOM.setStyle(ifr, 'height', h - dh); - DOM.setStyles(e, {width : w, height : h}); - }, - - destroy : function() { - var id = this.editor.id; - - Event.clear(id + '_resize'); - Event.clear(id + '_path_row'); - Event.clear(id + '_external_close'); - }, - - // Internal functions - - _simpleLayout : function(s, tb, o, p) { - var t = this, ed = t.editor, lo = s.theme_advanced_toolbar_location, sl = s.theme_advanced_statusbar_location, n, ic, etb, c; - - if (s.readonly) { - n = DOM.add(tb, 'tr'); - n = ic = DOM.add(n, 'td', {'class' : 'mceIframeContainer'}); - return ic; - } - - // Create toolbar container at top - if (lo == 'top') - t._addToolbars(tb, o); - - // Create external toolbar - if (lo == 'external') { - n = c = DOM.create('div', {style : 'position:relative'}); - n = DOM.add(n, 'div', {id : ed.id + '_external', 'class' : 'mceExternalToolbar'}); - DOM.add(n, 'a', {id : ed.id + '_external_close', href : 'javascript:;', 'class' : 'mceExternalClose'}); - n = DOM.add(n, 'table', {id : ed.id + '_tblext', cellSpacing : 0, cellPadding : 0}); - etb = DOM.add(n, 'tbody'); - - if (p.firstChild.className == 'mceOldBoxModel') - p.firstChild.appendChild(c); - else - p.insertBefore(c, p.firstChild); - - t._addToolbars(etb, o); - - ed.onMouseUp.add(function() { - var e = DOM.get(ed.id + '_external'); - DOM.show(e); - - DOM.hide(lastExtID); - - var f = Event.add(ed.id + '_external_close', 'click', function() { - DOM.hide(ed.id + '_external'); - Event.remove(ed.id + '_external_close', 'click', f); - }); - - DOM.show(e); - DOM.setStyle(e, 'top', 0 - DOM.getRect(ed.id + '_tblext').h - 1); - - // Fixes IE rendering bug - DOM.hide(e); - DOM.show(e); - e.style.filter = ''; - - lastExtID = ed.id + '_external'; - - e = null; - }); - } - - if (sl == 'top') - t._addStatusBar(tb, o); - - // Create iframe container - if (!s.theme_advanced_toolbar_container) { - n = DOM.add(tb, 'tr'); - n = ic = DOM.add(n, 'td', {'class' : 'mceIframeContainer'}); - } - - // Create toolbar container at bottom - if (lo == 'bottom') - t._addToolbars(tb, o); - - if (sl == 'bottom') - t._addStatusBar(tb, o); - - return ic; - }, - - _rowLayout : function(s, tb, o) { - var t = this, ed = t.editor, dc, da, cf = ed.controlManager, n, ic, to, a; - - dc = s.theme_advanced_containers_default_class || ''; - da = s.theme_advanced_containers_default_align || 'center'; - - each(explode(s.theme_advanced_containers || ''), function(c, i) { - var v = s['theme_advanced_container_' + c] || ''; - - switch (v.toLowerCase()) { - case 'mceeditor': - n = DOM.add(tb, 'tr'); - n = ic = DOM.add(n, 'td', {'class' : 'mceIframeContainer'}); - break; - - case 'mceelementpath': - t._addStatusBar(tb, o); - break; - - default: - a = (s['theme_advanced_container_' + c + '_align'] || da).toLowerCase(); - a = 'mce' + t._ufirst(a); - - n = DOM.add(DOM.add(tb, 'tr'), 'td', { - 'class' : 'mceToolbar ' + (s['theme_advanced_container_' + c + '_class'] || dc) + ' ' + a || da - }); - - to = cf.createToolbar("toolbar" + i); - t._addControls(v, to); - DOM.setHTML(n, to.renderHTML()); - o.deltaHeight -= s.theme_advanced_row_height; - } - }); - - return ic; - }, - - _addControls : function(v, tb) { - var t = this, s = t.settings, di, cf = t.editor.controlManager; - - if (s.theme_advanced_disable && !t._disabled) { - di = {}; - - each(explode(s.theme_advanced_disable), function(v) { - di[v] = 1; - }); - - t._disabled = di; - } else - di = t._disabled; - - each(explode(v), function(n) { - var c; - - if (di && di[n]) - return; - - // Compatiblity with 2.x - if (n == 'tablecontrols') { - each(["table","|","row_props","cell_props","|","row_before","row_after","delete_row","|","col_before","col_after","delete_col","|","split_cells","merge_cells"], function(n) { - n = t.createControl(n, cf); - - if (n) - tb.add(n); - }); - - return; - } - - c = t.createControl(n, cf); - - if (c) - tb.add(c); - }); - }, - - _addToolbars : function(c, o) { - var t = this, i, tb, ed = t.editor, s = t.settings, v, cf = ed.controlManager, di, n, h = [], a; - - a = s.theme_advanced_toolbar_align.toLowerCase(); - a = 'mce' + t._ufirst(a); - - n = DOM.add(DOM.add(c, 'tr'), 'td', {'class' : 'mceToolbar ' + a}); - - if (!ed.getParam('accessibility_focus') || ed.getParam('tab_focus')) - h.push(DOM.createHTML('a', {href : '#', onfocus : 'tinyMCE.get(\'' + ed.id + '\').focus();'}, '')); - - h.push(DOM.createHTML('a', {href : '#', accesskey : 'q', title : ed.getLang("advanced.toolbar_focus")}, '')); - - // Create toolbar and add the controls - for (i=1; (v = s['theme_advanced_buttons' + i]); i++) { - tb = cf.createToolbar("toolbar" + i, {'class' : 'mceToolbarRow' + i}); - - if (s['theme_advanced_buttons' + i + '_add']) - v += ',' + s['theme_advanced_buttons' + i + '_add']; - - if (s['theme_advanced_buttons' + i + '_add_before']) - v = s['theme_advanced_buttons' + i + '_add_before'] + ',' + v; - - t._addControls(v, tb); - - //n.appendChild(n = tb.render()); - h.push(tb.renderHTML()); - - o.deltaHeight -= s.theme_advanced_row_height; - } - - h.push(DOM.createHTML('a', {href : '#', accesskey : 'z', title : ed.getLang("advanced.toolbar_focus"), onfocus : 'tinyMCE.getInstanceById(\'' + ed.id + '\').focus();'}, '')); - DOM.setHTML(n, h.join('')); - }, - - _addStatusBar : function(tb, o) { - var n, t = this, ed = t.editor, s = t.settings, r, mf, me, td; - - n = DOM.add(tb, 'tr'); - n = td = DOM.add(n, 'td', {'class' : 'mceStatusbar'}); - n = DOM.add(n, 'div', {id : ed.id + '_path_row'}, s.theme_advanced_path ? ed.translate('advanced.path') + ': ' : ' '); - DOM.add(n, 'a', {href : '#', accesskey : 'x'}); - - if (s.theme_advanced_resizing && !tinymce.isOldWebKit) { - DOM.add(td, 'a', {id : ed.id + '_resize', href : 'javascript:;', onclick : "return false;", 'class' : 'mceResize'}); - - if (s.theme_advanced_resizing_use_cookie) { - ed.onPostRender.add(function() { - var o = Cookie.getHash("TinyMCE_" + ed.id + "_size"), c = DOM.get(ed.id + '_tbl'); - - if (!o) - return; - - if (s.theme_advanced_resize_horizontal) - c.style.width = Math.max(10, o.cw) + 'px'; - - c.style.height = Math.max(10, o.ch) + 'px'; - DOM.get(ed.id + '_ifr').style.height = Math.max(10, parseInt(o.ch) + t.deltaHeight) + 'px'; - }); - } - - ed.onPostRender.add(function() { - Event.add(ed.id + '_resize', 'mousedown', function(e) { - var c, p, w, h, n, pa; - - // Measure container - c = DOM.get(ed.id + '_tbl'); - w = c.clientWidth; - h = c.clientHeight; - - miw = s.theme_advanced_resizing_min_width || 100; - mih = s.theme_advanced_resizing_min_height || 100; - maw = s.theme_advanced_resizing_max_width || 0xFFFF; - mah = s.theme_advanced_resizing_max_height || 0xFFFF; - - // Setup placeholder - p = DOM.add(DOM.get(ed.id + '_parent'), 'div', {'class' : 'mcePlaceHolder'}); - DOM.setStyles(p, {width : w, height : h}); - - // Replace with placeholder - DOM.hide(c); - DOM.show(p); - - // Create internal resize obj - r = { - x : e.screenX, - y : e.screenY, - w : w, - h : h, - dx : null, - dy : null - }; - - // Start listening - mf = Event.add(DOM.doc, 'mousemove', function(e) { - var w, h; - - // Calc delta values - r.dx = e.screenX - r.x; - r.dy = e.screenY - r.y; - - // Boundery fix box - w = Math.max(miw, r.w + r.dx); - h = Math.max(mih, r.h + r.dy); - w = Math.min(maw, w); - h = Math.min(mah, h); - - // Resize placeholder - if (s.theme_advanced_resize_horizontal) - p.style.width = w + 'px'; - - p.style.height = h + 'px'; - - return Event.cancel(e); - }); - - me = Event.add(DOM.doc, 'mouseup', function(e) { - var ifr; - - // Stop listening - Event.remove(DOM.doc, 'mousemove', mf); - Event.remove(DOM.doc, 'mouseup', me); - - c.style.display = ''; - DOM.remove(p); - - if (r.dx === null) - return; - - ifr = DOM.get(ed.id + '_ifr'); - - if (s.theme_advanced_resize_horizontal) - c.style.width = Math.max(10, r.w + r.dx) + 'px'; - - c.style.height = Math.max(10, r.h + r.dy) + 'px'; - ifr.style.height = Math.max(10, ifr.clientHeight + r.dy) + 'px'; - - if (s.theme_advanced_resizing_use_cookie) { - Cookie.setHash("TinyMCE_" + ed.id + "_size", { - cw : r.w + r.dx, - ch : r.h + r.dy - }); - } - }); - - return Event.cancel(e); - }); - }); - } - - o.deltaHeight -= 21; - n = tb = null; - }, - - _nodeChanged : function(ed, cm, n, co) { - var t = this, p, de = 0, v, c, s = t.settings, cl, fz, fn; - - if (s.readonly) - return; - - tinymce.each(t.stateControls, function(c) { - cm.setActive(c, ed.queryCommandState(t.controls[c][1])); - }); - - cm.setActive('visualaid', ed.hasVisual); - cm.setDisabled('undo', !ed.undoManager.hasUndo() && !ed.typing); - cm.setDisabled('redo', !ed.undoManager.hasRedo()); - cm.setDisabled('outdent', !ed.queryCommandState('Outdent')); - - p = DOM.getParent(n, 'A'); - if (c = cm.get('link')) { - if (!p || !p.name) { - c.setDisabled(!p && co); - c.setActive(!!p); - } - } - - if (c = cm.get('unlink')) { - c.setDisabled(!p && co); - c.setActive(!!p && !p.name); - } - - if (c = cm.get('anchor')) { - c.setActive(!!p && p.name); - - if (tinymce.isWebKit) { - p = DOM.getParent(n, 'IMG'); - c.setActive(!!p && DOM.getAttrib(p, 'mce_name') == 'a'); - } - } - - p = DOM.getParent(n, 'IMG'); - if (c = cm.get('image')) - c.setActive(!!p && n.className.indexOf('mceItem') == -1); - - if (c = cm.get('styleselect')) { - if (n.className) { - t._importClasses(); - c.select(n.className); - } else - c.select(); - } - - if (c = cm.get('formatselect')) { - p = DOM.getParent(n, DOM.isBlock); - - if (p) - c.select(p.nodeName.toLowerCase()); - } - - if (ed.settings.convert_fonts_to_spans) { - ed.dom.getParent(n, function(n) { - if (n.nodeName === 'SPAN') { - if (!cl && n.className) - cl = n.className; - - if (!fz && n.style.fontSize) - fz = n.style.fontSize; - - if (!fn && n.style.fontFamily) - fn = n.style.fontFamily.replace(/[\"\']+/g, '').replace(/^([^,]+).*/, '$1').toLowerCase(); - } - - return false; - }); - - if (c = cm.get('fontselect')) { - c.select(function(v) { - return v.replace(/^([^,]+).*/, '$1').toLowerCase() == fn; - }); - } - - if (c = cm.get('fontsizeselect')) { - c.select(function(v) { - if (v.fontSize && v.fontSize === fz) - return true; - - if (v['class'] && v['class'] === cl) - return true; - }); - } - } else { - if (c = cm.get('fontselect')) - c.select(ed.queryCommandValue('FontName')); - - if (c = cm.get('fontsizeselect')) { - v = ed.queryCommandValue('FontSize'); - c.select(function(iv) { - return iv.fontSize == v; - }); - } - } - - if (s.theme_advanced_path && s.theme_advanced_statusbar_location) { - p = DOM.get(ed.id + '_path') || DOM.add(ed.id + '_path_row', 'span', {id : ed.id + '_path'}); - DOM.setHTML(p, ''); - - ed.dom.getParent(n, function(n) { - var na = n.nodeName.toLowerCase(), u, pi, ti = ''; - - // Ignore non element and hidden elements - if (n.nodeType != 1 || n.nodeName === 'BR' || (DOM.hasClass(n, 'mceItemHidden') || DOM.hasClass(n, 'mceItemRemoved'))) - return; - - // Fake name - if (v = DOM.getAttrib(n, 'mce_name')) - na = v; - - // Handle prefix - if (tinymce.isIE && n.scopeName !== 'HTML') - na = n.scopeName + ':' + na; - - // Remove internal prefix - na = na.replace(/mce\:/g, ''); - - // Handle node name - switch (na) { - case 'b': - na = 'strong'; - break; - - case 'i': - na = 'em'; - break; - - case 'img': - if (v = DOM.getAttrib(n, 'src')) - ti += 'src: ' + v + ' '; - - break; - - case 'a': - if (v = DOM.getAttrib(n, 'name')) { - ti += 'name: ' + v + ' '; - na += '#' + v; - } - - if (v = DOM.getAttrib(n, 'href')) - ti += 'href: ' + v + ' '; - - break; - - case 'font': - if (s.convert_fonts_to_spans) - na = 'span'; - - if (v = DOM.getAttrib(n, 'face')) - ti += 'font: ' + v + ' '; - - if (v = DOM.getAttrib(n, 'size')) - ti += 'size: ' + v + ' '; - - if (v = DOM.getAttrib(n, 'color')) - ti += 'color: ' + v + ' '; - - break; - - case 'span': - if (v = DOM.getAttrib(n, 'style')) - ti += 'style: ' + v + ' '; - - break; - } - - if (v = DOM.getAttrib(n, 'id')) - ti += 'id: ' + v + ' '; - - if (v = n.className) { - v = v.replace(/(webkit-[\w\-]+|Apple-[\w\-]+|mceItem\w+|mceVisualAid)/g, ''); - - if (v && v.indexOf('mceItem') == -1) { - ti += 'class: ' + v + ' '; - - if (DOM.isBlock(n) || na == 'img' || na == 'span') - na += '.' + v; - } - } - - na = na.replace(/(html:)/g, ''); - na = {name : na, node : n, title : ti}; - t.onResolveName.dispatch(t, na); - ti = na.title; - na = na.name; - - //u = "javascript:tinymce.EditorManager.get('" + ed.id + "').theme._sel('" + (de++) + "');"; - pi = DOM.create('a', {'href' : "javascript:;", onmousedown : "return false;", title : ti, 'class' : 'mcePath_' + (de++)}, na); - - if (p.hasChildNodes()) { - p.insertBefore(DOM.doc.createTextNode(' \u00bb '), p.firstChild); - p.insertBefore(pi, p.firstChild); - } else - p.appendChild(pi); - }, ed.getBody()); - } - }, - - // Commands gets called by execCommand - - _sel : function(v) { - this.editor.execCommand('mceSelectNodeDepth', false, v); - }, - - _mceInsertAnchor : function(ui, v) { - var ed = this.editor; - - ed.windowManager.open({ - url : tinymce.baseURL + '/themes/advanced/anchor.htm', - width : 320 + parseInt(ed.getLang('advanced.anchor_delta_width', 0)), - height : 90 + parseInt(ed.getLang('advanced.anchor_delta_height', 0)), - inline : true - }, { - theme_url : this.url - }); - }, - - _mceCharMap : function() { - var ed = this.editor; - - ed.windowManager.open({ - url : tinymce.baseURL + '/themes/advanced/charmap.htm', - width : 550 + parseInt(ed.getLang('advanced.charmap_delta_width', 0)), - height : 250 + parseInt(ed.getLang('advanced.charmap_delta_height', 0)), - inline : true - }, { - theme_url : this.url - }); - }, - - _mceHelp : function() { - var ed = this.editor; - - ed.windowManager.open({ - url : tinymce.baseURL + '/themes/advanced/about.htm', - width : 480, - height : 380, - inline : true - }, { - theme_url : this.url - }); - }, - - _mceColorPicker : function(u, v) { - var ed = this.editor; - - v = v || {}; - - ed.windowManager.open({ - url : tinymce.baseURL + '/themes/advanced/color_picker.htm', - width : 375 + parseInt(ed.getLang('advanced.colorpicker_delta_width', 0)), - height : 250 + parseInt(ed.getLang('advanced.colorpicker_delta_height', 0)), - close_previous : false, - inline : true - }, { - input_color : v.color, - func : v.func, - theme_url : this.url - }); - }, - - _mceCodeEditor : function(ui, val) { - var ed = this.editor; - - ed.windowManager.open({ - url : tinymce.baseURL + '/themes/advanced/source_editor.htm', - width : parseInt(ed.getParam("theme_advanced_source_editor_width", 720)), - height : parseInt(ed.getParam("theme_advanced_source_editor_height", 580)), - inline : true, - resizable : true, - maximizable : true - }, { - theme_url : this.url - }); - }, - - _mceImage : function(ui, val) { - var ed = this.editor; - - // Internal image object like a flash placeholder - if (ed.dom.getAttrib(ed.selection.getNode(), 'class').indexOf('mceItem') != -1) - return; - - ed.windowManager.open({ - url : tinymce.baseURL + '/themes/advanced/image.htm', - width : 355 + parseInt(ed.getLang('advanced.image_delta_width', 0)), - height : 275 + parseInt(ed.getLang('advanced.image_delta_height', 0)), - inline : true - }, { - theme_url : this.url - }); - }, - - _mceLink : function(ui, val) { - var ed = this.editor; - - ed.windowManager.open({ - url : tinymce.baseURL + '/themes/advanced/link.htm', - width : 310 + parseInt(ed.getLang('advanced.link_delta_width', 0)), - height : 200 + parseInt(ed.getLang('advanced.link_delta_height', 0)), - inline : true - }, { - theme_url : this.url - }); - }, - - _mceNewDocument : function() { - var ed = this.editor; - - ed.windowManager.confirm('advanced.newdocument', function(s) { - if (s) - ed.execCommand('mceSetContent', false, ''); - }); - }, - - _mceForeColor : function() { - var t = this; - - this._mceColorPicker(0, { - color: t.fgColor, - func : function(co) { - t.fgColor = co; - t.editor.execCommand('ForeColor', false, co); - } - }); - }, - - _mceBackColor : function() { - var t = this; - - this._mceColorPicker(0, { - color: t.bgColor, - func : function(co) { - t.bgColor = co; - t.editor.execCommand('HiliteColor', false, co); - } - }); - }, - - _ufirst : function(s) { - return s.substring(0, 1).toUpperCase() + s.substring(1); - } - }); - - tinymce.ThemeManager.add('advanced', tinymce.themes.AdvancedTheme); -}()); \ No newline at end of file +(function(e){var d=e.DOM,b=e.dom.Event,h=e.extend,f=e.each,a=e.util.Cookie,g,c=e.explode;e.ThemeManager.requireLangPack("advanced");e.create("tinymce.themes.AdvancedTheme",{sizes:[8,10,12,14,18,24,36],controls:{bold:["bold_desc","Bold"],italic:["italic_desc","Italic"],underline:["underline_desc","Underline"],strikethrough:["striketrough_desc","Strikethrough"],justifyleft:["justifyleft_desc","JustifyLeft"],justifycenter:["justifycenter_desc","JustifyCenter"],justifyright:["justifyright_desc","JustifyRight"],justifyfull:["justifyfull_desc","JustifyFull"],bullist:["bullist_desc","InsertUnorderedList"],numlist:["numlist_desc","InsertOrderedList"],outdent:["outdent_desc","Outdent"],indent:["indent_desc","Indent"],cut:["cut_desc","Cut"],copy:["copy_desc","Copy"],paste:["paste_desc","Paste"],undo:["undo_desc","Undo"],redo:["redo_desc","Redo"],link:["link_desc","mceLink"],unlink:["unlink_desc","unlink"],image:["image_desc","mceImage"],cleanup:["cleanup_desc","mceCleanup"],help:["help_desc","mceHelp"],code:["code_desc","mceCodeEditor"],hr:["hr_desc","InsertHorizontalRule"],removeformat:["removeformat_desc","RemoveFormat"],sub:["sub_desc","subscript"],sup:["sup_desc","superscript"],forecolor:["forecolor_desc","ForeColor"],forecolorpicker:["forecolor_desc","mceForeColor"],backcolor:["backcolor_desc","HiliteColor"],backcolorpicker:["backcolor_desc","mceBackColor"],charmap:["charmap_desc","mceCharMap"],visualaid:["visualaid_desc","mceToggleVisualAid"],anchor:["anchor_desc","mceInsertAnchor"],newdocument:["newdocument_desc","mceNewDocument"],blockquote:["blockquote_desc","mceBlockQuote"]},stateControls:["bold","italic","underline","strikethrough","bullist","numlist","justifyleft","justifycenter","justifyright","justifyfull","sub","sup","blockquote"],init:function(j,k){var l=this,m,i,n;l.editor=j;l.url=k;l.onResolveName=new e.util.Dispatcher(this);l.settings=m=h({theme_advanced_path:true,theme_advanced_toolbar_location:"bottom",theme_advanced_buttons1:"bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect",theme_advanced_buttons2:"bullist,numlist,|,outdent,indent,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code",theme_advanced_buttons3:"hr,removeformat,visualaid,|,sub,sup,|,charmap",theme_advanced_blockformats:"p,address,pre,h1,h2,h3,h4,h5,h6",theme_advanced_toolbar_align:"center",theme_advanced_fonts:"Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats",theme_advanced_more_colors:1,theme_advanced_row_height:23,theme_advanced_resize_horizontal:1,theme_advanced_resizing_use_cookie:1,theme_advanced_font_sizes:"1,2,3,4,5,6,7",readonly:j.settings.readonly},j.settings);if(!m.font_size_style_values){m.font_size_style_values="8pt,10pt,12pt,14pt,18pt,24pt,36pt"}if(e.is(m.theme_advanced_font_sizes,"string")){m.font_size_style_values=e.explode(m.font_size_style_values);m.font_size_classes=e.explode(m.font_size_classes||"");n={};j.settings.theme_advanced_font_sizes=m.theme_advanced_font_sizes;f(j.getParam("theme_advanced_font_sizes","","hash"),function(q,p){var o;if(p==q&&q>=1&&q<=7){p=q+" ("+l.sizes[q-1]+"pt)";o=m.font_size_classes[q-1];q=m.font_size_style_values[q-1]||(l.sizes[q-1]+"pt")}if(/^\s*\./.test(q)){o=q.replace(/\./g,"")}n[p]=o?{"class":o}:{fontSize:q}});m.theme_advanced_font_sizes=n}if((i=m.theme_advanced_path_location)&&i!="none"){m.theme_advanced_statusbar_location=m.theme_advanced_path_location}if(m.theme_advanced_statusbar_location=="none"){m.theme_advanced_statusbar_location=0}j.onInit.add(function(){if(!j.settings.readonly){j.onNodeChange.add(l._nodeChanged,l)}if(j.settings.content_css!==false){j.dom.loadCSS(j.baseURI.toAbsolute(k+"/skins/"+j.settings.skin+"/content.css"))}});j.onSetProgressState.add(function(q,o,r){var s,t=q.id,p;if(o){l.progressTimer=setTimeout(function(){s=q.getContainer();s=s.insertBefore(d.create("DIV",{style:"position:relative"}),s.firstChild);p=d.get(q.id+"_tbl");d.add(s,"div",{id:t+"_blocker","class":"mceBlocker",style:{width:p.clientWidth+2,height:p.clientHeight+2}});d.add(s,"div",{id:t+"_progress","class":"mceProgress",style:{left:p.clientWidth/2,top:p.clientHeight/2}})},r||0)}else{d.remove(t+"_blocker");d.remove(t+"_progress");clearTimeout(l.progressTimer)}});d.loadCSS(m.editor_css?j.documentBaseURI.toAbsolute(m.editor_css):k+"/skins/"+j.settings.skin+"/ui.css");if(m.skin_variant){d.loadCSS(k+"/skins/"+j.settings.skin+"/ui_"+m.skin_variant+".css")}},createControl:function(l,i){var j,k;if(k=i.createControl(l)){return k}switch(l){case"styleselect":return this._createStyleSelect();case"formatselect":return this._createBlockFormats();case"fontselect":return this._createFontSelect();case"fontsizeselect":return this._createFontSizeSelect();case"forecolor":return this._createForeColorMenu();case"backcolor":return this._createBackColorMenu()}if((j=this.controls[l])){return i.createButton(l,{title:"advanced."+j[0],cmd:j[1],ui:j[2],value:j[3]})}},execCommand:function(k,j,l){var i=this["_"+k];if(i){i.call(this,j,l);return true}return false},_importClasses:function(k){var i=this.editor,j=i.controlManager.get("styleselect");if(j.getLength()==0){f(i.dom.getClasses(),function(n,l){var m="style_"+l;i.formatter.register(m,{inline:"span",attributes:{"class":n["class"]},selector:"*"});j.add(n["class"],m)})}},_createStyleSelect:function(m){var k=this,i=k.editor,j=i.controlManager,l;l=j.createListBox("styleselect",{title:"advanced.style_select",onselect:function(o){var p,n=[];f(l.items,function(q){n.push(q.value)});i.focus();i.undoManager.add();p=i.formatter.matchAll(n);if(!o||p[0]==o){i.formatter.remove(p[0])}else{i.formatter.apply(o)}i.undoManager.add();i.nodeChanged();return false}});i.onInit.add(function(){var o=0,n=i.getParam("style_formats");if(n){f(n,function(p){var q,r=0;f(p,function(){r++});if(r>1){q=p.name=p.name||"style_"+(o++);i.formatter.register(q,p);l.add(p.title,q)}else{l.add(p.title)}})}else{f(i.getParam("theme_advanced_styles","","hash"),function(r,q){var p;if(r){p="style_"+(o++);i.formatter.register(p,{inline:"span",classes:r,selector:"*"});l.add(k.editor.translate(q),p)}})}});if(l.getLength()==0){l.onPostRender.add(function(o,p){if(!l.NativeListBox){b.add(p.id+"_text","focus",k._importClasses,k);b.add(p.id+"_text","mousedown",k._importClasses,k);b.add(p.id+"_open","focus",k._importClasses,k);b.add(p.id+"_open","mousedown",k._importClasses,k)}else{b.add(p.id,"focus",k._importClasses,k)}})}return l},_createFontSelect:function(){var k,j=this,i=j.editor;k=i.controlManager.createListBox("fontselect",{title:"advanced.fontdefault",onselect:function(l){var m=k.items[k.selectedIndex];if(!l&&m){i.execCommand("FontName",false,m.value);return}i.execCommand("FontName",false,l);k.select(function(n){return l==n});return false}});if(k){f(i.getParam("theme_advanced_fonts",j.settings.theme_advanced_fonts,"hash"),function(m,l){k.add(i.translate(l),m,{style:m.indexOf("dings")==-1?"font-family:"+m:""})})}return k},_createFontSizeSelect:function(){var m=this,k=m.editor,n,l=0,j=[];n=k.controlManager.createListBox("fontsizeselect",{title:"advanced.font_size",onselect:function(i){var o=n.items[n.selectedIndex];if(!i&&o){o=o.value;if(o["class"]){k.formatter.toggle("fontsize_class",{value:o["class"]});k.undoManager.add();k.nodeChanged()}else{k.execCommand("FontSize",false,o.fontSize)}return}if(i["class"]){k.focus();k.undoManager.add();k.formatter.toggle("fontsize_class",{value:i["class"]});k.undoManager.add();k.nodeChanged()}else{k.execCommand("FontSize",false,i.fontSize)}n.select(function(p){return i==p});return false}});if(n){f(m.settings.theme_advanced_font_sizes,function(o,i){var p=o.fontSize;if(p>=1&&p<=7){p=m.sizes[parseInt(p)-1]+"pt"}n.add(i,o,{style:"font-size:"+p,"class":"mceFontSize"+(l++)+(" "+(o["class"]||""))})})}return n},_createBlockFormats:function(){var k,i={p:"advanced.paragraph",address:"advanced.address",pre:"advanced.pre",h1:"advanced.h1",h2:"advanced.h2",h3:"advanced.h3",h4:"advanced.h4",h5:"advanced.h5",h6:"advanced.h6",div:"advanced.div",blockquote:"advanced.blockquote",code:"advanced.code",dt:"advanced.dt",dd:"advanced.dd",samp:"advanced.samp"},j=this;k=j.editor.controlManager.createListBox("formatselect",{title:"advanced.block",cmd:"FormatBlock"});if(k){f(j.editor.getParam("theme_advanced_blockformats",j.settings.theme_advanced_blockformats,"hash"),function(m,l){k.add(j.editor.translate(l!=m?l:i[m]),m,{"class":"mce_formatPreview mce_"+m})})}return k},_createForeColorMenu:function(){var m,j=this,k=j.settings,l={},i;if(k.theme_advanced_more_colors){l.more_colors_func=function(){j._mceColorPicker(0,{color:m.value,func:function(n){m.setColor(n)}})}}if(i=k.theme_advanced_text_colors){l.colors=i}if(k.theme_advanced_default_foreground_color){l.default_color=k.theme_advanced_default_foreground_color}l.title="advanced.forecolor_desc";l.cmd="ForeColor";l.scope=this;m=j.editor.controlManager.createColorSplitButton("forecolor",l);return m},_createBackColorMenu:function(){var m,j=this,k=j.settings,l={},i;if(k.theme_advanced_more_colors){l.more_colors_func=function(){j._mceColorPicker(0,{color:m.value,func:function(n){m.setColor(n)}})}}if(i=k.theme_advanced_background_colors){l.colors=i}if(k.theme_advanced_default_background_color){l.default_color=k.theme_advanced_default_background_color}l.title="advanced.backcolor_desc";l.cmd="HiliteColor";l.scope=this;m=j.editor.controlManager.createColorSplitButton("backcolor",l);return m},renderUI:function(k){var m,l,q,v=this,r=v.editor,w=v.settings,u,j,i;m=j=d.create("span",{id:r.id+"_parent","class":"mceEditor "+r.settings.skin+"Skin"+(w.skin_variant?" "+r.settings.skin+"Skin"+v._ufirst(w.skin_variant):"")});if(!d.boxModel){m=d.add(m,"div",{"class":"mceOldBoxModel"})}m=u=d.add(m,"table",{id:r.id+"_tbl","class":"mceLayout",cellSpacing:0,cellPadding:0});m=q=d.add(m,"tbody");switch((w.theme_advanced_layout_manager||"").toLowerCase()){case"rowlayout":l=v._rowLayout(w,q,k);break;case"customlayout":l=r.execCallback("theme_advanced_custom_layout",w,q,k,j);break;default:l=v._simpleLayout(w,q,k,j)}m=k.targetNode;i=d.stdMode?u.getElementsByTagName("tr"):u.rows;d.addClass(i[0],"mceFirst");d.addClass(i[i.length-1],"mceLast");f(d.select("tr",q),function(o){d.addClass(o.firstChild,"mceFirst");d.addClass(o.childNodes[o.childNodes.length-1],"mceLast")});if(d.get(w.theme_advanced_toolbar_container)){d.get(w.theme_advanced_toolbar_container).appendChild(j)}else{d.insertAfter(j,m)}b.add(r.id+"_path_row","click",function(n){n=n.target;if(n.nodeName=="A"){v._sel(n.className.replace(/^.*mcePath_([0-9]+).*$/,"$1"));return b.cancel(n)}});if(!r.getParam("accessibility_focus")){b.add(d.add(j,"a",{href:"#"},""),"focus",function(){tinyMCE.get(r.id).focus()})}if(w.theme_advanced_toolbar_location=="external"){k.deltaHeight=0}v.deltaHeight=k.deltaHeight;k.targetNode=null;return{iframeContainer:l,editorContainer:r.id+"_parent",sizeContainer:u,deltaHeight:k.deltaHeight}},getInfo:function(){return{longname:"Advanced theme",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",version:e.majorVersion+"."+e.minorVersion}},resizeBy:function(i,j){var k=d.get(this.editor.id+"_tbl");this.resizeTo(k.clientWidth+i,k.clientHeight+j)},resizeTo:function(i,m,k){var j=this.editor,l=this.settings,n=d.get(j.id+"_tbl"),o=d.get(j.id+"_ifr");i=Math.max(l.theme_advanced_resizing_min_width||100,i);m=Math.max(l.theme_advanced_resizing_min_height||100,m);i=Math.min(l.theme_advanced_resizing_max_width||65535,i);m=Math.min(l.theme_advanced_resizing_max_height||65535,m);d.setStyle(n,"height","");d.setStyle(o,"height",m);if(l.theme_advanced_resize_horizontal){d.setStyle(n,"width","");d.setStyle(o,"width",i);if(i"))}q.push(d.createHTML("a",{href:"#",accesskey:"q",title:r.getLang("advanced.toolbar_focus")},""));for(p=1;(y=A["theme_advanced_buttons"+p]);p++){m=j.createToolbar("toolbar"+p,{"class":"mceToolbarRow"+p});if(A["theme_advanced_buttons"+p+"_add"]){y+=","+A["theme_advanced_buttons"+p+"_add"]}if(A["theme_advanced_buttons"+p+"_add_before"]){y=A["theme_advanced_buttons"+p+"_add_before"]+","+y}z._addControls(y,m);q.push(m.renderHTML());k.deltaHeight-=A.theme_advanced_row_height}q.push(d.createHTML("a",{href:"#",accesskey:"z",title:r.getLang("advanced.toolbar_focus"),onfocus:"tinyMCE.getInstanceById('"+r.id+"').focus();"},""));d.setHTML(l,q.join(""))},_addStatusBar:function(m,j){var k,v=this,p=v.editor,w=v.settings,i,q,u,l;k=d.add(m,"tr");k=l=d.add(k,"td",{"class":"mceStatusbar"});k=d.add(k,"div",{id:p.id+"_path_row"},w.theme_advanced_path?p.translate("advanced.path")+": ":" ");d.add(k,"a",{href:"#",accesskey:"x"});if(w.theme_advanced_resizing){d.add(l,"a",{id:p.id+"_resize",href:"javascript:;",onclick:"return false;","class":"mceResize"});if(w.theme_advanced_resizing_use_cookie){p.onPostRender.add(function(){var n=a.getHash("TinyMCE_"+p.id+"_size"),r=d.get(p.id+"_tbl");if(!n){return}v.resizeTo(n.cw,n.ch)})}p.onPostRender.add(function(){b.add(p.id+"_resize","click",function(n){n.preventDefault()});b.add(p.id+"_resize","mousedown",function(D){var t,r,s,o,C,z,A,F,n,E,x;function y(G){G.preventDefault();n=A+(G.screenX-C);E=F+(G.screenY-z);v.resizeTo(n,E)}function B(G){b.remove(d.doc,"mousemove",t);b.remove(p.getDoc(),"mousemove",r);b.remove(d.doc,"mouseup",s);b.remove(p.getDoc(),"mouseup",o);n=A+(G.screenX-C);E=F+(G.screenY-z);v.resizeTo(n,E,true)}D.preventDefault();C=D.screenX;z=D.screenY;x=d.get(v.editor.id+"_ifr");A=n=x.clientWidth;F=E=x.clientHeight;t=b.add(d.doc,"mousemove",y);r=b.add(p.getDoc(),"mousemove",y);s=b.add(d.doc,"mouseup",B);o=b.add(p.getDoc(),"mouseup",B)})})}j.deltaHeight-=21;k=m=null},_nodeChanged:function(r,z,l,x,j){var C=this,i,y=0,B,u,D=C.settings,A,k,w,m,q;e.each(C.stateControls,function(n){z.setActive(n,r.queryCommandState(C.controls[n][1]))});function o(p){var s,n=j.parents,t=p;if(typeof(p)=="string"){t=function(v){return v.nodeName==p}}for(s=0;s - @@ -20,7 +19,7 @@
- +
@@ -33,11 +32,11 @@ - + - + - + - + - + - +
x
@@ -73,13 +72,8 @@
-
- -
- -
- -
+ +
diff --git a/chrome/content/zotero/tinymce/themes/advanced/img/icons.gif b/chrome/content/zotero/tinymce/themes/advanced/img/icons.gif index ccac36f54d983cc33a0a6e45134e3dabf1820367..e46de5333082869b9bdab2576a554a2f9d01a966 100755 GIT binary patch delta 556 zcmV+{0@MBRS(03^@+*J4nMj91K!6CCg;~%|ckg$9|95~7c!3}ITCag#i1+#^_l8J< zMz{hi-~mn`f&WkNUxKfAi@$h`&v=cmcm(hTgr^UBX9yQ4ge$;@S9v2sc!Q7Jc$RN@ zmw$PfkNI8?cZ4VRnMi^w;DBhrhc`+RN=Se=h;o@9dZI6Sqd$Lor0*6Nojs;s`lY}5 zD*y%?AcQ30fmnctx}x}`-+Hd^dawU_qECWAD1>z20BE=b8{mXb!1_C|f>=NVu#bDW zpL@Em`(u{{S@;LM$9uis`@ZLUzCVa0SOOTmgjL`H5`YKAhX)~m2gb8|$d7!eJ7C-QRuh-Zy%KS7+c?_=Av3*=JNnJy~rK`rcoD=5K!I_jd%qhmU`NkWb!Nz)#vo z{#n5N=kI>+|9GLPhphAZdEo$^A(xgh4GHvPh&Zm*sx;9k}YfYEZVec*RpNv_AT7Fa_7>mYxgeRym~JN1OPj2L>nUj delta 265 zcmV+k0rviqT=7}3@+*HkcdvJQzju7kcYWXYVXuK+i1+#^cYYsuf-iW3KX`;s`0DTl zfTs_7S9pkzc+QERc#5z1V-I(LCwGhAc#iLQkN#4PkEJJd6sW^ zmw$Pfk9nD&d1sdeS@;K=w|Sh;`JLB!ou_%A4|<^=dZI6Sqd$Loq)&RKPxg{;dXt~M zrH^{4pL(i?d4E@DtQYvI-+Hd^daoaN1i*)ee}ISodb2-!v`>3%$M|^Hc(s3fxQ~0e zA9i?eczh`PxzBsO-+R74bblXvt?zrmAAG_u{BA)2hHrShH+;r#e8+!$$d7! -
@@ -22,7 +21,7 @@ - +
@@ -39,7 +38,7 @@ - + @@ -51,13 +50,8 @@
-
- -
- -
- -
+ +
diff --git a/chrome/content/zotero/tinymce/themes/advanced/skins/default/content.css b/chrome/content/zotero/tinymce/themes/advanced/skins/default/content.css index 19da1943bc..9fba0431da 100755 --- a/chrome/content/zotero/tinymce/themes/advanced/skins/default/content.css +++ b/chrome/content/zotero/tinymce/themes/advanced/skins/default/content.css @@ -8,8 +8,9 @@ h4 {font-size: 1em} h5 {font-size: .83em} h6 {font-size: .75em} .mceItemTable, .mceItemTable td, .mceItemTable th, .mceItemTable caption, .mceItemVisualAid {border: 1px dashed #BBB;} -a.mceItemAnchor {width:12px; line-height:6px; overflow:hidden; padding-left:12px; background:url(img/items.gif) no-repeat bottom left;} -img.mceItemAnchor {width:12px; height:12px; background:url(img/items.gif) no-repeat;} +a.mceItemAnchor {display:inline-block; width:11px !important; height:11px !important; background:url(img/items.gif) no-repeat 0 0;} +span.mceItemNbsp {background: #DDD} +td.mceSelected, th.mceSelected {background-color:#3399ff !important} img {border:0;} table {cursor:default} table td, table th {cursor:text} @@ -17,7 +18,7 @@ ins {border-bottom:1px solid green; text-decoration: none; color:green} del {color:red; text-decoration:line-through} cite {border-bottom:1px dashed blue} acronym {border-bottom:1px dotted #CCC; cursor:help} -abbr, html\:abbr {border-bottom:1px dashed #CCC; cursor:help} +abbr {border-bottom:1px dashed #CCC; cursor:help} /* IE */ * html body { @@ -30,3 +31,6 @@ scrollbar-highlight-color:#F0F0EE; scrollbar-shadow-color:#F0F0EE; scrollbar-track-color:#F5F5F5; } + +img:-moz-broken {-moz-force-broken-image-icon:1; width:24px; height:24px} +font[face=mceinline] {font-family:inherit !important} diff --git a/chrome/content/zotero/tinymce/themes/advanced/skins/default/dialog.css b/chrome/content/zotero/tinymce/themes/advanced/skins/default/dialog.css index c944a60ba0..f01222650e 100755 --- a/chrome/content/zotero/tinymce/themes/advanced/skins/default/dialog.css +++ b/chrome/content/zotero/tinymce/themes/advanced/skins/default/dialog.css @@ -19,6 +19,7 @@ td {font-family:Verdana, Arial, Helvetica, sans-serif; font-size:10px;} textarea {resize:none;outline:none;} a:link, a:visited {color:black;} a:hover {color:#2B6FB6;} +.nowrap {white-space: nowrap} /* Forms */ fieldset {margin:0; padding:4px; border:1px solid #919B9C; font-family:Verdana, Arial; font-size:10px;} @@ -41,16 +42,18 @@ width:94px; height:26px; background:url(img/buttons.png) 0 -26px; cursor:pointer; padding-bottom:2px; +float:left; } -#insert {background:url(img/buttons.png) 0 -52px;} -#cancel {background:url(img/buttons.png) 0 0;} +#insert {background:url(img/buttons.png) 0 -52px} +#cancel {background:url(img/buttons.png) 0 0; float:right} /* Browse */ +a.pickcolor, a.browse {text-decoration:none} a.browse span {display:block; width:20px; height:18px; background:url(../../img/icons.gif) -860px 0; border:1px solid #FFF; margin-left:1px;} .mceOldBoxModel a.browse span {width:22px; height:20px;} a.browse:hover span {border:1px solid #0A246A; background-color:#B2BBD0;} -a.browse span.disabled {border:1px solid white; -moz-opacity:0.3; opacity:0.3; filter:progid:DXImageTransform.Microsoft.Alpha(opacity=30);} +a.browse span.disabled {border:1px solid white; opacity:0.3; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=30)} a.browse:hover span.disabled {border:1px solid white; background-color:transparent;} a.pickcolor span {display:block; width:20px; height:16px; background:url(../../img/icons.gif) -840px 0; margin-left:2px;} .mceOldBoxModel a.pickcolor span {width:21px; height:17px;} @@ -111,4 +114,4 @@ h3 {font-size:14px;} #colorpicker #namedcolors {width:150px;} #colorpicker #namedcolors a {display:block; float:left; width:10px; height:10px; margin:1px 1px 0 0; overflow:hidden;} #colorpicker #colornamecontainer {margin-top:5px;} -#colorpicker #picker_panel fieldset {margin:auto;width:325px;} \ No newline at end of file +#colorpicker #picker_panel fieldset {margin:auto;width:325px;} diff --git a/chrome/content/zotero/tinymce/themes/advanced/skins/default/ui.css b/chrome/content/zotero/tinymce/themes/advanced/skins/default/ui.css index e2aec12e3b..0049c7b3d0 100755 --- a/chrome/content/zotero/tinymce/themes/advanced/skins/default/ui.css +++ b/chrome/content/zotero/tinymce/themes/advanced/skins/default/ui.css @@ -4,7 +4,7 @@ .defaultSkin table td {vertical-align:middle} /* Containers */ -.defaultSkin table {background:#F0F0EE} +.defaultSkin table {direction:ltr; background:#F0F0EE} .defaultSkin iframe {display:block; background:#FFF} .defaultSkin .mceToolbar {height:26px} .defaultSkin .mceLeft {text-align:left} @@ -24,7 +24,7 @@ .defaultSkin .mceIframeContainer {border-top:1px solid #CCC; border-bottom:1px solid #CCC} .defaultSkin .mceStatusbar {font-family:'MS Sans Serif',sans-serif,Verdana,Arial; font-size:9pt; line-height:16px; overflow:visible; color:#000; display:block; height:20px} .defaultSkin .mceStatusbar div {float:left; margin:2px} -.defaultSkin .mceStatusbar a.mceResize {display:block; float:right; background:url(../../img/icons.gif) -800px 0; width:20px; height:20px; cursor:se-resize} +.defaultSkin .mceStatusbar a.mceResize {display:block; float:right; background:url(../../img/icons.gif) -800px 0; width:20px; height:20px; cursor:se-resize; outline:0} .defaultSkin .mceStatusbar a:hover {text-decoration:underline} .defaultSkin table.mceToolbar {margin-left:3px} .defaultSkin span.mceIcon, .defaultSkin img.mceIcon {display:block; width:20px; height:20px} @@ -37,7 +37,7 @@ .defaultSkin .mceButton {display:block; border:1px solid #F0F0EE; width:20px; height:20px; margin-right:1px} .defaultSkin a.mceButtonEnabled:hover {border:1px solid #0A246A; background-color:#B2BBD0} .defaultSkin a.mceButtonActive, .defaultSkin a.mceButtonSelected {border:1px solid #0A246A; background-color:#C2CBE0} -.defaultSkin .mceButtonDisabled .mceIcon {opacity:0.3; filter:alpha(opacity=30)} +.defaultSkin .mceButtonDisabled .mceIcon {opacity:0.3; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=30)} .defaultSkin .mceButtonLabeled {width:auto} .defaultSkin .mceButtonLabeled span.mceIcon {float:left} .defaultSkin span.mceButtonLabel {display:block; font-size:10px; padding:4px 6px 0 22px; font-family:Tahoma,Verdana,Arial,Helvetica} @@ -47,12 +47,9 @@ .defaultSkin .mceSeparator {display:block; background:url(../../img/icons.gif) -180px 0; width:2px; height:20px; margin:2px 2px 0 4px} /* ListBox */ -.defaultSkin .mceListBox {direction:ltr} .defaultSkin .mceListBox, .defaultSkin .mceListBox a {display:block} -/* width changed to 65px by Dan S./Zotero */ -.defaultSkin .mceListBox .mceText {padding-left:4px; width:65px; text-align:left; border:1px solid #CCC; border-right:0; background:#FFF; font-family:Tahoma,Verdana,Arial,Helvetica; font-size:11px; height:20px; line-height:20px; overflow:hidden} -/* margin-right changed to 1px by Dan S./Zotero */ -.defaultSkin .mceListBox .mceOpen {width:9px; height:20px; background:url(../../img/icons.gif) -741px 0; margin-right:1px; border:1px solid #CCC;} +.defaultSkin .mceListBox .mceText {padding-left:4px; width:70px; text-align:left; border:1px solid #CCC; border-right:0; background:#FFF; font-family:Tahoma,Verdana,Arial,Helvetica; font-size:11px; height:20px; line-height:20px; overflow:hidden} +.defaultSkin .mceListBox .mceOpen {width:9px; height:20px; background:url(../../img/icons.gif) -741px 0; margin-right:2px; border:1px solid #CCC;} .defaultSkin table.mceListBoxEnabled:hover .mceText, .defaultSkin .mceListBoxHover .mceText, .defaultSkin .mceListBoxSelected .mceText {border:1px solid #A2ABC0; border-right:0; background:#FFF} .defaultSkin table.mceListBoxEnabled:hover .mceOpen, .defaultSkin .mceListBoxHover .mceOpen, .defaultSkin .mceListBoxSelected .mceOpen {background-color:#FFF; border:1px solid #A2ABC0} .defaultSkin .mceListBoxDisabled a.mceText {color:gray; background-color:transparent;} @@ -65,13 +62,12 @@ .defaultSkin .mceSplitButton {width:32px; height:20px; direction:ltr} .defaultSkin .mceSplitButton a, .defaultSkin .mceSplitButton span {height:20px; display:block} .defaultSkin .mceSplitButton a.mceAction {width:20px; border:1px solid #F0F0EE; border-right:0;} -.defaultSkin .mceSplitButton span.mceAction {width:20px; background:url(../../img/icons.gif) 20px 20px;} -.defaultSkin .mceSplitButton a.mceOpen {width:9px; border:1px solid #F0F0EE;} -.defaultSkin .mceSplitButton span.mceOpen {width:9px; background:url(../../img/icons.gif) -741px 0;} +.defaultSkin .mceSplitButton span.mceAction {width:20px; background-image:url(../../img/icons.gif);} +.defaultSkin .mceSplitButton a.mceOpen {width:9px; background:url(../../img/icons.gif) -741px 0; border:1px solid #F0F0EE;} +.defaultSkin .mceSplitButton span.mceOpen {display:none} .defaultSkin table.mceSplitButtonEnabled:hover a.mceAction, .defaultSkin .mceSplitButtonHover a.mceAction, .defaultSkin .mceSplitButtonSelected a.mceAction {border:1px solid #0A246A; border-right:0; background-color:#B2BBD0} -.defaultSkin table.mceSplitButtonEnabled:hover a.mceOpen, .defaultSkin .mceSplitButtonHover a.mceOpen, .defaultSkin .mceSplitButtonSelected a.mceOpen {border:1px solid #0A246A;} -.defaultSkin table.mceSplitButtonEnabled:hover span.mceOpen, .defaultSkin .mceSplitButtonHover span.mceOpen, .defaultSkin .mceSplitButtonSelected span.mceOpen {background-color:#B2BBD0} -.defaultSkin .mceSplitButtonDisabled .mceAction, .defaultSkin .mceSplitButtonDisabled span.mceOpen {opacity:0.3; filter:alpha(opacity=30)} +.defaultSkin table.mceSplitButtonEnabled:hover a.mceOpen, .defaultSkin .mceSplitButtonHover a.mceOpen, .defaultSkin .mceSplitButtonSelected a.mceOpen {background-color:#B2BBD0; border:1px solid #0A246A;} +.defaultSkin .mceSplitButtonDisabled .mceAction, .defaultSkin .mceSplitButtonDisabled a.mceOpen {opacity:0.3; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=30)} .defaultSkin .mceSplitButtonActive a.mceAction {border:1px solid #0A246A; background-color:#C2CBE0} .defaultSkin .mceSplitButtonActive a.mceOpen {border-left:0;} @@ -109,9 +105,8 @@ .defaultSkin .mceMenuItemSub a {background:url(img/menu_arrow.gif) no-repeat top right;} /* Progress,Resize */ -.defaultSkin .mceBlocker {position:absolute; left:0; top:0; z-index:1000; opacity:0.5; filter:alpha(opacity=50); background:#FFF} +.defaultSkin .mceBlocker {position:absolute; left:0; top:0; z-index:1000; opacity:0.5; -ms-filter:'alpha(opacity=50)'; filter:alpha(opacity=50); background:#FFF} .defaultSkin .mceProgress {position:absolute; left:0; top:0; z-index:1001; background:url(img/progress.gif) no-repeat; width:32px; height:32px; margin:-16px 0 0 -16px} -.defaultSkin .mcePlaceHolder {border:1px dotted gray} /* Formats */ .defaultSkin .mce_formatPreview a {font-size:10px} @@ -214,4 +209,5 @@ .defaultSkin span.mce_del {background-position:-940px -20px} .defaultSkin span.mce_ins {background-position:-960px -20px} .defaultSkin span.mce_pagebreak {background-position:0 -40px} -.defaultSkin .mce_spellchecker span.mceAction {background-position:-540px -20px} +.defaultSkin span.mce_restoredraft {background-position:-20px -40px} +.defaultSkin span.mce_spellchecker {background-position:-540px -20px} diff --git a/chrome/content/zotero/tinymce/themes/advanced/source_editor.htm b/chrome/content/zotero/tinymce/themes/advanced/source_editor.htm index 119a913c93..5957bbd178 100755 --- a/chrome/content/zotero/tinymce/themes/advanced/source_editor.htm +++ b/chrome/content/zotero/tinymce/themes/advanced/source_editor.htm @@ -1,10 +1,8 @@ - {#advanced_dlg.code_title} - @@ -19,13 +17,8 @@
-
- -
- -
- -
+ +
diff --git a/chrome/content/zotero/tinymce/tiny_mce.js b/chrome/content/zotero/tinymce/tiny_mce.js index c48b13ada9..c1c868759b 100755 --- a/chrome/content/zotero/tinymce/tiny_mce.js +++ b/chrome/content/zotero/tinymce/tiny_mce.js @@ -1,432 +1,468 @@ -/* - * Contains modifications by Zotero (commented) - */ -var tinymce = { - majorVersion : '3', - minorVersion : '2.5', - releaseDate : '2009-06-29', +(function(win) { + var whiteSpaceRe = /^\s*|\s*$/g, + undefined, isRegExpBroken = 'B'.replace(/A(.)|B/, '$1') === '$1'; - _init : function() { - var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base, p, v; + var tinymce = { + majorVersion : '3', - // Browser checks - t.isOpera = w.opera && opera.buildNumber; - t.isWebKit = /WebKit/.test(ua); - 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); + minorVersion : '3.9.2', - // TinyMCE .NET webcontrol might be setting the values for TinyMCE - if (w.tinyMCEPreInit) { - t.suffix = tinyMCEPreInit.suffix; - t.baseURL = tinyMCEPreInit.base; - t.query = tinyMCEPreInit.query; - return; - } + releaseDate : '2010-09-29', - // Get suffix and base - t.suffix = ''; + _init : function() { + var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v; - // If base element found, add that infront of baseURL - nl = d.getElementsByTagName('base'); - for (i=0; i : - s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s); - cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name - - // Create namespace for new class - ns = t.createNS(s[3].replace(/\.\w+$/, '')); - - // Class already exists - if (ns[cn]) - return; - - // Make pure static class - if (s[2] == 'static') { - ns[cn] = p; - - if (this.onCreate) - this.onCreate(s[2], s[3], ns[cn]); - - return; - } - - // Create default constructor - if (!p[cn]) { - p[cn] = function() {}; - de = 1; - } - - // Add constructor and methods - ns[cn] = p[cn]; - t.extend(ns[cn].prototype, p); - - // Extend - if (s[5]) { - sp = t.resolve(s[5]).prototype; - scn = s[5].match(/\.(\w+)$/i)[1]; // Class name - - // Extend constructor - c = ns[cn]; - if (de) { - // Add passthrough constructor - ns[cn] = function() { - return sp[scn].apply(this, arguments); - }; } else { - // Add inherit constructor - ns[cn] = function() { - this.parent = sp[scn]; - return c.apply(this, arguments); - }; + // Hashtables + for (n in o) { + if (o.hasOwnProperty(n)) { + if (cb.call(s, o[n], n, o) === false) + return 0; + } + } } - ns[cn].prototype[cn] = ns[cn]; - // Add super methods - t.each(sp, function(f, n) { - ns[cn].prototype[n] = sp[n]; + return 1; + }, + + + map : function(a, f) { + var o = []; + + tinymce.each(a, function(v) { + o.push(f(v)); }); - // Add overridden methods - t.each(p, function(f, n) { - // Extend methods if needed - if (sp[n]) { - ns[cn].prototype[n] = function() { - this.parent = sp[n]; - return f.apply(this, arguments); + return o; + }, + + grep : function(a, f) { + var o = []; + + tinymce.each(a, function(v) { + if (!f || f(v)) + o.push(v); + }); + + return o; + }, + + inArray : function(a, v) { + var i, l; + + if (a) { + for (i = 0, l = a.length; i < l; i++) { + if (a[i] === v) + return i; + } + } + + return -1; + }, + + extend : function(o, e) { + var i, l, a = arguments; + + for (i = 1, l = a.length; i < l; i++) { + e = a[i]; + + tinymce.each(e, function(v, n) { + if (v !== undefined) + o[n] = v; + }); + } + + return o; + }, + + + trim : function(s) { + return (s ? '' + s : '').replace(whiteSpaceRe, ''); + }, + + create : function(s, p) { + var t = this, sp, ns, cn, scn, c, de = 0; + + // Parse : : + s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s); + cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name + + // Create namespace for new class + ns = t.createNS(s[3].replace(/\.\w+$/, '')); + + // Class already exists + if (ns[cn]) + return; + + // Make pure static class + if (s[2] == 'static') { + ns[cn] = p; + + if (this.onCreate) + this.onCreate(s[2], s[3], ns[cn]); + + return; + } + + // Create default constructor + if (!p[cn]) { + p[cn] = function() {}; + de = 1; + } + + // Add constructor and methods + ns[cn] = p[cn]; + t.extend(ns[cn].prototype, p); + + // Extend + if (s[5]) { + sp = t.resolve(s[5]).prototype; + scn = s[5].match(/\.(\w+)$/i)[1]; // Class name + + // Extend constructor + c = ns[cn]; + if (de) { + // Add passthrough constructor + ns[cn] = function() { + return sp[scn].apply(this, arguments); }; } else { - if (n != cn) - ns[cn].prototype[n] = f; + // Add inherit constructor + ns[cn] = function() { + this.parent = sp[scn]; + return c.apply(this, arguments); + }; + } + ns[cn].prototype[cn] = ns[cn]; + + // Add super methods + t.each(sp, function(f, n) { + ns[cn].prototype[n] = sp[n]; + }); + + // Add overridden methods + t.each(p, function(f, n) { + // Extend methods if needed + if (sp[n]) { + ns[cn].prototype[n] = function() { + this.parent = sp[n]; + return f.apply(this, arguments); + }; + } else { + if (n != cn) + ns[cn].prototype[n] = f; + } + }); + } + + // Add static methods + t.each(p['static'], function(f, n) { + ns[cn][n] = f; + }); + + if (this.onCreate) + this.onCreate(s[2], s[3], ns[cn].prototype); + }, + + walk : function(o, f, n, s) { + s = s || this; + + if (o) { + if (n) + o = o[n]; + + tinymce.each(o, function(o, i) { + if (f.call(s, o, i, n) === false) + return false; + + tinymce.walk(o, f, n, s); + }); + } + }, + + createNS : function(n, o) { + var i, v; + + o = o || win; + + n = n.split('.'); + for (i=0; i 10000) { - // Modified by Dan S./Zotero - if (o.success && c < 10000 && (x.status == 200 || x.status == 0)) + if (o.success && c < 10000 && x.status == 200) o.success.call(o.success_scope, '' + x.responseText, x, o); else if (o.error) o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o); @@ -898,9 +937,9 @@ tinymce.create('static tinymce.util.XHR', { // Wait for response, onReadyStateChange can not be used since it leaks memory in IE t = w.setTimeout(ready, 10); } - - } + } }); + (function() { var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR; @@ -952,12 +991,32 @@ tinymce.create('static tinymce.util.XHR', { return new tinymce.util.JSONRequest().send(o); } } - - }); -}());(function(tinymce) { + }); +}()); +(function(tinymce) { // Shorten names - var each = tinymce.each, is = tinymce.is; - var isWebKit = tinymce.isWebKit, isIE = tinymce.isIE; + var each = tinymce.each, + is = tinymce.is, + isWebKit = tinymce.isWebKit, + isIE = tinymce.isIE, + blockRe = /^(H[1-6R]|P|DIV|ADDRESS|PRE|FORM|T(ABLE|BODY|HEAD|FOOT|H|R|D)|LI|OL|UL|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|MENU|ISINDEX|SAMP)$/, + boolAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'), + mceAttribs = makeMap('src,href,style,coords,shape'), + encodedChars = {'&' : '&', '"' : '"', '<' : '<', '>' : '>'}, + encodeCharsRe = /[<>&\"]/g, + simpleSelectorRe = /^([a-z0-9],?)+$/i, + tagRegExp = /<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)(\s*\/?)>/g, + attrRegExp = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; + + function makeMap(str) { + var map = {}, i; + + str = str.split(','); + for (i = str.length; i >= 0; i--) + map[str[i]] = 1; + + return map; + }; tinymce.create('tinymce.dom.DOMUtils', { doc : null, @@ -980,15 +1039,15 @@ tinymce.create('static tinymce.util.XHR', { }, DOMUtils : function(d, s) { - var t = this; + var t = this, globalStyle; t.doc = d; t.win = window; t.files = {}; t.cssFlicker = false; t.counter = 0; - t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat"; - t.stdMode = d.documentMode === 8; + t.stdMode = d.documentMode >= 8; + t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat" || t.stdMode; t.settings = s = tinymce.extend({ keep_values : false, @@ -1005,6 +1064,16 @@ tinymce.create('static tinymce.util.XHR', { } } + // Build styles list + if (s.valid_styles) { + t._styles = {}; + + // Convert styles into a rule list + each(s.valid_styles, function(value, key) { + t._styles[key] = tinymce.explode(value); + }); + } + tinymce.addUnload(t.destroy, t); }, @@ -1124,6 +1193,14 @@ tinymce.create('static tinymce.util.XHR', { return e; }, + getNext : function(node, selector) { + return this._findSib(node, selector, 'nextSibling'); + }, + + getPrev : function(node, selector) { + return this._findSib(node, selector, 'previousSibling'); + }, + select : function(pa, s) { var t = this; @@ -1131,8 +1208,30 @@ tinymce.create('static tinymce.util.XHR', { return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []); }, - is : function(n, patt) { - return tinymce.dom.Sizzle.matches(patt, n.nodeType ? [n] : n).length > 0; + is : function(n, selector) { + var i; + + // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance + if (n.length === undefined) { + // Simple all selector + if (selector === '*') + return n.nodeType == 1; + + // Simple selector just elements + if (simpleSelectorRe.test(selector)) { + selector = selector.toLowerCase().split(/,/); + n = n.nodeName.toLowerCase(); + + for (i = selector.length - 1; i >= 0; i--) { + if (selector[i] == n) + return true; + } + + return false; + } + } + + return tinymce.dom.Sizzle.matches(selector, n.nodeType ? [n] : n).length > 0; }, @@ -1170,44 +1269,33 @@ tinymce.create('static tinymce.util.XHR', { o += ' ' + k + '="' + t.encode(a[k]) + '"'; } - if (tinymce.is(h)) + // A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime + if (typeof(h) != "undefined") return o + '>' + h + ''; return o + ' />'; }, - remove : function(n, k) { - var t = this; + remove : function(node, keep_children) { + return this.run(node, function(node) { + var parent, child; - return this.run(n, function(n) { - var p, g, i; + parent = node.parentNode; - p = n.parentNode; - - if (!p) + if (!parent) return null; - if (k) { - 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); - //}); + if (keep_children) { + while (child = node.firstChild) { + // IE 8 will crash if you don't remove completely empty text nodes + if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue) + parent.insertBefore(child, node); + else + node.removeChild(child); + } } - // Fix IE psuedo leak - if (t.fixPsuedoLeaks) { - p = n.cloneNode(true); - 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); + return parent.removeChild(node); }); }, @@ -1252,7 +1340,7 @@ tinymce.create('static tinymce.util.XHR', { // Force update of the style data if (t.settings.update_styles) - t.setAttrib(e, 'mce_style'); + t.setAttrib(e, '_mce_style'); }); }, @@ -1335,9 +1423,9 @@ tinymce.create('static tinymce.util.XHR', { // No mce_style for elements with these since they might get resized by the user if (s.keep_values) { if (v && !t._isRes(v)) - e.setAttribute('mce_style', v, 2); + e.setAttribute('_mce_style', v, 2); else - e.removeAttribute('mce_style', 2); + e.removeAttribute('_mce_style', 2); } e.style.cssText = v; @@ -1353,13 +1441,13 @@ tinymce.create('static tinymce.util.XHR', { if (s.url_converter) v = s.url_converter.call(s.url_converter_scope || t, v, n, e); - t.setAttrib(e, 'mce_' + n, v, 2); + t.setAttrib(e, '_mce_' + n, v, 2); } break; case "shape": - e.setAttribute('mce_style', v); + e.setAttribute('_mce_style', v); break; } @@ -1393,7 +1481,7 @@ tinymce.create('static tinymce.util.XHR', { // Try the mce variant for these if (/^(src|href|style|coords|shape)$/.test(n)) { - v = e.getAttribute("mce_" + n); + v = e.getAttribute("_mce_" + n); if (v) return v; @@ -1407,14 +1495,26 @@ 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; if (v) { - v = t.serializeStyle(t.parseStyle(v)); + v = t.serializeStyle(t.parseStyle(v), e.nodeName); if (t.settings.keep_values && !t._isRes(v)) - e.setAttribute('mce_style', v); + e.setAttribute('_mce_style', v); } } @@ -1482,7 +1582,7 @@ tinymce.create('static tinymce.util.XHR', { default: // IE has odd anonymous function for event attributes if (n.indexOf('on') === 0 && v) - v = ('' + v).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1'); + v = tinymce._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1', '' + v); } } @@ -1502,7 +1602,6 @@ tinymce.create('static tinymce.util.XHR', { 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}; } @@ -1624,15 +1723,23 @@ tinymce.create('static tinymce.util.XHR', { return o; }, - serializeStyle : function(o) { - var s = ''; + serializeStyle : function(o, name) { + var t = this, s = ''; - each(o, function(v, k) { + function add(v, k) { if (k && v) { - if (tinymce.isGecko && k.indexOf('-moz-') === 0) + // Remove browser specific styles like -moz- or -webkit- + if (k.indexOf('-') === 0) return; switch (k) { + case 'font-weight': + // Opera will output bold as 700 + if (v == 700) + v = 'bold'; + + break; + case 'color': case 'background-color': v = v.toLowerCase(); @@ -1641,7 +1748,19 @@ tinymce.create('static tinymce.util.XHR', { s += (s ? ' ' : '') + k + ': ' + v + ';'; } - }); + }; + + // Validate style output + if (name && t._styles) { + each(t._styles['*'], function(name) { + add(o[name], name); + }); + + each(t._styles[name.toLowerCase()], function(name) { + add(o[name], name); + }); + } else + each(o, add); return s; }, @@ -1666,7 +1785,7 @@ tinymce.create('static tinymce.util.XHR', { // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading // It's ugly but it seems to work fine. - if (isIE && d.documentMode) { + if (isIE && d.documentMode && d.recalc) { link.onload = function() { d.recalc(); link.onload = null; @@ -1704,8 +1823,17 @@ tinymce.create('static tinymce.util.XHR', { re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g"); v = e.className.replace(re, ' '); + v = tinymce.trim(v != ' ' ? v : ''); - return e.className = tinymce.trim(v != ' ' ? v : ''); + e.className = v; + + // Empty class attr + if (!v) { + e.removeAttribute('class'); + e.removeAttribute('className'); + } + + return v; } return e.className; @@ -1749,6 +1877,10 @@ tinymce.create('static tinymce.util.XHR', { if (isIE) { function set() { + // Remove all child nodes + while (e.firstChild) + e.firstChild.removeNode(); + try { // IE will remove comments from the beginning // unless you padd the contents with something @@ -1758,10 +1890,6 @@ tinymce.create('static tinymce.util.XHR', { // IE sometimes produces an unknown runtime error on innerHTML if it's an block element within a block element for example a div inside a p // This seems to fix this problem - // Remove all child nodes - while (e.firstChild) - e.firstChild.removeNode(); - // Create new div with HTML contents and a BR infront to keep comments x = t.create('div'); x.innerHTML = '
' + h; @@ -1779,7 +1907,7 @@ tinymce.create('static tinymce.util.XHR', { // DOM tree if contents like this

  • Item 1

is inserted // It seems to be that IE doesn't like a root block element placed inside another root block element if (t.settings.fix_ie_paragraphs) - h = h.replace(/

<\/p>|]+)><\/p>|/gi, ' 

'); + h = h.replace(/

<\/p>|]+)><\/p>|/gi, ' 

'); set(); @@ -1790,12 +1918,12 @@ tinymce.create('static tinymce.util.XHR', { n = nl[i]; if (!n.hasChildNodes()) { - if (!n.mce_keep) { + if (!n._mce_keep) { x = 1; // Is broken break; } - n.removeAttribute('mce_keep'); + n.removeAttribute('_mce_keep'); } } } @@ -1804,13 +1932,13 @@ 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(/<\/p>/g, '
'); + h = h.replace(/

]+)>|

/ig, '

'); + h = h.replace(/<\/p>/gi, '
'); // Set the new HTML with DIVs set(); - // Replace all DIV elements with he mce_tmp attibute back to paragraphs + // Replace all DIV elements with the _mce_tmp attibute back to paragraphs // This is needed since IE has a annoying bug see above for details // This is a slow process but it has to be done. :( if (t.settings.fix_ie_paragraphs) { @@ -1819,7 +1947,7 @@ tinymce.create('static tinymce.util.XHR', { n = nl[i]; // Is it a temp div - if (n.mce_tmp) { + if (n._mce_tmp) { // Create new paragraph p = t.doc.createElement('p'); @@ -1827,7 +1955,7 @@ tinymce.create('static tinymce.util.XHR', { n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) { var v; - if (b !== 'mce_tmp') { + if (b !== '_mce_tmp') { v = n.getAttribute(b); if (!v && b === 'class') @@ -1855,27 +1983,23 @@ 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; - // Convert strong and em to b and i in FF since it can't handle them - if (tinymce.isGecko) { - h = h.replace(/<(\/?)strong>|]+)>/gi, '<$1b$2>'); - h = h.replace(/<(\/?)em>|]+)>/gi, '<$1i$2>'); - } else if (isIE) { + 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 + // Force tags open, and on IE9 replace $1$2 that got left behind due to bugs in their RegExp engine + h = tinymce._replace(/]+)\/>|/gi, '', h); // Force open - // Store away src and href in mce_src and mce_href since browsers mess them up + // Store away src and href in _mce_src and mce_href since browsers mess them up if (s.keep_values) { // Wrap scripts and styles in comments for serialization purposes - if (/)/g, '\n'); @@ -1887,34 +2011,37 @@ tinymce.create('static tinymce.util.XHR', { }; // Wrap the script contents in CDATA and keep them from executing - h = h.replace(/]+|)>([\s\S]*?)<\/script>/g, function(v, attribs, text) { + h = h.replace(/]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) { // Force type attribute if (!attribs) attribs = ' type="text/javascript"'; - // Prefix script type/language attribute values with mce- to prevent it from executing - attribs = attribs.replace(/(type|language)=\"?/, '$&mce-'); - attribs = attribs.replace(/src=\"([^\"]+)\"?/, function(a, url) { + // Convert the src attribute of the scripts + attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) { if (s.url_converter) url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script')); - return 'mce_src="' + url + '"'; + return '_mce_src="' + url + '"'; }); // Wrap text contents - if (tinymce.trim(text)) - text = ''; + if (tinymce.trim(text)) { + codeBlocks.push(trim(text)); + text = ''; + } return '' + text + ''; }); // Wrap style elements - h = h.replace(/]+|)>([\s\S]*?)<\/style>/g, function(v, attribs, text) { + 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 + ''; + return '' + text + ''; }); // Wrap noscript elements @@ -1923,45 +2050,52 @@ tinymce.create('static tinymce.util.XHR', { }); } - h = h.replace(//g, ''); + h = tinymce._replace(//g, '', h); - // 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) { - var u = c; + // This function processes the attributes in the HTML string to force boolean + // attributes to the attr="attr" format and convert style, src and href to _mce_ versions + function processTags(html) { + return html.replace(tagRegExp, function(match, elm_name, attrs, end) { + return '<' + elm_name + attrs.replace(attrRegExp, function(match, name, value, val2, val3) { + var mceValue; - // Tag already got a mce_ version - if (a.indexOf('mce_' + b) != -1) - return m; + name = name.toLowerCase(); + value = value || val2 || val3 || ""; - if (b == 'style') { - // No mce_style for elements with these since they might get resized by the user - if (t._isRes(c)) - return m; + // Treat boolean attributes + if (boolAttrs[name]) { + // false or 0 is treated as a missing attribute + if (value === 'false' || value === '0') + return; - if (s.hex_colors) { - u = u.replace(/rgb\([^\)]+\)/g, function(v) { - return t.toHex(v); - }); + return name + '="' + name + '"'; } - 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)) + ')'; - }); + // Is attribute one that needs special treatment + if (mceAttribs[name] && attrs.indexOf('_mce_' + name) == -1) { + mceValue = t.decode(value); + + // Convert URLs to relative/absolute ones + if (s.url_converter && (name == "src" || name == "href")) + mceValue = s.url_converter.call(s.url_converter_scope || t, mceValue, name, elm_name); + + // Process styles lowercases them and compresses them + if (name == 'style') + mceValue = t.serializeStyle(t.parseStyle(mceValue), name); + + return name + '="' + value + '"' + ' _mce_' + name + '="' + t.encode(mceValue) + '"'; } - } 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)); - } - return ' ' + b + '="' + c + '" mce_' + b + '="' + u + '"'; - }; + return match; + }) + end + '>'; + }); + }; - a = a.replace(/ (src|href|style|coords|shape)=[\"]([^\"]+)[\"]/gi, handle); // W3C - a = a.replace(/ (src|href|style|coords|shape)=[\']([^\']+)[\']/gi, handle); // W3C + h = processTags(h); - 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]; }); } @@ -1988,25 +2122,41 @@ tinymce.create('static tinymce.util.XHR', { setOuterHTML : function(e, h, d) { var t = this; - return this.run(e, function(e) { + function setHTML(e, h, d) { var n, tp; + tp = d.createElement("body"); + tp.innerHTML = h; + + n = tp.lastChild; + while (n) { + t.insertAfter(n.cloneNode(true), e); + n = n.previousSibling; + } + + t.remove(e); + }; + + return this.run(e, function(e) { e = t.get(e); - d = d || e.ownerDocument || t.doc; - if (isIE && e.nodeType == 1) - e.outerHTML = h; - else { - tp = d.createElement("body"); - tp.innerHTML = h; + // Only set HTML on elements + if (e.nodeType == 1) { + d = d || e.ownerDocument || t.doc; - n = tp.lastChild; - while (n) { - t.insertAfter(n.cloneNode(true), e); - n = n.previousSibling; - } - - t.remove(e); + if (isIE) { + try { + // Try outerHTML for IE it sometimes produces an unknown runtime error + if (isIE && e.nodeType == 1) + e.outerHTML = h; + else + setHTML(e, h, d); + } catch (ex) { + // Fix for unknown runtime error + setHTML(e, h, d); + } + } else + setHTML(e, h, d); } }); }, @@ -2015,7 +2165,7 @@ tinymce.create('static tinymce.util.XHR', { var e, n, v; // Look for entities to decode - if (/&[^;]+;/.test(s)) { + if (/&[\w#]+;/.test(s)) { // Decode the entities using a div element not super efficient but less code e = this.doc.createElement("div"); e.innerHTML = s; @@ -2025,7 +2175,7 @@ tinymce.create('static tinymce.util.XHR', { if (n) { do { v += n.nodeValue; - } while (n.nextSibling); + } while (n = n.nextSibling); } return v || s; @@ -2034,43 +2184,27 @@ tinymce.create('static tinymce.util.XHR', { return s; }, - encode : function(s) { - return s ? ('' + s).replace(/[<>&\"]/g, function (c, b) { - switch (c) { - case '&': - return '&'; - - case '"': - return '"'; - - case '<': - return '<'; - - case '>': - return '>'; - } - - return c; - }) : s; + encode : function(str) { + return ('' + str).replace(encodeCharsRe, function(chr) { + return encodedChars[chr]; + }); }, - insertAfter : function(n, r) { - var t = this; + insertAfter : function(node, reference_node) { + reference_node = this.get(reference_node); - r = t.get(r); + return this.run(node, function(node) { + var parent, nextSibling; - return this.run(n, function(n) { - var p, ns; + parent = reference_node.parentNode; + nextSibling = reference_node.nextSibling; - p = r.parentNode; - ns = r.nextSibling; - - if (ns) - p.insertBefore(n, ns); + if (nextSibling) + parent.insertBefore(node, nextSibling); else - p.appendChild(n); + parent.appendChild(node); - return n; + return node; }); }, @@ -2080,7 +2214,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 blockRe.test(n); }, replace : function(n, o, k) { @@ -2091,23 +2225,34 @@ tinymce.create('static tinymce.util.XHR', { return t.run(o, function(o) { if (k) { - each(o.childNodes, function(c) { - n.appendChild(c.cloneNode(true)); + each(tinymce.grep(o.childNodes), function(c) { + n.appendChild(c); }); } - // Fix IE psuedo leak for elements since replacing elements if fairly common - // Will break parentNode for some unknown reason - if (t.fixPsuedoLeaks && o.nodeType === 1) { - o.parentNode.insertBefore(n, o); - t.remove(o); - return n; - } - return o.parentNode.replaceChild(n, o); }); }, + rename : function(elm, name) { + var t = this, newElm; + + if (elm.nodeName != name.toUpperCase()) { + // Rename block element + newElm = t.create(name); + + // Copy attribs to new block + each(t.getAttribs(elm), function(attr_node) { + t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName)); + }); + + // Replace block + t.replace(newElm, elm, 1); + } + + return newElm || elm; + }, + findCommonAncestor : function(a, b) { var ps = a, pe; @@ -2174,7 +2319,7 @@ tinymce.create('static tinymce.util.XHR', { // Remove everything but class name ov = v; - v = v.replace(/.*\.([a-z0-9_\-]+).*/i, '$1'); + v = tinymce._replace(/.*\.([a-z0-9_\-]+).*/i, '$1', v); // Filter classes if (f && !(v = f(v, ov))) @@ -2251,9 +2396,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; @@ -2281,64 +2430,96 @@ tinymce.create('static tinymce.util.XHR', { return d.createRange ? d.createRange() : new tinymce.dom.Range(this); }, + nodeIndex : function(node, normalized) { + var idx = 0, lastNodeType, lastNode, nodeType; + + if (node) { + for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) { + nodeType = node.nodeType; + + // Normalize text nodes + if (normalized && nodeType == 3) { + if (nodeType == lastNodeType || !node.nodeValue.length) + continue; + } + + idx++; + lastNodeType = nodeType; + } + } + + return idx; + }, + 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 + // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense + // but we don't want that in our code since it serves no purpose for the end user // 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]; + function trim(node) { + var i, children = node.childNodes; - if (n && n[na] && n[na].nodeType == 1 && isEmpty(n[na])) - t.remove(n[na]); - }; + if (node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark') + return; - 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 + for (i = children.length - 1; i >= 0; i--) + trim(children[i]); - return n.replace(/[ \t\r\n]+| | /g, '') == ''; + if (node.nodeType != 9) { + // Keep non whitespace text nodes + if (node.nodeType == 3 && node.nodeValue.length > 0) { + // If parent element isn't a block or there isn't any useful contents for example "

" + if (!t.isBlock(node.parentNode) || tinymce.trim(node.nodeValue).length > 0) + return; + } + + if (node.nodeType == 1) { + // If the only child is a bookmark then move it up + children = node.childNodes; + if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('_mce_type') == 'bookmark') + node.parentNode.insertBefore(children[0], node); + + // Keep non empty elements or img, hr etc + if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName)) + return; + } + + t.remove(node); + } + + return node; }; if (pe && e) { // Get before chunk - r.setStartBefore(pe); - r.setEndBefore(e); + r.setStart(pe.parentNode, t.nodeIndex(pe)); + r.setEnd(e.parentNode, t.nodeIndex(e)); bef = r.extractContents(); // Get after chunk r = t.createRng(); - r.setStartAfter(e); - r.setEndAfter(pe); + r.setStart(e.parentNode, t.nodeIndex(e) + 1); + r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1); aft = r.extractContents(); - // Insert chunks and remove parent + // Insert before chunk pa = pe.parentNode; + pa.insertBefore(trim(bef), pe); - // Remove right side edge of the before contents - trimEdge(bef, 'lastChild'); - - if (!isEmpty(bef)) - pa.insertBefore(bef, pe); - + // Insert middle chunk 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); - + // Insert after chunk + pa.insertBefore(trim(aft), pe); t.remove(pe); return re || e; @@ -2364,6 +2545,27 @@ tinymce.create('static tinymce.util.XHR', { }, + _findSib : function(node, selector, name) { + var t = this, f = selector; + + if (node) { + // If expression make a function of it using is + if (is(f, 'string')) { + f = function(node) { + return t.is(node, selector); + }; + } + + // Loop all siblings + for (node = node[name]; node; node = node[name]) { + if (f(node)) + return node; + } + } + + return null; + }, + _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); @@ -2398,237 +2600,239 @@ tinymce.create('static tinymce.util.XHR', { return s; } */ + }); - }); - - // 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; - - function indexOf(child, parent) { - var i, node; - - if (child.parentNode != parent) - return -1; - - for (node = parent.firstChild, i = 0; node != child; node = node.nextSibling) - i++; - - return i; - }; - - function nodeIndex(n) { - var i = 0; - - while (n.previousSibling) { - i++; - n = n.previousSibling; - } - - return i; - }; - - function getSelectedNode(container, offset) { - var child; - - if (container.nodeType == 3 /* TEXT_NODE */) - return container; - - if (offset < 0) - return container; - - child = container.firstChild; - while (child != null && offset > 0) { - --offset; - child = child.nextSibling; - } - - if (child != null) - return child; - - return container; - }; - // Range constructor function Range(dom) { - var d = dom.doc; - - extend(this, { - dom : dom, + var t = this, + doc = dom.doc, + EXTRACT = 0, + CLONE = 1, + DELETE = 2, + TRUE = true, + FALSE = false, + START_OFFSET = 'startOffset', + START_CONTAINER = 'startContainer', + END_CONTAINER = 'endContainer', + END_OFFSET = 'endOffset', + extend = tinymce.extend, + nodeIndex = dom.nodeIndex; + extend(t, { // Inital states - startContainer : d, + startContainer : doc, startOffset : 0, - endContainer : d, + endContainer : doc, endOffset : 0, - collapsed : true, - commonAncestorContainer : d, + collapsed : TRUE, + commonAncestorContainer : doc, // Range constants START_TO_START : 0, START_TO_END : 1, END_TO_END : 2, - END_TO_START : 3 + END_TO_START : 3, + + // Public methods + setStart : setStart, + setEnd : setEnd, + setStartBefore : setStartBefore, + setStartAfter : setStartAfter, + setEndBefore : setEndBefore, + setEndAfter : setEndAfter, + collapse : collapse, + selectNode : selectNode, + selectNodeContents : selectNodeContents, + compareBoundaryPoints : compareBoundaryPoints, + deleteContents : deleteContents, + extractContents : extractContents, + cloneContents : cloneContents, + insertNode : insertNode, + surroundContents : surroundContents, + cloneRange : cloneRange }); - }; - // Add range methods - extend(Range.prototype, { - setStart : function(n, o) { - this._setEndPoint(true, n, o); - }, + function setStart(n, o) { + _setEndPoint(TRUE, n, o); + }; - setEnd : function(n, o) { - this._setEndPoint(false, n, o); - }, + function setEnd(n, o) { + _setEndPoint(FALSE, n, o); + }; - setStartBefore : function(n) { - this.setStart(n.parentNode, nodeIndex(n)); - }, + function setStartBefore(n) { + setStart(n.parentNode, nodeIndex(n)); + }; - setStartAfter : function(n) { - this.setStart(n.parentNode, nodeIndex(n) + 1); - }, + function setStartAfter(n) { + setStart(n.parentNode, nodeIndex(n) + 1); + }; - setEndBefore : function(n) { - this.setEnd(n.parentNode, nodeIndex(n)); - }, + function setEndBefore(n) { + setEnd(n.parentNode, nodeIndex(n)); + }; - setEndAfter : function(n) { - this.setEnd(n.parentNode, nodeIndex(n) + 1); - }, - - collapse : function(ts) { - var t = this; + function setEndAfter(n) { + setEnd(n.parentNode, nodeIndex(n) + 1); + }; + function collapse(ts) { if (ts) { - t.endContainer = t.startContainer; - t.endOffset = t.startOffset; + t[END_CONTAINER] = t[START_CONTAINER]; + t[END_OFFSET] = t[START_OFFSET]; } else { - t.startContainer = t.endContainer; - t.startOffset = t.endOffset; + t[START_CONTAINER] = t[END_CONTAINER]; + t[START_OFFSET] = t[END_OFFSET]; } - t.collapsed = true; - }, + t.collapsed = TRUE; + }; - selectNode : function(n) { - this.setStartBefore(n); - this.setEndAfter(n); - }, + function selectNode(n) { + setStartBefore(n); + setEndAfter(n); + }; - selectNodeContents : function(n) { - this.setStart(n, 0); - this.setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length); - }, + function selectNodeContents(n) { + setStart(n, 0); + 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; + function compareBoundaryPoints(h, r) { + var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET]; // Check START_TO_START if (h === 0) - return t._compareBoundaryPoints(sc, so, sc, so); + return _compareBoundaryPoints(sc, so, sc, so); // Check START_TO_END if (h === 1) - return t._compareBoundaryPoints(sc, so, ec, eo); + return _compareBoundaryPoints(sc, so, ec, eo); // Check END_TO_END if (h === 2) - return t._compareBoundaryPoints(ec, eo, ec, eo); + return _compareBoundaryPoints(ec, eo, ec, eo); // Check END_TO_START if (h === 3) - return t._compareBoundaryPoints(ec, eo, sc, so); - }, + return _compareBoundaryPoints(ec, eo, sc, so); + }; - deleteContents : function() { - this._traverse(DELETE); - }, + function deleteContents() { + _traverse(DELETE); + }; - extractContents : function() { - return this._traverse(EXTRACT); - }, + function extractContents() { + return _traverse(EXTRACT); + }; - cloneContents : function() { - return this._traverse(CLONE); - }, + function cloneContents() { + return _traverse(CLONE); + }; - insertNode : function(n) { - var t = this, nn, o; + function insertNode(n) { + var startContainer = this[START_CONTAINER], + startOffset = this[START_OFFSET], 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); + if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) { + if (!startOffset) { + // At the start of text + startContainer.parentNode.insertBefore(n, startContainer); + } else if (startOffset >= startContainer.nodeValue.length) { + // At the end of text + dom.insertAfter(n, startContainer); + } else { + // Middle, need to split + nn = startContainer.splitText(startOffset); + startContainer.parentNode.insertBefore(n, nn); + } } else { // Insert element node - if (t.startContainer.childNodes.length > 0) - o = t.startContainer.childNodes[t.startOffset]; + if (startContainer.childNodes.length > 0) + o = startContainer.childNodes[startOffset]; - t.startContainer.insertBefore(n, o); + if (o) + startContainer.insertBefore(n, o); + else + startContainer.appendChild(n); } - }, + }; - surroundContents : function(n) { - var t = this, f = t.extractContents(); + function surroundContents(n) { + var 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, + function cloneRange() { + return extend(new Range(dom), { + startContainer : t[START_CONTAINER], + startOffset : t[START_OFFSET], + endContainer : t[END_CONTAINER], + endOffset : t[END_OFFSET], collapsed : t.collapsed, commonAncestorContainer : t.commonAncestorContainer }); - }, + }; -/* - detach : function() { - // Not implemented - }, -*/ - // Internal methods + // Private methods - _isCollapsed : function() { - return (this.startContainer == this.endContainer && this.startOffset == this.endOffset); - }, + function _getSelectedNode(container, offset) { + var child; - _compareBoundaryPoints : function (containerA, offsetA, containerB, offsetB) { + if (container.nodeType == 3 /* TEXT_NODE */) + return container; + + if (offset < 0) + return container; + + child = container.firstChild; + while (child && offset > 0) { + --offset; + child = child.nextSibling; + } + + if (child) + return child; + + return container; + }; + + function _isCollapsed() { + return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]); + }; + + function _compareBoundaryPoints(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 + // 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) { + if (offsetA == offsetB) return 0; // equal - } else if (offsetA < offsetB) { + + if (offsetA < offsetB) return -1; // before - } else { - return 1; // after - } + + 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 + // 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) { + while (c && c.parentNode != containerA) c = c.parentNode; - } + if (c) { offsetC = 0; n = containerA.firstChild; @@ -2638,15 +2842,14 @@ tinymce.create('static tinymce.util.XHR', { n = n.nextSibling; } - if (offsetA <= offsetC) { + if (offsetA <= offsetC) return -1; // before - } else { - return 1; // after - } + + 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 + // 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) { @@ -2662,124 +2865,113 @@ tinymce.create('static tinymce.util.XHR', { n = n.nextSibling; } - if (offsetC < offsetB) { + if (offsetC < offsetB) return -1; // before - } else { - return 1; // after - } + + 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 + // 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); + cmnRoot = dom.findCommonAncestor(containerA, containerB); childA = containerA; - while (childA && childA.parentNode != cmnRoot) { - childA = childA.parentNode; - } + while (childA && childA.parentNode != cmnRoot) + childA = childA.parentNode; - if (!childA) { + if (!childA) childA = cmnRoot; - } childB = containerB; - while (childB && childB.parentNode != cmnRoot) { + while (childB && childB.parentNode != cmnRoot) childB = childB.parentNode; - } - if (!childB) { + if (!childB) childB = cmnRoot; - } - if (childA == childB) { + if (childA == childB) return 0; // equal - } n = cmnRoot.firstChild; while (n) { - if (n == childA) { + if (n == childA) return -1; // before - } - if (n == childB) { + if (n == childB) return 1; // after - } n = n.nextSibling; } - }, + }; - _setEndPoint : function(st, n, o) { - var t = this, ec, sc; + function _setEndPoint(st, n, o) { + var ec, sc; if (st) { - t.startContainer = n; - t.startOffset = o; + t[START_CONTAINER] = n; + t[START_OFFSET] = o; } else { - t.endContainer = n; - t.endOffset = o; + t[END_CONTAINER] = n; + t[END_OFFSET] = 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 + // 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; + ec = t[END_CONTAINER]; while (ec.parentNode) ec = ec.parentNode; - sc = t.startContainer; + sc = t[START_CONTAINER]; 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 + if (sc == ec) { + // 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) + if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0) t.collapse(st); - } + } else + t.collapse(st); - t.collapsed = t._isCollapsed(); - t.commonAncestorContainer = t.dom.findCommonAncestor(t.startContainer, t.endContainer); - }, + t.collapsed = _isCollapsed(); + t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]); + }; - // This code is heavily "inspired" by the Apache Xerces implementation. I hope they don't mind. :) + function _traverse(how) { + var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep; - _traverse : function(how) { - var t = this, c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep; + if (t[START_CONTAINER] == t[END_CONTAINER]) + return _traverseSameContainer(how); - 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); + for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) { + if (p == t[START_CONTAINER]) + return _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); + for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) { + if (p == t[END_CONTAINER]) + return _traverseCommonEndContainer(c, how); ++startContainerDepth; } depthDiff = startContainerDepth - endContainerDepth; - startNode = t.startContainer; + startNode = t[START_CONTAINER]; while (depthDiff > 0) { startNode = startNode.parentNode; depthDiff--; } - endNode = t.endContainer; + endNode = t[END_CONTAINER]; while (depthDiff < 0) { endNode = endNode.parentNode; depthDiff++; @@ -2791,47 +2983,47 @@ tinymce.create('static tinymce.util.XHR', { endNode = ep; } - return t._traverseCommonAncestors(startNode, endNode, how); - }, + return _traverseCommonAncestors(startNode, endNode, how); + }; - _traverseSameContainer : function(how) { - var t = this, frag, s, sub, n, cnt, sibling, xferNode; + function _traverseSameContainer(how) { + var frag, s, sub, n, cnt, sibling, xferNode; if (how != DELETE) - frag = t.dom.doc.createDocumentFragment(); + frag = doc.createDocumentFragment(); // If selection is empty, just return the fragment - if (t.startOffset == t.endOffset) + if (t[START_OFFSET] == t[END_OFFSET]) return frag; // Text node needs special case handling - if (t.startContainer.nodeType == 3 /* TEXT_NODE */) { + if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) { // get the substring - s = t.startContainer.nodeValue; - sub = s.substring(t.startOffset, t.endOffset); + s = t[START_CONTAINER].nodeValue; + sub = s.substring(t[START_OFFSET], t[END_OFFSET]); // set the original text node to its new value if (how != CLONE) { - t.startContainer.deleteData(t.startOffset, t.endOffset - t.startOffset); + t[START_CONTAINER].deleteData(t[START_OFFSET], t[END_OFFSET] - t[START_OFFSET]); // Nothing is partially selected, so collapse to start point - t.collapse(true); + t.collapse(TRUE); } if (how == DELETE) - return null; + return; - frag.appendChild(t.dom.doc.createTextNode(sub)); + frag.appendChild(doc.createTextNode(sub)); return frag; } // Copy nodes between the start/end offsets. - n = getSelectedNode(t.startContainer, t.startOffset); - cnt = t.endOffset - t.startOffset; + n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]); + cnt = t[END_OFFSET] - t[START_OFFSET]; while (cnt > 0) { sibling = n.nextSibling; - xferNode = t._traverseFullySelected(n, how); + xferNode = _traverseFullySelected(n, how); if (frag) frag.appendChild( xferNode ); @@ -2842,31 +3034,31 @@ tinymce.create('static tinymce.util.XHR', { // Nothing is partially selected, so collapse to start point if (how != CLONE) - t.collapse(true); + t.collapse(TRUE); return frag; - }, + }; - _traverseCommonStartContainer : function(endAncestor, how) { - var t = this, frag, n, endIdx, cnt, sibling, xferNode; + function _traverseCommonStartContainer(endAncestor, how) { + var frag, n, endIdx, cnt, sibling, xferNode; if (how != DELETE) - frag = t.dom.doc.createDocumentFragment(); + frag = doc.createDocumentFragment(); - n = t._traverseRightBoundary(endAncestor, how); + n = _traverseRightBoundary(endAncestor, how); if (frag) frag.appendChild(n); - endIdx = indexOf(endAncestor, t.startContainer); - cnt = endIdx - t.startOffset; + endIdx = nodeIndex(endAncestor); + cnt = endIdx - t[START_OFFSET]; if (cnt <= 0) { - // Collapse to just before the endAncestor, which + // Collapse to just before the endAncestor, which // is partially selected. if (how != CLONE) { t.setEndBefore(endAncestor); - t.collapse(false); + t.collapse(FALSE); } return frag; @@ -2875,7 +3067,7 @@ tinymce.create('static tinymce.util.XHR', { n = endAncestor.previousSibling; while (cnt > 0) { sibling = n.previousSibling; - xferNode = t._traverseFullySelected(n, how); + xferNode = _traverseFullySelected(n, how); if (frag) frag.insertBefore(xferNode, frag.firstChild); @@ -2884,34 +3076,34 @@ tinymce.create('static tinymce.util.XHR', { n = sibling; } - // Collapse to just before the endAncestor, which + // Collapse to just before the endAncestor, which // is partially selected. if (how != CLONE) { t.setEndBefore(endAncestor); - t.collapse(false); + t.collapse(FALSE); } return frag; - }, + }; - _traverseCommonEndContainer : function(startAncestor, how) { - var t = this, frag, startIdx, n, cnt, sibling, xferNode; + function _traverseCommonEndContainer(startAncestor, how) { + var frag, startIdx, n, cnt, sibling, xferNode; if (how != DELETE) - frag = t.dom.doc.createDocumentFragment(); + frag = doc.createDocumentFragment(); - n = t._traverseLeftBoundary(startAncestor, how); + n = _traverseLeftBoundary(startAncestor, how); if (frag) frag.appendChild(n); - startIdx = indexOf(startAncestor, t.endContainer); + startIdx = nodeIndex(startAncestor); ++startIdx; // Because we already traversed it.... - cnt = t.endOffset - startIdx; + cnt = t[END_OFFSET] - startIdx; n = startAncestor.nextSibling; while (cnt > 0) { sibling = n.nextSibling; - xferNode = t._traverseFullySelected(n, how); + xferNode = _traverseFullySelected(n, how); if (frag) frag.appendChild(xferNode); @@ -2922,25 +3114,25 @@ tinymce.create('static tinymce.util.XHR', { if (how != CLONE) { t.setStartAfter(startAncestor); - t.collapse(true); + t.collapse(TRUE); } return frag; - }, + }; - _traverseCommonAncestors : function(startAncestor, endAncestor, how) { - var t = this, n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling; + function _traverseCommonAncestors(startAncestor, endAncestor, how) { + var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling; if (how != DELETE) - frag = t.dom.doc.createDocumentFragment(); + frag = doc.createDocumentFragment(); - n = t._traverseLeftBoundary(startAncestor, how); + n = _traverseLeftBoundary(startAncestor, how); if (frag) frag.appendChild(n); commonParent = startAncestor.parentNode; - startOffset = indexOf(startAncestor, commonParent); - endOffset = indexOf(endAncestor, commonParent); + startOffset = nodeIndex(startAncestor); + endOffset = nodeIndex(endAncestor); ++startOffset; cnt = endOffset - startOffset; @@ -2948,7 +3140,7 @@ tinymce.create('static tinymce.util.XHR', { while (cnt > 0) { nextSibling = sibling.nextSibling; - n = t._traverseFullySelected(sibling, how); + n = _traverseFullySelected(sibling, how); if (frag) frag.appendChild(n); @@ -2957,38 +3149,37 @@ tinymce.create('static tinymce.util.XHR', { --cnt; } - n = t._traverseRightBoundary(endAncestor, how); + n = _traverseRightBoundary(endAncestor, how); if (frag) frag.appendChild(n); if (how != CLONE) { t.setStartAfter(startAncestor); - t.collapse(true); + 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; + function _traverseRightBoundary(root, how) { + var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER]; if (next == root) - return t._traverseNode(next, isFullySelected, false, how); + return _traverseNode(next, isFullySelected, FALSE, how); parent = next.parentNode; - clonedParent = t._traverseNode(parent, false, false, how); + clonedParent = _traverseNode(parent, FALSE, FALSE, how); - while (parent != null) { - while (next != null) { + while (parent) { + while (next) { prevSibling = next.previousSibling; - clonedChild = t._traverseNode(next, isFullySelected, false, how); + clonedChild = _traverseNode(next, isFullySelected, FALSE, how); if (how != DELETE) clonedParent.insertBefore(clonedChild, clonedParent.firstChild); - isFullySelected = true; + isFullySelected = TRUE; next = prevSibling; } @@ -2998,37 +3189,33 @@ tinymce.create('static tinymce.util.XHR', { next = parent.previousSibling; parent = parent.parentNode; - clonedGrandParent = t._traverseNode(parent, false, false, how); + clonedGrandParent = _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; + function _traverseLeftBoundary(root, how) { + var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent; if (next == root) - return t._traverseNode(next, isFullySelected, true, how); + return _traverseNode(next, isFullySelected, TRUE, how); parent = next.parentNode; - clonedParent = t._traverseNode(parent, false, true, how); + clonedParent = _traverseNode(parent, FALSE, TRUE, how); - while (parent != null) { - while (next != null) { + while (parent) { + while (next) { nextSibling = next.nextSibling; - clonedChild = t._traverseNode(next, isFullySelected, true, how); + clonedChild = _traverseNode(next, isFullySelected, TRUE, how); if (how != DELETE) clonedParent.appendChild(clonedChild); - isFullySelected = true; + isFullySelected = TRUE; next = nextSibling; } @@ -3038,33 +3225,30 @@ tinymce.create('static tinymce.util.XHR', { next = parent.nextSibling; parent = parent.parentNode; - clonedGrandParent = t._traverseNode(parent, false, true, how); + clonedGrandParent = _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; + function _traverseNode(n, isFullySelected, isLeft, how) { + var txtValue, newNodeValue, oldNodeValue, offset, newNode; if (isFullySelected) - return t._traverseFullySelected(n, how); + return _traverseFullySelected(n, how); if (n.nodeType == 3 /* TEXT_NODE */) { txtValue = n.nodeValue; if (isLeft) { - offset = t.startOffset; + offset = t[START_OFFSET]; newNodeValue = txtValue.substring(offset); oldNodeValue = txtValue.substring(0, offset); } else { - offset = t.endOffset; + offset = t[END_OFFSET]; newNodeValue = txtValue.substring(0, offset); oldNodeValue = txtValue.substring(offset); } @@ -3073,102 +3257,38 @@ tinymce.create('static tinymce.util.XHR', { n.nodeValue = oldNodeValue; if (how == DELETE) - return null; + return; - newNode = n.cloneNode(false); + newNode = n.cloneNode(FALSE); newNode.nodeValue = newNodeValue; return newNode; } if (how == DELETE) - return null; + return; - return n.cloneNode(false); - }, - - _traverseFullySelected : function(n, how) { - var t = this; + return n.cloneNode(FALSE); + }; + function _traverseFullySelected(n, how) { if (how != DELETE) - return how == CLONE ? n.cloneNode(true) : n; + 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; - }; + var t = this, invisibleChar = '\uFEFF', range, lastIERng, dom = selection.dom, TRUE = true, FALSE = false; + // Returns a W3C DOM compatible range object by using the IE Range API 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}; - }; + var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed; // If selection is outside the current document just return an empty range element = ieRange.item ? ieRange.item(0) : ieRange.parentElement(); @@ -3177,182 +3297,205 @@ tinymce.create('static tinymce.util.XHR', { // Handle control selection or text selection of a image if (ieRange.item || !element.hasChildNodes()) { - domRange.setStart(element.parentNode, findIndex(element)); + domRange.setStart(element.parentNode, dom.nodeIndex(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); + function findEndPoint(start) { + var marker, container, offset, nodes, startIndex = 0, endIndex, index, parent, checkRng, position; - // Normalize the elements to avoid fragmented dom - startPos.parent.normalize(); - endPos.parent.normalize(); + // Setup temp range and collapse it + checkRng = ieRange.duplicate(); + checkRng.collapse(start); - // Set start container and offset - sc = startPos.parent.childNodes[Math.min(startPos.index, startPos.parent.childNodes.length - 1)]; + // Create marker and insert it at the end of the endpoints parent + marker = dom.create('a'); + parent = checkRng.parentElement(); - if (sc.nodeType != 3) - domRange.setStart(startPos.parent, startPos.index); - else - domRange.setStart(startPos.parent.childNodes[startPos.index], startPos.offset); + // If parent doesn't have any children then set the container to that parent and the index to 0 + if (!parent.hasChildNodes()) { + domRange[start ? 'setStart' : 'setEnd'](parent, 0); + return; + } - // Set end container and offset - ec = endPos.parent.childNodes[Math.min(endPos.index, endPos.parent.childNodes.length - 1)]; + parent.appendChild(marker); + checkRng.moveToElementText(marker); + position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng); + if (position > 0) { + // The position is after the end of the parent element. + // This is the case where IE puts the caret to the left edge of a table. + domRange[start ? 'setStartAfter' : 'setEndAfter'](parent); + dom.remove(marker); + return; + } - if (ec.nodeType != 3) { - if (!collapsed) - endPos.index++; + // Setup node list and endIndex + nodes = tinymce.grep(parent.childNodes); + endIndex = nodes.length - 1; + // Perform a binary search for the position + while (startIndex <= endIndex) { + index = Math.floor((startIndex + endIndex) / 2); - domRange.setEnd(endPos.parent, endPos.index); - } else - domRange.setEnd(endPos.parent.childNodes[endPos.index], endPos.offset); + // Insert marker and check it's position relative to the selection + parent.insertBefore(marker, nodes[index]); + checkRng.moveToElementText(marker); + position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng); + if (position > 0) { + // Marker is to the right + startIndex = index + 1; + } else if (position < 0) { + // Marker is to the left + endIndex = index - 1; + } else { + // Maker is where we are + found = true; + break; + } + } - // 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)); + // Setup container + container = position > 0 || index == 0 ? marker.nextSibling : marker.previousSibling; - ec = domRange.endContainer; - if (ec.nodeType == 1) - domRange.setEnd(ec, Math.min(domRange.endOffset, ec.childNodes.length)); - } + // Handle element selection + if (container.nodeType == 1) { + dom.remove(marker); - // Restore selection to new range - t.addRange(domRange); + // Find offset and container + offset = dom.nodeIndex(container); + container = container.parentNode; + + // Move the offset if we are setting the end or the position is after an element + if (!start || index > 0) + offset++; + } else { + // Calculate offset within text node + if (position > 0 || index == 0) { + checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange); + offset = checkRng.text.length; + } else { + checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange); + offset = container.nodeValue.length - checkRng.text.length; + } + + dom.remove(marker); + } + + domRange[start ? 'setStart' : 'setEnd'](container, offset); + }; + + // Find start point + findEndPoint(true); + + // Find end point if needed + if (!collapsed) + findEndPoint(); return domRange; }; this.addRange = function(rng) { - var ieRng, body = selection.dom.doc.body, startPos, endPos, sc, so, ec, eo; + var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, doc = selection.dom.doc, body = doc.body; + + function setEndPoint(start) { + var container, offset, marker, tmpRng, nodes; + + marker = dom.create('a'); + container = start ? startContainer : endContainer; + offset = start ? startOffset : endOffset; + tmpRng = ieRng.duplicate(); + + if (container == doc) { + container = body; + offset = 0; + } + + if (container.nodeType == 3) { + container.parentNode.insertBefore(marker, container); + tmpRng.moveToElementText(marker); + tmpRng.moveStart('character', offset); + dom.remove(marker); + ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng); + } else { + nodes = container.childNodes; + + if (nodes.length) { + if (offset >= nodes.length) { + dom.insertAfter(marker, nodes[nodes.length - 1]); + } else { + container.insertBefore(marker, nodes[offset]); + } + + tmpRng.moveToElementText(marker); + } else { + // Empty node selection for example
|
+ marker = doc.createTextNode(invisibleChar); + container.appendChild(marker); + tmpRng.moveToElementText(marker.parentNode); + tmpRng.collapse(TRUE); + } + + ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng); + dom.remove(marker); + } + } + + // Destroy cached range + this.destroy(); // Setup some shorter versions - sc = rng.startContainer; - so = rng.startOffset; - ec = rng.endContainer; - eo = rng.endOffset; + startContainer = rng.startContainer; + startOffset = rng.startOffset; + endContainer = rng.endContainer; + endOffset = 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 single element selection then try making a control selection out of it + if (startContainer == endContainer && startContainer.nodeType == 1 && startOffset == endOffset - 1) { + if (startOffset == endOffset - 1) { + try { + ctrlRng = body.createControlRange(); + ctrlRng.addElement(startContainer.childNodes[startOffset]); + ctrlRng.select(); + ctrlRng.scrollIntoView(); + return; + } catch (ex) { + // Ignore } } - - if (so == eo) - ieRng.collapse(eo <= rng.endContainer.childNodes.length - 1); - - ieRng.select(); - - return; } - function getCharPos(container, offset) { - var nodeVal, rng, pos; + // Set start/end point of selection + setEndPoint(true); + setEndPoint(); - 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; - } + // Select the new range and scroll it into view + ieRng.select(); + ieRng.scrollIntoView(); }; this.getRangeAt = function() { // Setup new range if the cache is empty - if (!range || !compareRanges(lastIERng, selection.getRng())) { + if (!range || !tinymce.dom.RangeUtils.compareRanges(lastIERng, selection.getRng())) { range = getRange(); // Store away text range for next call lastIERng = selection.getRng(); } + // IE will say that the range is equal then produce an invalid argument exception + // if you perform specific operations in a keyup event. For example Ctrl+Del. + // This hack will invalidate the range cache if the exception occurs + try { + range.startContainer.nextSibling; + } catch (ex) { + range = getRange(); + lastIERng = null; + } + // Return cached range return range; }; @@ -3367,6 +3510,7 @@ tinymce.create('static tinymce.util.XHR', { tinymce.dom.TridentSelection = Selection; })(); + /* * Sizzle CSS Selector Engine - v1.0 * Copyright 2009, The Dojo Foundation @@ -3375,14 +3519,26 @@ tinymce.create('static tinymce.util.XHR', { */ (function(){ -var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g, +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, done = 0, toString = Object.prototype.toString, - hasDuplicate = false; + hasDuplicate = false, + baseHasDuplicate = true; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function(){ + baseHasDuplicate = false; + return 0; +}); var Sizzle = function(selector, context, results, seed) { results = results || []; - var origContext = context = context || document; + context = context || document; + + var origContext = context; if ( context.nodeType !== 1 && context.nodeType !== 9 ) { return []; @@ -3392,19 +3548,25 @@ var Sizzle = function(selector, context, results, seed) { return results; } - var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context); + var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context), + soFar = selector, ret, cur, pop, i; // Reset the position of the chunker regexp (start from head) - chunker.lastIndex = 0; - - while ( (m = chunker.exec(selector)) !== null ) { - parts.push( m[1] ); + do { + chunker.exec(""); + m = chunker.exec(soFar); + + if ( m ) { + soFar = m[3]; - if ( m[2] ) { - extra = RegExp.rightContext; - break; + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } } - } + } while ( m ); if ( parts.length > 1 && origPOS.exec( selector ) ) { if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { @@ -3417,9 +3579,10 @@ var Sizzle = function(selector, context, results, seed) { while ( parts.length ) { selector = parts.shift(); - if ( Expr.relative[ selector ] ) + if ( Expr.relative[ selector ] ) { selector += parts.shift(); - + } + set = posProcess( selector, set ); } } @@ -3428,12 +3591,12 @@ var Sizzle = function(selector, context, results, seed) { // (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 ); + 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 ? + 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; @@ -3445,7 +3608,8 @@ var Sizzle = function(selector, context, results, seed) { } while ( parts.length ) { - var cur = parts.pop(), pop = cur; + cur = parts.pop(); + pop = cur; if ( !Expr.relative[ cur ] ) { cur = ""; @@ -3469,20 +3633,20 @@ var Sizzle = function(selector, context, results, seed) { } if ( !checkSet ) { - throw "Syntax error, unrecognized expression: " + (cur || selector); + Sizzle.error( 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])) ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { results.push( set[i] ); } } } else { - for ( var i = 0; checkSet[i] != null; i++ ) { + for ( i = 0; checkSet[i] != null; i++ ) { if ( checkSet[i] && checkSet[i].nodeType === 1 ) { results.push( set[i] ); } @@ -3502,7 +3666,7 @@ var Sizzle = function(selector, context, results, seed) { Sizzle.uniqueSort = function(results){ if ( sortOrder ) { - hasDuplicate = false; + hasDuplicate = baseHasDuplicate; results.sort(sortOrder); if ( hasDuplicate ) { @@ -3513,6 +3677,8 @@ Sizzle.uniqueSort = function(results){ } } } + + return results; }; Sizzle.matches = function(expr, set){ @@ -3520,7 +3686,7 @@ Sizzle.matches = function(expr, set){ }; Sizzle.find = function(expr, context, isXML){ - var set, match; + var set; if ( !expr ) { return []; @@ -3529,8 +3695,9 @@ Sizzle.find = function(expr, context, isXML){ 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 ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice(1,1); if ( left.substr( left.length - 1 ) !== "\\" ) { match[1] = (match[1] || "").replace(/\\/g, ""); @@ -3552,15 +3719,21 @@ Sizzle.find = function(expr, context, isXML){ Sizzle.filter = function(expr, set, inplace, not){ var old = expr, result = [], curLoop = set, match, anyFound, - isXMLFilter = set && set[0] && isXML(set[0]); + isXMLFilter = set && set[0] && Sizzle.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; + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var filter = Expr.filter[ type ], found, item, left = match[1]; anyFound = false; - if ( curLoop == result ) { + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { result = []; } @@ -3611,9 +3784,9 @@ Sizzle.filter = function(expr, set, inplace, not){ } // Improper expression - if ( expr == old ) { + if ( expr === old ) { if ( anyFound == null ) { - throw "Syntax error, unrecognized expression: " + expr; + Sizzle.error( expr ); } else { break; } @@ -3625,18 +3798,23 @@ Sizzle.filter = function(expr, set, inplace, not){ return curLoop; }; +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + 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\))?/ + 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\))?/ }, + leftMatch: {}, attrMap: { "class": "className", "for": "htmlFor" @@ -3647,20 +3825,20 @@ var Expr = Sizzle.selectors = { } }, relative: { - "+": function(checkSet, part, isXML){ + "+": function(checkSet, part){ var isPartStr = typeof part === "string", isTag = isPartStr && !/\W/.test(part), isPartStrNotTag = isPartStr && !isTag; - if ( isTag && !isXML ) { - part = part.toUpperCase(); + if ( isTag ) { + part = part.toLowerCase(); } 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 ? + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? elem || false : elem === part; } @@ -3670,22 +3848,23 @@ var Expr = Sizzle.selectors = { Sizzle.filter( part, checkSet, true ); } }, - ">": function(checkSet, part, isXML){ - var isPartStr = typeof part === "string"; + ">": function(checkSet, part){ + var isPartStr = typeof part === "string", + elem, i = 0, l = checkSet.length; if ( isPartStr && !/\W/.test(part) ) { - part = isXML ? part : part.toUpperCase(); + part = part.toLowerCase(); - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; + for ( ; i < l; i++ ) { + elem = checkSet[i]; if ( elem ) { var parent = elem.parentNode; - checkSet[i] = parent.nodeName === part ? parent : false; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; } } } else { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; + for ( ; i < l; i++ ) { + elem = checkSet[i]; if ( elem ) { checkSet[i] = isPartStr ? elem.parentNode : @@ -3699,20 +3878,22 @@ var Expr = Sizzle.selectors = { } }, "": function(checkSet, part, isXML){ - var doneName = done++, checkFn = dirCheck; + var doneName = done++, checkFn = dirCheck, nodeCheck; - if ( !part.match(/\W/) ) { - var nodeCheck = part = isXML ? part : part.toUpperCase(); + if ( typeof part === "string" && !/\W/.test(part) ) { + part = part.toLowerCase(); + nodeCheck = part; checkFn = dirNodeCheck; } checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); }, "~": function(checkSet, part, isXML){ - var doneName = done++, checkFn = dirCheck; + var doneName = done++, checkFn = dirCheck, nodeCheck; - if ( typeof part === "string" && !part.match(/\W/) ) { - var nodeCheck = part = isXML ? part : part.toUpperCase(); + if ( typeof part === "string" && !/\W/.test(part) ) { + part = part.toLowerCase(); + nodeCheck = part; checkFn = dirNodeCheck; } @@ -3726,7 +3907,7 @@ var Expr = Sizzle.selectors = { return m ? [m] : []; } }, - NAME: function(match, context, isXML){ + NAME: function(match, context){ if ( typeof context.getElementsByName !== "undefined" ) { var ret = [], results = context.getElementsByName(match[1]); @@ -3753,9 +3934,10 @@ var Expr = Sizzle.selectors = { for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { if ( elem ) { - if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) { - if ( !inplace ) + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { result.push( elem ); + } } else if ( inplace ) { curLoop[i] = false; } @@ -3768,14 +3950,13 @@ var Expr = Sizzle.selectors = { 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(); + return match[1].toLowerCase(); }, CHILD: function(match){ - if ( match[1] == "nth" ) { + 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" || + 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 @@ -3804,7 +3985,7 @@ var Expr = Sizzle.selectors = { 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]) ) { + if ( ( chunker.exec(match[3]) || "" ).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); @@ -3850,7 +4031,7 @@ var Expr = Sizzle.selectors = { return !!Sizzle( match[3], elem ).length; }, header: function(elem){ - return /h\d/i.test( elem.nodeName ); + return (/h\d/i).test( elem.nodeName ); }, text: function(elem){ return "text" === elem.type; @@ -3877,10 +4058,10 @@ var Expr = Sizzle.selectors = { return "reset" === elem.type; }, button: function(elem){ - return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON"; + return "button" === elem.type || elem.nodeName.toLowerCase() === "button"; }, input: function(elem){ - return /input|select|textarea|button/i.test(elem.nodeName); + return (/input|select|textarea|button/i).test(elem.nodeName); } }, setFilters: { @@ -3903,10 +4084,10 @@ var Expr = Sizzle.selectors = { return i > match[3] - 0; }, nth: function(elem, i, match){ - return match[3] - 0 == i; + return match[3] - 0 === i; }, eq: function(elem, i, match){ - return match[3] - 0 == i; + return match[3] - 0 === i; } }, filter: { @@ -3916,17 +4097,19 @@ var Expr = Sizzle.selectors = { if ( filter ) { return filter( elem, i, match, array ); } else if ( name === "contains" ) { - return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0; + return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").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 ) { + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { return false; } } return true; + } else { + Sizzle.error( "Syntax error, unrecognized expression: " + name ); } }, CHILD: function(elem, match){ @@ -3934,20 +4117,26 @@ var Expr = Sizzle.selectors = { switch (type) { case 'only': case 'first': - while (node = node.previousSibling) { - if ( node.nodeType === 1 ) return false; + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + if ( type === "first" ) { + return true; } - if ( type == 'first') return true; node = elem; case 'last': - while (node = node.nextSibling) { - if ( node.nodeType === 1 ) return false; + 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 ) { + if ( first === 1 && last === 0 ) { return true; } @@ -3965,10 +4154,10 @@ var Expr = Sizzle.selectors = { } var diff = elem.nodeIndex - last; - if ( first == 0 ) { - return diff == 0; + if ( first === 0 ) { + return diff === 0; } else { - return ( diff % first == 0 && diff / first >= 0 ); + return ( diff % first === 0 && diff / first >= 0 ); } } }, @@ -3976,7 +4165,7 @@ var Expr = Sizzle.selectors = { return elem.nodeType === 1 && elem.getAttribute("id") === match; }, TAG: function(elem, match){ - return (match === "*" && elem.nodeType === 1) || elem.nodeName === match; + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; }, CLASS: function(elem, match){ return (" " + (elem.className || elem.getAttribute("class")) + " ") @@ -4004,7 +4193,7 @@ var Expr = Sizzle.selectors = { !check ? value && result !== false : type === "!=" ? - value != check : + value !== check : type === "^=" ? value.indexOf(check) === 0 : type === "$=" ? @@ -4023,14 +4212,18 @@ var Expr = Sizzle.selectors = { } }; -var origPOS = Expr.match.POS; +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; for ( var type in Expr.match ) { - Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); } var makeArray = function(array, results) { - array = Array.prototype.slice.call( array ); + array = Array.prototype.slice.call( array, 0 ); if ( results ) { results.push.apply( results, array ); @@ -4042,23 +4235,25 @@ var makeArray = function(array, results) { // Perform a simple check to determine if the browser is capable of // converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) try { - Array.prototype.slice.call( document.documentElement.childNodes ); + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; // Provide a fallback method if it does not work } catch(e){ makeArray = function(array, results) { - var ret = results || []; + var ret = results || [], i = 0; 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++ ) { + for ( var l = array.length; i < l; i++ ) { ret.push( array[i] ); } } else { - for ( var i = 0; array[i]; i++ ) { + for ( ; array[i]; i++ ) { ret.push( array[i] ); } } @@ -4072,6 +4267,13 @@ var sortOrder; if ( document.documentElement.compareDocumentPosition ) { sortOrder = function( a, b ) { + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + if ( a == b ) { + hasDuplicate = true; + } + return a.compareDocumentPosition ? -1 : 1; + } + var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; if ( ret === 0 ) { hasDuplicate = true; @@ -4080,6 +4282,13 @@ if ( document.documentElement.compareDocumentPosition ) { }; } else if ( "sourceIndex" in document.documentElement ) { sortOrder = function( a, b ) { + if ( !a.sourceIndex || !b.sourceIndex ) { + if ( a == b ) { + hasDuplicate = true; + } + return a.sourceIndex ? -1 : 1; + } + var ret = a.sourceIndex - b.sourceIndex; if ( ret === 0 ) { hasDuplicate = true; @@ -4088,6 +4297,13 @@ if ( document.documentElement.compareDocumentPosition ) { }; } else if ( document.createRange ) { sortOrder = function( a, b ) { + if ( !a.ownerDocument || !b.ownerDocument ) { + if ( a == b ) { + hasDuplicate = true; + } + return a.ownerDocument ? -1 : 1; + } + var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); aRange.setStart(a, 0); aRange.setEnd(a, 0); @@ -4101,12 +4317,32 @@ if ( document.documentElement.compareDocumentPosition ) { }; } +// Utility function for retreiving the text value of an array of DOM nodes +Sizzle.getText = function( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += Sizzle.getText( elem.childNodes ); + } + } + + 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(); + id = "script" + (new Date()).getTime(); form.innerHTML = ""; // Inject it into the root element, check its status, and remove it quickly @@ -4115,7 +4351,7 @@ if ( document.documentElement.compareDocumentPosition ) { // The workaround has to do additional checks after a getElementById // Which slows things down for other browsers (hence the branching) - if ( !!document.getElementById( id ) ) { + if ( document.getElementById( id ) ) { Expr.find.ID = function(match, context, isXML){ if ( typeof context.getElementById !== "undefined" && !isXML ) { var m = context.getElementById(match[1]); @@ -4130,6 +4366,7 @@ if ( document.documentElement.compareDocumentPosition ) { } root.removeChild( form ); + root = form = null; // release memory in IE })(); (function(){ @@ -4170,68 +4407,75 @@ if ( document.documentElement.compareDocumentPosition ) { return elem.getAttribute("href", 2); }; } + + div = null; // release memory in IE })(); -if ( document.querySelectorAll ) (function(){ - var oldSizzle = Sizzle, div = document.createElement("div"); - div.innerHTML = "

"; +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){} + // 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 && !Sizzle.isXML(context) ) { + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(e){} + } - return oldSizzle(query, context, extra, seed); - }; + return oldSizzle(query, context, extra, seed); + }; - for ( var prop in oldSizzle ) { - Sizzle[ prop ] = oldSizzle[ prop ]; - } -})(); + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } -if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){ + div = null; // release memory in IE + })(); +} + +(function(){ var div = document.createElement("div"); + div.innerHTML = "
"; // Opera can't find a second classname (in 9.6) - if ( div.getElementsByClassName("e").length === 0 ) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || 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 ) + 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]); } }; + + div = null; // release memory in IE })(); 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; @@ -4246,7 +4490,7 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { elem.sizset = i; } - if ( elem.nodeName === cur ) { + if ( elem.nodeName.toLowerCase() === cur ) { match = elem; break; } @@ -4260,14 +4504,9 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { } 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; @@ -4302,15 +4541,17 @@ function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { } } -var contains = document.compareDocumentPosition ? function(a, b){ - return a.compareDocumentPosition(b) & 16; +Sizzle.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"; +Sizzle.isXML = function(elem){ + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; }; var posProcess = function(selector, context){ @@ -4339,6 +4580,7 @@ window.tinymce.dom.Sizzle = Sizzle; })(); + (function(tinymce) { // Shorten names var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event; @@ -4611,10 +4853,8 @@ window.tinymce.dom.Sizzle = Sizzle; this.cancelBubble = true; } } + }); - }); - - // Shorten name and setup global instance Event = tinymce.dom.Event = new tinymce.dom.EventUtils(); // Dispatch DOM content loaded event for IE and Safari @@ -4624,45 +4864,25 @@ window.tinymce.dom.Sizzle = Sizzle; Event.destroy(); }); })(tinymce); + (function(tinymce) { - var each = tinymce.each; + tinymce.dom.Element = function(id, settings) { + var t = this, dom, el; - tinymce.create('tinymce.dom.Element', { - Element : function(id, s) { - var t = this, dom, el; + t.settings = settings = settings || {}; + t.id = id; + t.dom = dom = settings.dom || tinymce.DOM; - s = s || {}; - t.id = id; - t.dom = dom = s.dom || tinymce.DOM; - t.settings = s; + // Only IE leaks DOM references, this is a lot faster + if (!tinymce.isIE) + el = dom.get(t.id); - // Only IE leaks DOM references, this is a lot faster - if (!tinymce.isIE) - el = t.dom.get(t.id); - - each([ - 'getPos', - 'getRect', - 'getParent', - 'add', - 'setStyle', - 'getStyle', - 'setStyles', - 'setAttrib', - 'setAttribs', - 'getAttrib', - 'addClass', - 'removeClass', - 'hasClass', - 'getOuterHTML', - 'setOuterHTML', - 'remove', - 'show', - 'hide', - 'isHidden', - 'setHTML', - 'get' - ], function(k) { + tinymce.each( + ('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' + + 'setAttrib,setAttribs,getAttrib,addClass,removeClass,' + + 'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' + + 'isHidden,setHTML,get').split(/,/) + , function(k) { t[k] = function() { var a = [id], i; @@ -4674,83 +4894,86 @@ window.tinymce.dom.Sizzle = Sizzle; return a; }; - }); - }, - - on : function(n, f, s) { - return tinymce.dom.Event.add(this.id, n, f, s); - }, - - getXY : function() { - return { - x : parseInt(this.getStyle('left')), - y : parseInt(this.getStyle('top')) - }; - }, - - getSize : function() { - var n = this.dom.get(this.id); - - return { - w : parseInt(this.getStyle('width') || n.clientWidth), - h : parseInt(this.getStyle('height') || n.clientHeight) - }; - }, - - moveTo : function(x, y) { - this.setStyles({left : x, top : y}); - }, - - moveBy : function(x, y) { - var p = this.getXY(); - - this.moveTo(p.x + x, p.y + y); - }, - - resizeTo : function(w, h) { - this.setStyles({width : w, height : h}); - }, - - resizeBy : function(w, h) { - var s = this.getSize(); - - this.resizeTo(s.w + w, s.h + h); - }, - - update : function(k) { - var t = this, b, dom = t.dom; - - if (tinymce.isIE6 && t.settings.blocker) { - k = k || ''; - - // Ignore getters - if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0) - return; - - // Remove blocker on remove - if (k == 'remove') { - dom.remove(t.blocker); - return; - } - - if (!t.blocker) { - t.blocker = dom.uniqueId(); - b = dom.add(t.settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'}); - dom.setStyle(b, 'opacity', 0); - } else - b = dom.get(t.blocker); - - dom.setStyle(b, 'left', t.getStyle('left', 1)); - dom.setStyle(b, 'top', t.getStyle('top', 1)); - dom.setStyle(b, 'width', t.getStyle('width', 1)); - dom.setStyle(b, 'height', t.getStyle('height', 1)); - dom.setStyle(b, 'display', t.getStyle('display', 1)); - dom.setStyle(b, 'zIndex', parseInt(t.getStyle('zIndex', 1) || 0) - 1); - } - } - }); + + tinymce.extend(t, { + on : function(n, f, s) { + return tinymce.dom.Event.add(t.id, n, f, s); + }, + + getXY : function() { + return { + x : parseInt(t.getStyle('left')), + y : parseInt(t.getStyle('top')) + }; + }, + + getSize : function() { + var n = dom.get(t.id); + + return { + w : parseInt(t.getStyle('width') || n.clientWidth), + h : parseInt(t.getStyle('height') || n.clientHeight) + }; + }, + + moveTo : function(x, y) { + t.setStyles({left : x, top : y}); + }, + + moveBy : function(x, y) { + var p = t.getXY(); + + t.moveTo(p.x + x, p.y + y); + }, + + resizeTo : function(w, h) { + t.setStyles({width : w, height : h}); + }, + + resizeBy : function(w, h) { + var s = t.getSize(); + + t.resizeTo(s.w + w, s.h + h); + }, + + update : function(k) { + var b; + + if (tinymce.isIE6 && settings.blocker) { + k = k || ''; + + // Ignore getters + if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0) + return; + + // Remove blocker on remove + if (k == 'remove') { + dom.remove(t.blocker); + return; + } + + if (!t.blocker) { + t.blocker = dom.uniqueId(); + b = dom.add(settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'}); + dom.setStyle(b, 'opacity', 0); + } else + b = dom.get(t.blocker); + + dom.setStyles(b, { + left : t.getStyle('left', 1), + top : t.getStyle('top', 1), + width : t.getStyle('width', 1), + height : t.getStyle('height', 1), + display : t.getStyle('display', 1), + zIndex : parseInt(t.getStyle('zIndex', 1) || 0) - 1 + }); + } + } + }); + }; })(tinymce); + (function(tinymce) { function trimNl(s) { return s.replace(/[\n\r]+/g, ''); @@ -4781,6 +5004,9 @@ window.tinymce.dom.Sizzle = Sizzle; if (!t.win.getSelection) t.tridentSel = new tinymce.dom.TridentSelection(t); + if (tinymce.isIE && dom.boxModel) + this._fixIESelection(); + // Prevent leaks tinymce.addUnload(t.destroy, t); }, @@ -4838,23 +5064,38 @@ window.tinymce.dom.Sizzle = Sizzle; h += '_'; // Delete and insert new node - r.deleteContents(); - r.insertNode(t.getRng().createContextualFragment(h)); + + if (r.startContainer == d && r.endContainer == d) { + // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents + d.body.innerHTML = h; + } else { + r.deleteContents(); + if (d.body.childNodes.length == 0) { + d.body.innerHTML = h; + } else { + // createContextualFragment doesn't exists in IE 9 DOMRanges + if (r.createContextualFragment) { + r.insertNode(r.createContextualFragment(h)); + } else { + // Fake createContextualFragment call in IE 9 + var frag = d.createDocumentFragment(), temp = d.createElement('div'); + + frag.appendChild(temp); + temp.outerHTML = h; + + r.insertNode(frag); + } + } + } // Move to caret marker c = t.dom.get('__caret'); - // Make sure we wrap it compleatly, Opera fails with a simple select call r = d.createRange(); r.setStartBefore(c); - r.setEndAfter(c); + r.setEndBefore(c); t.setRng(r); - // Delete the marker, and hopefully the caret gets placed in the right location - // Removed this since it seems to remove   in FF and simply deleting it - // doesn't seem to affect the caret position in any browser - //d.execCommand('Delete', false, null); - // Remove the caret position t.dom.remove('__caret'); } else { @@ -4872,34 +5113,50 @@ window.tinymce.dom.Sizzle = Sizzle; }, getStart : function() { - var t = this, r = t.getRng(), e; + var rng = this.getRng(), startElement, parentElement, checkRng, node; - if (isIE) { - if (r.item) - return r.item(0); + if (rng.duplicate || rng.item) { + // Control selection, return first item + if (rng.item) + return rng.item(0); - r = r.duplicate(); - r.collapse(1); - e = r.parentElement(); + // Get start element + checkRng = rng.duplicate(); + checkRng.collapse(1); + startElement = checkRng.parentElement(); - if (e && e.nodeName == 'BODY') - return e.firstChild; + // Check if range parent is inside the start element, then return the inner parent element + // This will fix issues when a single element is selected, IE would otherwise return the wrong start element + parentElement = node = rng.parentElement(); + while (node = node.parentNode) { + if (node == startElement) { + startElement = parentElement; + break; + } + } - return e; + // If start element is body element try to move to the first child if it exists + if (startElement && startElement.nodeName == 'BODY') + return startElement.firstChild || startElement; + + return startElement; } else { - e = r.startContainer; + startElement = rng.startContainer; - if (e.nodeName == 'BODY') - return e.firstChild; + if (startElement.nodeType == 1 && startElement.hasChildNodes()) + startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)]; - return t.dom.getParent(e, '*'); + if (startElement && startElement.nodeType == 3) + return startElement.parentNode; + + return startElement; } }, getEnd : function() { - var t = this, r = t.getRng(), e; + var t = this, r = t.getRng(), e, eo; - if (isIE) { + if (r.duplicate || r.item) { if (r.item) return r.item(0); @@ -4908,331 +5165,302 @@ window.tinymce.dom.Sizzle = Sizzle; e = r.parentElement(); if (e && e.nodeName == 'BODY') - return e.lastChild; + return e.lastChild || e; return e; } else { e = r.endContainer; + eo = r.endOffset; - if (e.nodeName == 'BODY') - return e.lastChild; + if (e.nodeType == 1 && e.hasChildNodes()) + e = e.childNodes[eo > 0 ? eo - 1 : eo]; - return t.dom.getParent(e, '*'); + if (e && e.nodeType == 3) + return e.parentNode; + + return e; } }, - getBookmark : function(si) { - var t = this, r = t.getRng(), tr, sx, sy, vp = t.dom.getViewPort(t.win), e, sp, bp, le, c = -0xFFFFFF, s, ro = t.dom.getRoot(), wb = 0, wa = 0, nv; - sx = vp.x; - sy = vp.y; + getBookmark : function(type, normalized) { + var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles; - // Simple bookmark fast but not as persistent - if (si) - return {rng : r, scrollX : sx, scrollY : sy}; + function findIndex(name, element) { + var index = 0; - // Handle IE - if (isIE) { - // Control selection - if (r.item) { - e = r.item(0); + each(dom.select(name), function(node, i) { + if (node == element) + index = i; + }); - each(t.dom.select(e.nodeName), function(n, i) { - if (e == n) { - sp = i; - return false; + return index; + }; + + if (type == 2) { + function getLocation() { + var rng = t.getRng(true), root = dom.getRoot(), bookmark = {}; + + function getPoint(rng, start) { + var container = rng[start ? 'startContainer' : 'endContainer'], + offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0; + + if (container.nodeType == 3) { + if (normalized) { + for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) + offset += node.nodeValue.length; + } + + point.push(offset); + } else { + childNodes = container.childNodes; + + if (offset >= childNodes.length && childNodes.length) { + after = 1; + offset = Math.max(0, childNodes.length - 1); + } + + point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after); } - }); - return { - tag : e.nodeName, - index : sp, - scrollX : sx, - scrollY : sy + for (; container && container != root; container = container.parentNode) + point.push(t.dom.nodeIndex(container, normalized)); + + return point; }; - } + bookmark.start = getPoint(rng, true); + + if (!t.isCollapsed()) + bookmark.end = getPoint(rng); + + return bookmark; + }; + + return getLocation(); + } + + // Handle simple range + if (type) + return {rng : t.getRng()}; + + rng = t.getRng(); + id = dom.uniqueId(); + collapsed = tinyMCE.activeEditor.selection.isCollapsed(); + styles = 'overflow:hidden;line-height:0px'; + + // Explorer method + if (rng.duplicate || rng.item) { // Text selection - tr = t.dom.doc.body.createTextRange(); - tr.moveToElementText(ro); - tr.collapse(true); - bp = Math.abs(tr.move('character', c)); + if (!rng.item) { + rng2 = rng.duplicate(); - tr = r.duplicate(); - tr.collapse(true); - sp = Math.abs(tr.move('character', c)); + // Insert start marker + rng.collapse(); + rng.pasteHTML('' + chr + ''); - tr = r.duplicate(); - tr.collapse(false); - le = Math.abs(tr.move('character', c)) - sp; - - return { - start : sp - bp, - length : le, - scrollX : sx, - scrollY : sy - }; - } - - // Handle W3C - e = t.getNode(); - s = t.getSel(); - - if (!s) - return null; - - // Image selection - if (e && e.nodeName == 'IMG') { - return { - scrollX : sx, - scrollY : sy - }; - } - - // Text selection - - function getPos(r, sn, en) { - var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}; - - while ((n = w.nextNode()) != null) { - if (n == sn) - d.start = p; - - if (n == en) { - d.end = p; - return d; + // Insert end marker + if (!collapsed) { + rng2.collapse(false); + rng2.pasteHTML('' + chr + ''); } - - p += trimNl(n.nodeValue || '').length; - } - - return null; - }; - - // Caret or selection - if (s.anchorNode == s.focusNode && s.anchorOffset == s.focusOffset) { - e = getPos(ro, s.anchorNode, s.focusNode); - - if (!e) - return {scrollX : sx, scrollY : sy}; - - // Count whitespace before - trimNl(s.anchorNode.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;}); - - return { - start : Math.max(e.start + s.anchorOffset - wb, 0), - end : Math.max(e.end + s.focusOffset - wb, 0), - scrollX : sx, - scrollY : sy, - beg : s.anchorOffset - wb == 0 - }; - } else { - e = getPos(ro, r.startContainer, r.endContainer); - - // Count whitespace before start and end container - //(r.startContainer.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;}); - //(r.endContainer.nodeValue || '').replace(/^\s+/, function(a) {wa = a.length;}); - - if (!e) - return {scrollX : sx, scrollY : sy}; - - return { - start : Math.max(e.start + r.startOffset - wb, 0), - end : Math.max(e.end + r.endOffset - wa, 0), - scrollX : sx, - scrollY : sy, - beg : r.startOffset - wb == 0 - }; - } - }, - - moveToBookmark : function(b) { - var t = this, r = t.getRng(), s = t.getSel(), ro = t.dom.getRoot(), sd, nvl, nv; - - function getPos(r, sp, ep) { - var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}, o, v, wa, wb; - - while ((n = w.nextNode()) != null) { - wa = wb = 0; - - nv = n.nodeValue || ''; - //nv.replace(/^\s+[^\s]/, function(a) {wb = a.length - 1;}); - //nv.replace(/[^\s]\s+$/, function(a) {wa = a.length - 1;}); - - nvl = trimNl(nv).length; - p += nvl; - - if (p >= sp && !d.startNode) { - o = sp - (p - nvl); - - // Fix for odd quirk in FF - if (b.beg && o >= nvl) - continue; - - d.startNode = n; - d.startOffset = o + wb; - } - - if (p >= ep) { - d.endNode = n; - d.endOffset = ep - (p - nvl) + wb; - return d; - } - } - - return null; - }; - - if (!b) - return false; - - t.win.scrollTo(b.scrollX, b.scrollY); - - // Handle explorer - if (isIE) { - // Handle simple - if (r = b.rng) { - try { - r.select(); - } catch (ex) { - // Ignore - } - - return true; - } - - t.win.focus(); - - // Handle control bookmark - if (b.tag) { - r = ro.createControlRange(); - - each(t.dom.select(b.tag), function(n, i) { - if (i == b.index) - r.addElement(n); - }); } else { - // Try/catch needed since this operation breaks when TinyMCE is placed in hidden divs/tabs - try { - // Incorrect bookmark - if (b.start < 0) - return true; + // Control selection + element = rng.item(0); + name = element.nodeName; - r = s.createRange(); - r.moveToElementText(ro); - r.collapse(true); - r.moveStart('character', b.start); - r.moveEnd('character', b.length); - } catch (ex2) { - return true; - } + return {name : name, index : findIndex(name, element)}; + } + } else { + element = t.getNode(); + name = element.nodeName; + if (name == 'IMG') + return {name : name, index : findIndex(name, element)}; + + // W3C method + rng2 = rng.cloneRange(); + + // Insert end marker + if (!collapsed) { + rng2.collapse(false); + rng2.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_end', style : styles}, chr)); } - try { - r.select(); - } catch (ex) { - // Needed for some odd IE bug #1843306 - } - - return true; + rng.collapse(true); + rng.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_start', style : styles}, chr)); } - // Handle W3C - if (!s) - return false; + t.moveToBookmark({id : id, keep : 1}); - // Handle simple - if (b.rng) { - s.removeAllRanges(); - s.addRange(b.rng); - } else { - if (is(b.start) && is(b.end)) { - try { - sd = getPos(ro, b.start, b.end); + return {id : id}; + }, - if (sd) { - r = t.dom.doc.createRange(); - r.setStart(sd.startNode, sd.startOffset); - r.setEnd(sd.endNode, sd.endOffset); - s.removeAllRanges(); - s.addRange(r); + moveToBookmark : function(bookmark) { + var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset; + + // Clear selection cache + if (t.tridentSel) + t.tridentSel.destroy(); + + if (bookmark) { + if (bookmark.start) { + rng = dom.createRng(); + root = dom.getRoot(); + + function setEndPoint(start) { + var point = bookmark[start ? 'start' : 'end'], i, node, offset, children; + + if (point) { + // Find container node + for (node = root, i = point.length - 1; i >= 1; i--) { + children = node.childNodes; + + if (children.length) + node = children[point[i]]; + } + + // Set offset within container node + if (start) + rng.setStart(node, point[0]); + else + rng.setEnd(node, point[0]); } + }; - if (!tinymce.isOpera) - t.win.focus(); - } catch (ex) { - // Ignore + setEndPoint(true); + setEndPoint(); + + t.setRng(rng); + } else if (bookmark.id) { + function restoreEndPoint(suffix) { + var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep; + + if (marker) { + node = marker.parentNode; + + if (suffix == 'start') { + if (!keep) { + idx = dom.nodeIndex(marker); + } else { + node = marker.firstChild; + idx = 1; + } + + startContainer = endContainer = node; + startOffset = endOffset = idx; + } else { + if (!keep) { + idx = dom.nodeIndex(marker); + } else { + node = marker.firstChild; + idx = 1; + } + + endContainer = node; + endOffset = idx; + } + + if (!keep) { + prev = marker.previousSibling; + next = marker.nextSibling; + + // Remove all marker text nodes + each(tinymce.grep(marker.childNodes), function(node) { + if (node.nodeType == 3) + node.nodeValue = node.nodeValue.replace(/\uFEFF/g, ''); + }); + + // Remove marker but keep children if for example contents where inserted into the marker + // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature + while (marker = dom.get(bookmark.id + '_' + suffix)) + dom.remove(marker, 1); + + // If siblings are text nodes then merge them unless it's Opera since it some how removes the node + // and we are sniffing since adding a lot of detection code for a browser with 3% of the market isn't worth the effort. Sorry, Opera but it's just a fact + if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !tinymce.isOpera) { + idx = prev.nodeValue.length; + prev.appendData(next.nodeValue); + dom.remove(next); + + if (suffix == 'start') { + startContainer = endContainer = prev; + startOffset = endOffset = idx; + } else { + endContainer = prev; + endOffset = idx; + } + } + } + } + }; + + function addBogus(node) { + // Adds a bogus BR element for empty block elements + // on non IE browsers just to have a place to put the caret + if (!isIE && dom.isBlock(node) && !node.innerHTML) + node.innerHTML = '
'; + + return node; + }; + + // Restore start/end points + restoreEndPoint('start'); + restoreEndPoint('end'); + + if (startContainer) { + rng = dom.createRng(); + rng.setStart(addBogus(startContainer), startOffset); + rng.setEnd(addBogus(endContainer), endOffset); + t.setRng(rng); } - } + } else if (bookmark.name) { + t.select(dom.select(bookmark.name)[bookmark.index]); + } else if (bookmark.rng) + t.setRng(bookmark.rng); } }, - select : function(n, c) { - var t = this, r = t.getRng(), s = t.getSel(), b, fn, ln, d = t.win.document; + select : function(node, content) { + var t = this, dom = t.dom, rng = dom.createRng(), idx; - function find(n, start) { - var walker, o; + idx = dom.nodeIndex(node); + rng.setStart(node.parentNode, idx); + rng.setEnd(node.parentNode, idx + 1); - if (n) { - walker = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false); + // Find first/last text node or BR element + if (content) { + function setPoint(node, start) { + var walker = new tinymce.dom.TreeWalker(node, node); - // Find first/last non empty text node - while (n = walker.nextNode()) { - o = n; - - if (tinymce.trim(n.nodeValue).length != 0) { + do { + // Text node + if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) { if (start) - return n; + rng.setStart(node, 0); else - o = n; + rng.setEnd(node, node.nodeValue.length); + + return; } - } - } - return o; - }; + // BR element + if (node.nodeName == 'BR') { + if (start) + rng.setStartBefore(node); + else + rng.setEndBefore(node); - if (isIE) { - try { - b = d.body; + return; + } + } while (node = (start ? walker.next() : walker.prev())); + }; - if (/^(IMG|TABLE)$/.test(n.nodeName)) { - r = b.createControlRange(); - r.addElement(n); - } else { - r = b.createTextRange(); - r.moveToElementText(n); - } - - r.select(); - } catch (ex) { - // Throws illigal agrument in IE some times - } - } else { - if (c) { - fn = find(n, 1) || t.dom.select('br:first', n)[0]; - ln = find(n, 0) || t.dom.select('br:last', n)[0]; - - if (fn && ln) { - r = d.createRange(); - - if (fn.nodeName == 'BR') - r.setStartBefore(fn); - else - r.setStart(fn, 0); - - if (ln.nodeName == 'BR') - r.setEndBefore(ln); - else - r.setEnd(ln, ln.nodeValue.length); - } else - r.selectNode(n); - } else - r.selectNode(n); - - t.setRng(r); + setPoint(node, 1); + setPoint(node); } - return n; + t.setRng(rng); + + return node; }, isCollapsed : function() { @@ -5241,7 +5469,10 @@ window.tinymce.dom.Sizzle = Sizzle; if (!r || r.item) return false; - return !s || r.boundingWidth == 0 || r.collapsed; + if (r.compareEndPoints) + return r.compareEndPoints('StartToEnd', r) === 0; + + return !s || r.collapsed; }, collapse : function(b) { @@ -5265,7 +5496,7 @@ window.tinymce.dom.Sizzle = Sizzle; }, getRng : function(w3c) { - var t = this, s, r; + var t = this, s, r, elm, doc = t.win.document; // Found tridentSel object then we need to use that one if (w3c && t.tridentSel) @@ -5273,29 +5504,49 @@ window.tinymce.dom.Sizzle = Sizzle; try { if (s = t.getSel()) - r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : t.win.document.createRange()); + r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : doc.createRange()); } catch (ex) { // IE throws unspecified error here if TinyMCE is placed in a frame/iframe } + // We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet + if (tinymce.isIE && r.setStart && doc.selection.createRange().item) { + elm = doc.selection.createRange().item(0); + r = doc.createRange(); + r.setStartBefore(elm); + r.setEndAfter(elm); + } + // No range found then create an empty one // This can occur when the editor is placed in a hidden container element on Gecko // Or on IE when there was an exception if (!r) - r = isIE ? t.win.document.body.createTextRange() : t.win.document.createRange(); + r = doc.createRange ? doc.createRange() : doc.body.createTextRange(); + if (t.selectedRange && t.explicitRange) { + if (r.compareBoundaryPoints(r.START_TO_START, t.selectedRange) === 0 && r.compareBoundaryPoints(r.END_TO_END, t.selectedRange) === 0) { + // Safari, Opera and Chrome only ever select text which causes the range to change. + // This lets us use the originally set range if the selection hasn't been changed by the user. + r = t.explicitRange; + } else { + t.selectedRange = null; + t.explicitRange = null; + } + } return r; }, setRng : function(r) { var s, t = this; - + if (!t.tridentSel) { s = t.getSel(); if (s) { + t.explicitRange = r; s.removeAllRanges(); s.addRange(r); + t.selectedRange = s.getRangeAt(0); } } else { // Is W3C Range @@ -5322,33 +5573,36 @@ window.tinymce.dom.Sizzle = Sizzle; }, getNode : function() { - var t = this, r = t.getRng(), s = t.getSel(), e; + var t = this, rng = t.getRng(), sel = t.getSel(), elm; - if (!isIE) { + if (rng.setStart) { // Range maybe lost after the editor is made visible again - if (!r) + if (!rng) return t.dom.getRoot(); - e = r.commonAncestorContainer; + elm = rng.commonAncestorContainer; // Handle selection a image or other control like element such as anchors - if (!r.collapsed) { - // If the anchor node is a element instead of a text node then return this element - if (tinymce.isWebKit && s.anchorNode && s.anchorNode.nodeType == 1) - return s.anchorNode.childNodes[s.anchorOffset]; - - if (r.startContainer == r.endContainer) { - if (r.startOffset - r.endOffset < 2) { - if (r.startContainer.hasChildNodes()) - e = r.startContainer.childNodes[r.startOffset]; + if (!rng.collapsed) { + if (rng.startContainer == rng.endContainer) { + if (rng.startOffset - rng.endOffset < 2) { + if (rng.startContainer.hasChildNodes()) + elm = rng.startContainer.childNodes[rng.startOffset]; } } + + // If the anchor node is a element instead of a text node then return this element + if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1) + return sel.anchorNode.childNodes[sel.anchorOffset]; } - return t.dom.getParent(e, '*'); + if (elm && elm.nodeType == 3) + return elm.parentNode; + + return elm; } - return r.item ? r.item(0) : r.parentElement(); + return rng.item ? rng.item(0) : rng.parentElement(); }, getSelectedBlocks : function(st, en) { @@ -5386,10 +5640,82 @@ window.tinymce.dom.Sizzle = Sizzle; // Manual destroy then remove unload handler if (!s) tinymce.removeUnload(t.destroy); - } + }, - }); + // IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode + _fixIESelection : function() { + var dom = this.dom, doc = dom.doc, body = doc.body, started, startRng; + + // Make HTML element unselectable since we are going to handle selection by hand + doc.documentElement.unselectable = true; + + // Return range from point or null if it failed + function rngFromPoint(x, y) { + var rng = body.createTextRange(); + + try { + rng.moveToPoint(x, y); + } catch (ex) { + // IE sometimes throws and exception, so lets just ignore it + rng = null; + } + + return rng; + }; + + // Fires while the selection is changing + function selectionChange(e) { + var pointRng; + + // Check if the button is down or not + if (e.button) { + // Create range from mouse position + pointRng = rngFromPoint(e.x, e.y); + + if (pointRng) { + // Check if pointRange is before/after selection then change the endPoint + if (pointRng.compareEndPoints('StartToStart', startRng) > 0) + pointRng.setEndPoint('StartToStart', startRng); + else + pointRng.setEndPoint('EndToEnd', startRng); + + pointRng.select(); + } + } else + endSelection(); + } + + // Removes listeners + function endSelection() { + dom.unbind(doc, 'mouseup', endSelection); + dom.unbind(doc, 'mousemove', selectionChange); + started = 0; + }; + + // Detect when user selects outside BODY + dom.bind(doc, 'mousedown', function(e) { + if (e.target.nodeName === 'HTML') { + if (started) + endSelection(); + + started = 1; + + // Setup start position + startRng = rngFromPoint(e.x, e.y); + if (startRng) { + // Listen for selection change events + dom.bind(doc, 'mouseup', endSelection); + dom.bind(doc, 'mousemove', selectionChange); + + dom.win.focus(); + startRng.select(); + } + } + }); + } + }); })(tinymce); + (function(tinymce) { tinymce.create('tinymce.dom.XMLWriter', { node : null, @@ -5471,7 +5797,7 @@ window.tinymce.dom.Sizzle = Sizzle; var h; h = this.doc.xml || new XMLSerializer().serializeToString(this.doc); - h = h.replace(/<\?[^?]+\?>||<\/html>||]+>/g, ''); + h = h.replace(/<\?[^?]+\?>|]*>|<\/html>||]+>/g, ''); h = h.replace(/ ?\/>/g, ' />'); if (this.valid) @@ -5479,10 +5805,12 @@ window.tinymce.dom.Sizzle = Sizzle; return h; } - - }); + }); })(tinymce); + (function(tinymce) { + var attrsCharsRegExp = /[&\"<>]/g, textCharsRegExp = /[<>&]/g, encodedChars = {'&' : '&', '"' : '"', '<' : '<', '>' : '>'}; + tinymce.create('tinymce.dom.StringWriter', { str : null, tags : null, @@ -5513,12 +5841,16 @@ window.tinymce.dom.Sizzle = Sizzle; this.inAttr = true; this.count++; this.elementCount = this.count; + this.attrs = {}; }, writeAttribute : function(n, v) { var t = this; - t.writeRaw(" " + t.encode(n) + '="' + t.encode(v) + '"'); + if (!t.attrs[n]) { + t.writeRaw(" " + t.encode(n, true) + '="' + t.encode(v, true) + '"'); + t.attrs[n] = v; + } }, writeEndElement : function() { @@ -5559,7 +5891,7 @@ window.tinymce.dom.Sizzle = Sizzle; writeComment : function(v) { this._writeAttributesEnd(); - this.writeRaw(''); + this.writeRaw(''); this.count++; }, @@ -5567,23 +5899,9 @@ window.tinymce.dom.Sizzle = Sizzle; this.str += v; }, - encode : function(s) { - return s.replace(/[<>&"]/g, function(v) { - switch (v) { - case '<': - return '<'; - - case '>': - return '>'; - - case '&': - return '&'; - - case '"': - return '"'; - } - - return v; + encode : function(s, attr) { + return s.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(v) { + return encodedChars[v]; }); }, @@ -5606,9 +5924,9 @@ window.tinymce.dom.Sizzle = Sizzle; return true; } - - }); + }); })(tinymce); + (function(tinymce) { // Shorten names var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko; @@ -5632,27 +5950,29 @@ window.tinymce.dom.Sizzle = Sizzle; t.writer = new tinymce.dom.StringWriter(); } + // IE9 broke the XML attributes order so it can't be used anymore + if (tinymce.isIE && document.documentMode > 8) { + t.writer = new tinymce.dom.StringWriter(); + } + // Default settings t.settings = s = extend({ dom : tinymce.DOM, valid_nodes : 0, node_filter : 0, attr_filter : 0, - invalid_attrs : /^(mce_|_moz_|sizset|sizcache)/, + invalid_attrs : /^(_mce_|_moz_|sizset|sizcache)/, 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, invalid_elements : 0, fix_table_elements : 1, fix_list_elements : true, fix_content_duplication : true, convert_fonts_to_spans : false, font_size_classes : 0, - font_size_style_values : 0, apply_source_formatting : 0, indent_mode : 'simple', indent_char : '\t', @@ -5663,6 +5983,11 @@ window.tinymce.dom.Sizzle = Sizzle; }, s); t.dom = s.dom; + t.schema = s.schema; + + // Use raw entities if no entities are defined + if (s.entity_encoding == 'named' && !s.entities) + s.entity_encoding = 'raw'; if (s.remove_redundant_brs) { t.onPostProcess.add(function(se, o) { @@ -5726,20 +6051,27 @@ window.tinymce.dom.Sizzle = Sizzle; if (s.fix_table_elements) { t.onPreProcess.add(function(se, o) { - each(t.dom.select('p table', o.node), function(n) { - // 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) - n.outerHTML = n.outerHTML; + // 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'); - t.dom.split(t.dom.getParent(n, 'p'), n); - }); + 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 + } + } + }); + } }); } }, setEntities : function(s) { - var t = this, a, i, l = {}, re = '', v; + var t = this, a, i, l = {}, v; // No need to setup more than once if (t.entityLookup) @@ -5757,94 +6089,11 @@ window.tinymce.dom.Sizzle = Sizzle; l[String.fromCharCode(a[i])] = a[i + 1]; v = parseInt(a[i]).toString(16); - re += '\\u' + '0000'.substring(v.length) + v; } - if (!re) { - t.settings.entity_encoding = 'raw'; - return; - } - - t.entitiesRE = new RegExp('[' + re + ']', 'g'); t.entityLookup = l; }, - setValidChildRules : function(s) { - this.childRules = null; - this.addValidChildRules(s); - }, - - addValidChildRules : function(s) { - var t = this, inst, intr, bloc; - - if (!s) - return; - - inst = 'A|BR|SPAN|BDO|MAP|OBJECT|IMG|TT|I|B|BIG|SMALL|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|#text|#comment'; - intr = 'A|BR|SPAN|BDO|OBJECT|APPLET|IMG|MAP|IFRAME|TT|I|B|U|S|STRIKE|BIG|SMALL|FONT|BASEFONT|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|INPUT|SELECT|TEXTAREA|LABEL|BUTTON|#text|#comment'; - bloc = 'H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|FORM|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP'; - - each(s.split(','), function(s) { - var p = s.split(/\[|\]/), re; - - s = ''; - each(p[1].split('|'), function(v) { - if (s) - s += '|'; - - switch (v) { - case '%itrans': - v = intr; - break; - - case '%itrans_na': - v = intr.substring(2); - break; - - case '%istrict': - v = inst; - break; - - case '%istrict_na': - v = inst.substring(2); - break; - - case '%btrans': - v = bloc; - break; - - case '%bstrict': - v = bloc; - break; - } - - s += v; - }); - re = new RegExp('^(' + s.toLowerCase() + ')$', 'i'); - - each(p[0].split('/'), function(s) { - t.childRules = t.childRules || {}; - t.childRules[s] = re; - }); - }); - - // Build regex - s = ''; - each(t.childRules, function(v, k) { - if (s) - s += '|'; - - s += k; - }); - - t.parentElementsRE = new RegExp('^(' + s.toLowerCase() + ')$', 'i'); - - /*console.debug(t.parentElementsRE.toString()); - each(t.childRules, function(v) { - console.debug(v.toString()); - });*/ - }, - setRules : function(s) { var t = this; @@ -6049,18 +6298,56 @@ window.tinymce.dom.Sizzle = Sizzle; }, 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(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); @@ -6073,11 +6360,16 @@ window.tinymce.dom.Sizzle = Sizzle; // Serialize HTML DOM into a string t.writer.reset(); + t._info = o; t._serializeNode(n, o.getInner); // 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); @@ -6122,23 +6414,23 @@ window.tinymce.dom.Sizzle = Sizzle; // This process is only done when getting contents out from the editor. if (!o.set) { // We need to replace paragraph whitespace with an nbsp before indentation to keep the \u00a0 char - h = h.replace(/

\s+<\/p>|]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? ' 

' : ' 

'); + h = tinymce._replace(/

\s+<\/p>|]+)>\s+<\/p>/g, s.entity_encoding == 'numeric' ? ' 

' : ' 

', h); if (s.remove_linebreaks) { h = h.replace(/\r?\n|\r/g, ' '); - h = h.replace(/(<[^>]+>)\s+/g, '$1 '); - h = h.replace(/\s+(<\/[^>]+>)/g, ' $1'); - h = h.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object) ([^>]+)>\s+/g, '<$1 $2>'); // Trim block start - h = h.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>\s+/g, '<$1>'); // Trim block start - h = h.replace(/\s+<\/(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>/g, ''); // Trim block end + h = tinymce._replace(/(<[^>]+>)\s+/g, '$1 ', h); + h = tinymce._replace(/\s+(<\/[^>]+>)/g, ' $1', h); + h = tinymce._replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object) ([^>]+)>\s+/g, '<$1 $2>', h); // Trim block start + h = tinymce._replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>\s+/g, '<$1>', h); // Trim block start + h = tinymce._replace(/\s+<\/(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>/g, '', h); // Trim block end } // Simple indentation if (s.apply_source_formatting && s.indent_mode == 'simple') { // Add line breaks before and after block elements - h = h.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n'); - h = h.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>'); - h = h.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '\n'); + h = tinymce._replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g, '\n<$1$2$3>\n', h); + h = tinymce._replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g, '\n<$1$2>', h); + h = tinymce._replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g, '\n', h); h = h.replace(/\n\n/g, '\n'); } } @@ -6146,14 +6438,11 @@ window.tinymce.dom.Sizzle = Sizzle; h = t._unprotect(h, p); // Restore CDATA sections - h = h.replace(//g, ''); - - // Restore scripts - h = h.replace(/(type|language)=\"mce-/g, '$1="'); + h = tinymce._replace(//g, '', h); // Restore the \u00a0 character if raw mode is enabled if (s.entity_encoding == 'raw') - h = h.replace(/

 <\/p>|]+)> <\/p>/g, '\u00a0

'); + h = tinymce._replace(/

 <\/p>|]+)> <\/p>/g, '\u00a0

', h); // Restore noscript elements h = h.replace(/]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) { @@ -6164,23 +6453,34 @@ window.tinymce.dom.Sizzle = Sizzle; o.content = h; }, - _serializeNode : function(n, inn) { - var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv, closed; + _serializeNode : function(n, inner) { + var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv, closed, keep, type, scopeName; if (!s.node_filter || s.node_filter(n)) { switch (n.nodeType) { case 1: // Element - if (n.hasAttribute ? n.hasAttribute('mce_bogus') : n.getAttribute('mce_bogus')) + if (n.hasAttribute ? n.hasAttribute('_mce_bogus') : n.getAttribute('_mce_bogus')) return; - iv = false; + iv = keep = false; hc = n.hasChildNodes(); - nn = n.getAttribute('mce_name') || n.nodeName.toLowerCase(); + nn = n.getAttribute('_mce_name') || n.nodeName.toLowerCase(); + + // Get internal type + type = n.getAttribute('_mce_type'); + if (type) { + if (!t._info.cleanup) { + iv = true; + return; + } else + keep = 1; + } // Add correct prefix on IE if (isIE) { - if (n.scopeName !== 'HTML' && n.scopeName !== 'html') - nn = n.scopeName + ':' + nn; + scopeName = n.scopeName; + if (scopeName && scopeName !== 'HTML' && scopeName !== 'html') + nn = scopeName + ':' + nn; } // Remove mce prefix on IE needed for the abbr element @@ -6188,18 +6488,20 @@ window.tinymce.dom.Sizzle = Sizzle; nn = nn.substring(4); // Check if valid - if (!t.validElementsRE || !t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inn) { - iv = true; - break; + if (!keep) { + if (!t.validElementsRE || !t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inner) { + iv = true; + break; + } } if (isIE) { // Fix IE content duplication (DOM can have multiple copies of the same node) if (s.fix_content_duplication) { - if (n.mce_serialized == t.key) + if (n._mce_serialized == t.key) return; - n.mce_serialized = t.key; + n._mce_serialized = t.key; } // IE sometimes adds a / infront of the node name @@ -6212,18 +6514,23 @@ window.tinymce.dom.Sizzle = Sizzle; } // Check if valid child - if (t.childRules) { - if (t.parentElementsRE.test(t.elementName)) { - if (!t.childRules[t.elementName].test(nn)) { - iv = true; - break; - } + if (s.validate_children) { + if (t.elementName && !t.schema.isValid(t.elementName, nn)) { + iv = true; + break; } t.elementName = nn; } ru = t.findRule(nn); + + // No valid rule for this element could be found then skip it + if (!ru) { + iv = true; + break; + } + nn = ru.name || nn; closed = s.closed.test(nn); @@ -6283,6 +6590,10 @@ window.tinymce.dom.Sizzle = Sizzle; } } + // Keep type attribute + if (type && keep) + w.writeAttribute('_mce_type', type); + // Write text from script if (nn === 'script' && tinymce.trim(n.innerHTML)) { w.writeText('// '); // Padd it with a comment so it will parse on older browsers @@ -6295,7 +6606,7 @@ window.tinymce.dom.Sizzle = Sizzle; if (ru.padd) { // If it has only one bogus child, padd it anyway workaround for
bug if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) { - if (cn.hasAttribute ? cn.hasAttribute('mce_bogus') : cn.getAttribute('mce_bogus')) + if (cn.hasAttribute ? cn.hasAttribute('_mce_bogus') : cn.getAttribute('_mce_bogus')) w.writeText('\u00a0'); } else if (!hc) w.writeText('\u00a0'); // No children then padd it @@ -6305,10 +6616,8 @@ window.tinymce.dom.Sizzle = Sizzle; case 3: // Text // Check if valid child - if (t.childRules && t.parentElementsRE.test(t.elementName)) { - if (!t.childRules[t.elementName].test(n.nodeName)) - return; - } + if (s.validate_children && t.elementName && !t.schema.isValid(t.elementName, '#text')) + return; return w.writeText(n.nodeValue); @@ -6401,7 +6710,7 @@ window.tinymce.dom.Sizzle = Sizzle; t.setEntities(s.entities); l = t.entityLookup; - h = h.replace(t.entitiesRE, function(a) { + h = h.replace(/[\u007E-\uFFFF]/g, function(a) { var v; if (v = l[a]) @@ -6431,7 +6740,6 @@ window.tinymce.dom.Sizzle = Sizzle; t.setRules(s.valid_elements); t.addRules(s.extended_valid_elements); - t.addValidChildRules(s.valid_child_elements); if (s.invalid_elements) t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$'); @@ -6454,16 +6762,6 @@ window.tinymce.dom.Sizzle = Sizzle; 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': @@ -6506,273 +6804,573 @@ window.tinymce.dom.Sizzle = Sizzle; return v; } - - }); + }); })(tinymce); + (function(tinymce) { - var each = tinymce.each, Event = tinymce.dom.Event; + tinymce.dom.ScriptLoader = function(settings) { + var QUEUED = 0, + LOADING = 1, + LOADED = 2, + states = {}, + queue = [], + scriptLoadedCallbacks = {}, + queueLoadedCallbacks = [], + loading = 0, + undefined; - tinymce.create('tinymce.dom.ScriptLoader', { - ScriptLoader : function(s) { - this.settings = s || {}; - this.queue = []; - this.lookup = {}; - }, + function loadScript(url, callback) { + var t = this, dom = tinymce.DOM, elm, uri, loc, id; - isDone : function(u) { - return this.lookup[u] ? this.lookup[u].state == 2 : 0; - }, + // Execute callback when script is loaded + function done() { + dom.remove(id); - markDone : function(u) { - this.lookup[u] = {state : 2, url : u}; - }, + if (elm) + elm.onreadystatechange = elm.onload = elm = null; - add : function(u, cb, s, pr) { - var t = this, lo = t.lookup, o; + callback(); + }; - if (o = lo[u]) { - // Is loaded fire callback - if (cb && o.state == 2) - cb.call(s || this); + id = dom.uniqueId(); - return o; - } + if (tinymce.isIE6) { + uri = new tinymce.util.URI(url); + loc = location; - o = {state : 0, url : u, func : cb, scope : s || this}; - - if (pr) - t.queue.unshift(o); - else - t.queue.push(o); - - lo[u] = o; - - return o; - }, - - load : function(u, cb, s) { - var t = this, o; - - if (o = t.lookup[u]) { - // Is loaded fire callback - if (cb && o.state == 2) - cb.call(s || t); - - return o; - } - - function loadScript(u) { - if (Event.domLoaded || t.settings.strict_mode) { + // If script is from same domain and we + // use IE 6 then use XHR since it's more reliable + if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol) { tinymce.util.XHR.send({ - url : tinymce._addVer(u), - error : t.settings.error, - async : false, - success : function(co) { - throw('evalremoved3'); + url : tinymce._addVer(uri.getURI()), + success : function(content) { + // Create new temp script element + var script = dom.create('script', { + type : 'text/javascript' + }); + + // Evaluate script in global scope + script.text = content; + document.getElementsByTagName('head')[0].appendChild(script); + dom.remove(script); + + done(); } }); - } else - document.write(''); - }; - if (!tinymce.is(u, 'string')) { - each(u, function(u) { - loadScript(u); - }); - - if (cb) - cb.call(s || t); - } else { - loadScript(u); - - if (cb) - cb.call(s || t); + return; + } } - }, - loadQueue : function(cb, s) { - var t = this; - - if (!t.queueLoading) { - t.queueLoading = 1; - t.queueCallbacks = []; - - t.loadScripts(t.queue, function() { - t.queueLoading = 0; - - if (cb) - cb.call(s || t); - - each(t.queueCallbacks, function(o) { - o.func.call(o.scope); - }); - }); - } else if (cb) - t.queueCallbacks.push({func : cb, scope : s || t}); - }, - - eval : function(co) { - var w = window; - - // Evaluate script - if (!w.execScript) { - try { - throw('evalremoved1'); - } catch (ex) { - throw('evalremoved2'); - } - } else - w.execScript(co); // IE - }, - - loadScripts : function(sc, cb, s) { - var t = this, lo = t.lookup; - - function done(o) { - o.state = 2; // Has been loaded - - // Run callback - if (o.func) - o.func.call(o.scope || t); - }; - - function allDone() { - var l; - - // Check if all files are loaded - l = sc.length; - each(sc, function(o) { - o = lo[o.url]; - - if (o.state === 2) {// It has finished loading - done(o); - l--; - } else - load(o); - }); - - // They are all loaded - if (l === 0 && cb) { - cb.call(s || t); - cb = 0; - } - }; - - function load(o) { - if (o.state > 0) - return; - - o.state = 1; // Is loading - - tinymce.dom.ScriptLoader.loadScript(o.url, function() { - done(o); - allDone(); - }); - }; - - each(sc, function(o) { - var u = o.url; - - // Add to queue if needed - if (!lo[u]) { - lo[u] = o; - t.queue.push(o); - } else - o = lo[u]; - - // Is already loading or has been loaded - if (o.state > 0) - return; - - if (!Event.domLoaded && !t.settings.strict_mode) { - var ix, ol = ''; - - // Add onload events - if (cb || o.func) { - o.state = 1; // Is loading - - ix = tinymce.dom.ScriptLoader._addOnLoad(function() { - done(o); - allDone(); - }); - - if (tinymce.isIE) - ol = ' onreadystatechange="'; - else - ol = ' onload="'; - - ol += 'tinymce.dom.ScriptLoader._onLoad(this,\'' + u + '\',' + ix + ');"'; - } - - document.write(''); - - if (!o.func) - done(o); - } else - load(o); + // Create new script element + elm = dom.create('script', { + id : id, + type : 'text/javascript', + src : tinymce._addVer(url) }); - allDone(); - }, + // Add onload listener for non IE browsers since IE9 + // fires onload event before the script is parsed and executed + if (!tinymce.isIE) + elm.onload = done; - // Static methods - 'static' : { - _addOnLoad : function(f) { - var t = this; + elm.onreadystatechange = function() { + var state = elm.readyState; - t._funcs = t._funcs || []; - t._funcs.push(f); + // Loaded state is passed on IE 6 however there + // are known issues with this method but we can't use + // XHR in a cross domain loading + if (state == 'complete' || state == 'loaded') + done(); + }; - return t._funcs.length - 1; - }, + // Most browsers support this feature so we report errors + // for those at least to help users track their missing plugins etc + // todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option + /*elm.onerror = function() { + alert('Failed to load: ' + url); + };*/ - _onLoad : function(e, u, ix) { - if (!tinymce.isIE || e.readyState == 'complete') - this._funcs[ix].call(this); - }, + // Add script to document + (document.getElementsByTagName('head')[0] || document.body).appendChild(elm); + }; - loadScript : function(u, cb) { - var id = tinymce.DOM.uniqueId(), e; + this.isDone = function(url) { + return states[url] == LOADED; + }; - function done() { - Event.clear(id); - tinymce.DOM.remove(id); + this.markDone = function(url) { + states[url] = LOADED; + }; - if (cb) { - cb.call(document, u); - cb = 0; - } - }; + this.add = this.load = function(url, callback, scope) { + var item, state = states[url]; - if (tinymce.isIE) { -/* Event.add(e, 'readystatechange', function(e) { - if (e.target && e.target.readyState == 'complete') - done(); - });*/ - - tinymce.util.XHR.send({ - url : tinymce._addVer(u), - async : false, - success : function(co) { - window.execScript(co); - done(); - } - }); - } else { - e = tinymce.DOM.create('script', {id : id, type : 'text/javascript', src : tinymce._addVer(u)}); - Event.add(e, 'load', done); - - // Check for head or body - (document.getElementsByTagName('head')[0] || document.body).appendChild(e); - } + // Add url to load queue + if (state == undefined) { + queue.push(url); + states[url] = QUEUED; } - } - }); + if (callback) { + // Store away callback for later execution + if (!scriptLoadedCallbacks[url]) + scriptLoadedCallbacks[url] = []; + + scriptLoadedCallbacks[url].push({ + func : callback, + scope : scope || this + }); + } + }; + + this.loadQueue = function(callback, scope) { + this.loadScripts(queue, callback, scope); + }; + + this.loadScripts = function(scripts, callback, scope) { + var loadScripts; + + function execScriptLoadedCallbacks(url) { + // Execute URL callback functions + tinymce.each(scriptLoadedCallbacks[url], function(callback) { + callback.func.call(callback.scope); + }); + + scriptLoadedCallbacks[url] = undefined; + }; + + queueLoadedCallbacks.push({ + func : callback, + scope : scope || this + }); + + loadScripts = function() { + var loadingScripts = tinymce.grep(scripts); + + // Current scripts has been handled + scripts.length = 0; + + // Load scripts that needs to be loaded + tinymce.each(loadingScripts, function(url) { + // Script is already loaded then execute script callbacks directly + if (states[url] == LOADED) { + execScriptLoadedCallbacks(url); + return; + } + + // Is script not loading then start loading it + if (states[url] != LOADING) { + states[url] = LOADING; + loading++; + + loadScript(url, function() { + states[url] = LOADED; + loading--; + + execScriptLoadedCallbacks(url); + + // Load more scripts if they where added by the recently loaded script + loadScripts(); + }); + } + }); + + // No scripts are currently loading then execute all pending queue loaded callbacks + if (!loading) { + tinymce.each(queueLoadedCallbacks, function(callback) { + callback.func.call(callback.scope); + }); + + queueLoadedCallbacks.length = 0; + } + }; + + loadScripts(); + }; + }; // Global script loader tinymce.ScriptLoader = new tinymce.dom.ScriptLoader(); })(tinymce); + +tinymce.dom.TreeWalker = function(start_node, root_node) { + var node = start_node; + + function findSibling(node, start_name, sibling_name, shallow) { + var sibling, parent; + + if (node) { + // Walk into nodes if it has a start + if (!shallow && node[start_name]) + return node[start_name]; + + // Return the sibling if it has one + if (node != root_node) { + sibling = node[sibling_name]; + if (sibling) + return sibling; + + // Walk up the parents to look for siblings + for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) { + sibling = parent[sibling_name]; + if (sibling) + return sibling; + } + } + } + }; + + this.current = function() { + return node; + }; + + this.next = function(shallow) { + return (node = findSibling(node, 'firstChild', 'nextSibling', shallow)); + }; + + this.prev = function(shallow) { + return (node = findSibling(node, 'lastChild', 'lastSibling', shallow)); + }; +}; + +(function() { + var transitional = {}; + + function unpack(lookup, data) { + var key; + + function replace(value) { + return value.replace(/[A-Z]+/g, function(key) { + return replace(lookup[key]); + }); + }; + + // Unpack lookup + for (key in lookup) { + if (lookup.hasOwnProperty(key)) + lookup[key] = replace(lookup[key]); + } + + // Unpack and parse data into object map + replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]/g, function(str, name, children) { + var i, map = {}; + + children = children.split(/\|/); + + for (i = children.length - 1; i >= 0; i--) + map[children[i]] = 1; + + transitional[name] = map; + }); + }; + + // This is the XHTML 1.0 transitional elements with it's children packed to reduce it's size + // we will later include the attributes here and use it as a default for valid elements but it + // requires us to rewrite the serializer engine + unpack({ + Z : '#|H|K|N|O|P', + Y : '#|X|form|R|Q', + X : 'p|T|div|U|W|isindex|fieldset|table', + W : 'pre|hr|blockquote|address|center|noframes', + U : 'ul|ol|dl|menu|dir', + ZC : '#|p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q', + T : 'h1|h2|h3|h4|h5|h6', + ZB : '#|X|S|Q', + S : 'R|P', + ZA : '#|a|G|J|M|O|P', + R : '#|a|H|K|N|O', + Q : 'noscript|P', + P : 'ins|del|script', + O : 'input|select|textarea|label|button', + N : 'M|L', + M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym', + L : 'sub|sup', + K : 'J|I', + J : 'tt|i|b|u|s|strike', + I : 'big|small|font|basefont', + H : 'G|F', + G : 'br|span|bdo', + F : 'object|applet|img|map|iframe' + }, 'script[]' + + 'style[]' + + 'object[#|param|X|form|a|H|K|N|O|Q]' + + 'param[]' + + 'p[S]' + + 'a[Z]' + + 'br[]' + + 'span[S]' + + 'bdo[S]' + + 'applet[#|param|X|form|a|H|K|N|O|Q]' + + 'h1[S]' + + 'img[]' + + 'map[X|form|Q|area]' + + 'h2[S]' + + 'iframe[#|X|form|a|H|K|N|O|Q]' + + 'h3[S]' + + 'tt[S]' + + 'i[S]' + + 'b[S]' + + 'u[S]' + + 's[S]' + + 'strike[S]' + + 'big[S]' + + 'small[S]' + + 'font[S]' + + 'basefont[]' + + 'em[S]' + + 'strong[S]' + + 'dfn[S]' + + 'code[S]' + + 'q[S]' + + 'samp[S]' + + 'kbd[S]' + + 'var[S]' + + 'cite[S]' + + 'abbr[S]' + + 'acronym[S]' + + 'sub[S]' + + 'sup[S]' + + 'input[]' + + 'select[optgroup|option]' + + 'optgroup[option]' + + 'option[]' + + 'textarea[]' + + 'label[S]' + + 'button[#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + + 'h4[S]' + + 'ins[#|X|form|a|H|K|N|O|Q]' + + 'h5[S]' + + 'del[#|X|form|a|H|K|N|O|Q]' + + 'h6[S]' + + 'div[#|X|form|a|H|K|N|O|Q]' + + 'ul[li]' + + 'li[#|X|form|a|H|K|N|O|Q]' + + 'ol[li]' + + 'dl[dt|dd]' + + 'dt[S]' + + 'dd[#|X|form|a|H|K|N|O|Q]' + + 'menu[li]' + + 'dir[li]' + + 'pre[ZA]' + + 'hr[]' + + 'blockquote[#|X|form|a|H|K|N|O|Q]' + + 'address[S|p]' + + 'center[#|X|form|a|H|K|N|O|Q]' + + 'noframes[#|X|form|a|H|K|N|O|Q]' + + 'isindex[]' + + 'fieldset[#|legend|X|form|a|H|K|N|O|Q]' + + 'legend[S]' + + 'table[caption|col|colgroup|thead|tfoot|tbody|tr]' + + 'caption[S]' + + 'col[]' + + 'colgroup[col]' + + 'thead[tr]' + + 'tr[th|td]' + + 'th[#|X|form|a|H|K|N|O|Q]' + + 'form[#|X|a|H|K|N|O|Q]' + + 'noscript[#|X|form|a|H|K|N|O|Q]' + + 'td[#|X|form|a|H|K|N|O|Q]' + + 'tfoot[tr]' + + 'tbody[tr]' + + 'area[]' + + 'base[]' + + 'body[#|X|form|a|H|K|N|O|Q]' + ); + + tinymce.dom.Schema = function() { + var t = this, elements = transitional; + + t.isValid = function(name, child_name) { + var element = elements[name]; + + return !!(element && (!child_name || element[child_name])); + }; + }; +})(); +(function(tinymce) { + tinymce.dom.RangeUtils = function(dom) { + var INVISIBLE_CHAR = '\uFEFF'; + + this.walk = function(rng, callback) { + var startContainer = rng.startContainer, + startOffset = rng.startOffset, + endContainer = rng.endContainer, + endOffset = rng.endOffset, + ancestor, startPoint, + endPoint, node, parent, siblings, nodes; + + // Handle table cell selection the table plugin enables + // you to fake select table cells and perform formatting actions on them + nodes = dom.select('td.mceSelected,th.mceSelected'); + if (nodes.length > 0) { + tinymce.each(nodes, function(node) { + callback([node]); + }); + + return; + } + + function collectSiblings(node, name, end_node) { + var siblings = []; + + for (; node && node != end_node; node = node[name]) + siblings.push(node); + + return siblings; + }; + + function findEndPoint(node, root) { + do { + if (node.parentNode == root) + return node; + + node = node.parentNode; + } while(node); + }; + + function walkBoundary(start_node, end_node, next) { + var siblingName = next ? 'nextSibling' : 'previousSibling'; + + for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) { + parent = node.parentNode; + siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName); + + if (siblings.length) { + if (!next) + siblings.reverse(); + + callback(siblings); + } + } + }; + + // If index based start position then resolve it + if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) + startContainer = startContainer.childNodes[startOffset]; + + // If index based end position then resolve it + if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) + endContainer = endContainer.childNodes[Math.min(startOffset == endOffset ? endOffset : endOffset - 1, endContainer.childNodes.length - 1)]; + + // Find common ancestor and end points + ancestor = dom.findCommonAncestor(startContainer, endContainer); + + // Same container + if (startContainer == endContainer) + return callback([startContainer]); + + // Process left side + for (node = startContainer; node; node = node.parentNode) { + if (node == endContainer) + return walkBoundary(startContainer, ancestor, true); + + if (node == ancestor) + break; + } + + // Process right side + for (node = endContainer; node; node = node.parentNode) { + if (node == startContainer) + return walkBoundary(endContainer, ancestor); + + if (node == ancestor) + break; + } + + // Find start/end point + startPoint = findEndPoint(startContainer, ancestor) || startContainer; + endPoint = findEndPoint(endContainer, ancestor) || endContainer; + + // Walk left leaf + walkBoundary(startContainer, startPoint, true); + + // Walk the middle from start to end point + siblings = collectSiblings( + startPoint == startContainer ? startPoint : startPoint.nextSibling, + 'nextSibling', + endPoint == endContainer ? endPoint.nextSibling : endPoint + ); + + if (siblings.length) + callback(siblings); + + // Walk right leaf + walkBoundary(endContainer, endPoint); + }; + + /* this.split = function(rng) { + var startContainer = rng.startContainer, + startOffset = rng.startOffset, + endContainer = rng.endContainer, + endOffset = rng.endOffset; + + function splitText(node, offset) { + if (offset == node.nodeValue.length) + node.appendData(INVISIBLE_CHAR); + + node = node.splitText(offset); + + if (node.nodeValue === INVISIBLE_CHAR) + node.nodeValue = ''; + + return node; + }; + + // Handle single text node + if (startContainer == endContainer) { + if (startContainer.nodeType == 3) { + if (startOffset != 0) + startContainer = endContainer = splitText(startContainer, startOffset); + + if (endOffset - startOffset != startContainer.nodeValue.length) + splitText(startContainer, endOffset - startOffset); + } + } else { + // Split startContainer text node if needed + if (startContainer.nodeType == 3 && startOffset != 0) { + startContainer = splitText(startContainer, startOffset); + startOffset = 0; + } + + // Split endContainer text node if needed + if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) { + endContainer = splitText(endContainer, endOffset).previousSibling; + endOffset = endContainer.nodeValue.length; + } + } + + return { + startContainer : startContainer, + startOffset : startOffset, + endContainer : endContainer, + endOffset : endOffset + }; + }; +*/ + }; + + tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) { + if (rng1 && rng2) { + // Compare native IE ranges + if (rng1.item || rng1.duplicate) { + // Both are control ranges and the selected element matches + if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0)) + return true; + + // Both are text ranges and the range matches + if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) + return true; + } else { + // Compare w3c ranges + return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset; + } + } + + return false; + }; +})(tinymce); + (function(tinymce) { // Shorten class names var DOM = tinymce.DOM, is = tinymce.is; @@ -6872,12 +7470,14 @@ window.tinymce.dom.Sizzle = Sizzle; destroy : function() { tinymce.dom.Event.clear(this.id); } - - }); -})(tinymce);tinymce.create('tinymce.ui.Container:tinymce.ui.Control', { + }); +})(tinymce); +tinymce.create('tinymce.ui.Container:tinymce.ui.Control', { Container : function(id, s) { this.parent(id, s); + this.controls = []; + this.lookup = {}; }, @@ -6891,8 +7491,8 @@ window.tinymce.dom.Sizzle = Sizzle; get : function(n) { return this.lookup[n]; } +}); - }); tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { Separator : function(id, s) { @@ -6903,8 +7503,8 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { renderHTML : function() { return tinymce.DOM.createHTML('span', {'class' : this.classPrefix}); } +}); - }); (function(tinymce) { var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk; @@ -6932,9 +7532,9 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { if (is(t.selected)) t.setSelected(t.selected); } - - }); + }); })(tinymce); + (function(tinymce) { var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk; @@ -7031,9 +7631,9 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { return m; } - - }); -})(tinymce);(function(tinymce) { + }); +})(tinymce); +(function(tinymce) { var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element; tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', { @@ -7359,9 +7959,9 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { DOM.addClass(ro, 'mceLast'); } - - }); -})(tinymce);(function(tinymce) { + }); +})(tinymce); +(function(tinymce) { var DOM = tinymce.DOM; tinymce.create('tinymce.ui.Button:tinymce.ui.Control', { @@ -7392,9 +7992,9 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { return s.onclick.call(s.scope, e); }); } - - }); + }); })(tinymce); + (function(tinymce) { var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher; @@ -7403,11 +8003,17 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { var t = this; t.parent(id, s); + t.items = []; + t.onChange = new Dispatcher(t); + t.onPostRender = new Dispatcher(t); + t.onAdd = new Dispatcher(t); + t.onRenderMenu = new tinymce.util.Dispatcher(this); + t.classPrefix = 'mceListBox'; }, @@ -7536,16 +8142,16 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { hideMenu : function(e) { var t = this; - // Prevent double toogles by canceling the mouse click event to the button - if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open')) - return; + if (t.menu && t.menu.isMenuVisible) { + // Prevent double toogles by canceling the mouse click event to the button + if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open')) + return; - if (!e || !DOM.getParent(e.target, '.mceMenu')) { - DOM.removeClass(t.id, t.classPrefix + 'Selected'); - Event.remove(DOM.doc, 'mousedown', t.hideMenu, t); - - if (t.menu) + if (!e || !DOM.getParent(e.target, '.mceMenu')) { + DOM.removeClass(t.id, t.classPrefix + 'Selected'); + Event.remove(DOM.doc, 'mousedown', t.hideMenu, t); t.menu.hideMenu(); + } } }, @@ -7571,13 +8177,25 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { }); each(t.items, function(o) { - o.id = DOM.uniqueId(); - o.onclick = function() { - if (t.settings.onselect(o.value) !== false) - t.select(o.value); // Must be runned after - }; + // No value then treat it as a title + if (o.value === undefined) { + m.add({ + title : o.title, + 'class' : 'mceMenuItemTitle', + onclick : function() { + if (t.settings.onselect('') !== false) + t.select(''); // Must be runned after + } + }); + } else { + o.id = DOM.uniqueId(); + o.onclick = function() { + if (t.settings.onselect(o.value) !== false) + t.select(o.value); // Must be runned after + }; - m.add(o); + m.add(o); + } }); t.onRenderMenu.dispatch(t, m); @@ -7588,7 +8206,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { var t = this, cp = t.classPrefix; Event.add(t.id, 'click', t.showMenu, t); - Event.add(t.id + '_text', 'focus', function(e) { + Event.add(t.id + '_text', 'focus', function() { if (!t._focused) { t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) { var idx = -1, v, kc = e.keyCode; @@ -7645,9 +8263,9 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { Event.clear(this.id + '_text'); Event.clear(this.id + '_open'); } - - }); -})(tinymce);(function(tinymce) { + }); +})(tinymce); +(function(tinymce) { var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher; tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', { @@ -7720,7 +8338,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { }, getLength : function() { - return DOM.get(this.id).options.length - 1; + return this.items.length; }, renderHTML : function() { @@ -7774,15 +8392,17 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { t.onPostRender.dispatch(t, DOM.get(t.id)); } - - }); -})(tinymce);(function(tinymce) { + }); +})(tinymce); +(function(tinymce) { var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each; tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', { MenuButton : function(id, s) { this.parent(id, s); + this.onRenderMenu = new tinymce.util.Dispatcher(this); + s.menu_container = s.menu_container || DOM.doc.body; }, @@ -7861,9 +8481,9 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { } }); } - - }); + }); })(tinymce); + (function(tinymce) { var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each; @@ -7927,9 +8547,9 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { Event.clear(this.id + '_action'); Event.clear(this.id + '_open'); } - - }); + }); })(tinymce); + (function(tinymce) { var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each; @@ -7946,6 +8566,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { }, t.settings); t.onShowMenu = new tinymce.util.Dispatcher(t); + t.onHideMenu = new tinymce.util.Dispatcher(t); t.value = s.default_color; @@ -8037,7 +8658,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { style : { backgroundColor : '#' + c }, - mce_color : '#' + c + _mce_color : '#' + c }); }); @@ -8059,7 +8680,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { e = e.target; - if (e.nodeName == 'A' && (c = e.getAttribute('mce_color'))) + if (e.nodeName == 'A' && (c = e.getAttribute('_mce_color'))) t.setColor(c); return Event.cancel(e); // Prevent IE auto save warning @@ -8093,9 +8714,9 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { Event.clear(this.id + '_more'); DOM.remove(this.id + '_menu'); } - - }); + }); })(tinymce); + tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { renderHTML : function() { var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl; @@ -8157,32 +8778,30 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { return dom.createHTML('table', {id : t.id, 'class' : 'mceToolbar' + (s['class'] ? ' ' + s['class'] : ''), cellpadding : '0', cellspacing : '0', align : t.settings.align || ''}, '' + h + ''); } +}); - }); (function(tinymce) { var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each; tinymce.create('tinymce.AddOnManager', { - items : [], - urls : {}, - lookup : {}, - onAdd : new Dispatcher(this), + AddOnManager : function() { + var self = this; + + self.items = []; + self.urls = {}; + self.lookup = {}; + self.onAdd = new Dispatcher(self); + }, get : function(n) { return this.lookup[n]; }, requireLangPack : function(n) { - var u, s = tinymce.EditorManager.settings; + var s = tinymce.settings; - if (s && s.language) { - u = this.urls[n] + '/langs/' + s.language + '.js'; - - if (!tinymce.dom.Event.domLoaded && !s.strict_mode) - tinymce.ScriptLoader.load(u); - else - tinymce.ScriptLoader.add(u); - } + if (s && s.language) + tinymce.ScriptLoader.add(this.urls[n] + '/langs/' + s.language + '.js'); }, add : function(id, o) { @@ -8203,47 +8822,57 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { u = tinymce.baseURL + '/' + u; t.urls[n] = u.substring(0, u.lastIndexOf('/')); - tinymce.ScriptLoader.add(u, cb, s); - } - }); + if (!t.lookup[n]) + tinymce.ScriptLoader.add(u, cb, s); + } + }); // Create plugin and theme managers tinymce.PluginManager = new tinymce.AddOnManager(); tinymce.ThemeManager = new tinymce.AddOnManager(); -}(tinymce));(function(tinymce) { - // Shorten names - var each = tinymce.each, extend = tinymce.extend, DOM = tinymce.DOM, Event = tinymce.dom.Event, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, explode = tinymce.explode; +}(tinymce)); + +(function(tinymce) { + // Shorten names + var each = tinymce.each, extend = tinymce.extend, + DOM = tinymce.DOM, Event = tinymce.dom.Event, + ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, + explode = tinymce.explode, + Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0; + + // Setup some URLs where the editor API is located and where the document is + tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, ''); + if (!/[\/\\]$/.test(tinymce.documentBaseURL)) + tinymce.documentBaseURL += '/'; + + tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL); + + tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL); + + // Add before unload listener + // This was required since IE was leaking memory if you added and removed beforeunload listeners + // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event + tinymce.onBeforeUnload = new Dispatcher(tinymce); + + // Must be on window or IE will leak if the editor is placed in frame or iframe + Event.add(window, 'beforeunload', function(e) { + tinymce.onBeforeUnload.dispatch(tinymce, e); + }); + + tinymce.onAddEditor = new Dispatcher(tinymce); + + tinymce.onRemoveEditor = new Dispatcher(tinymce); + + tinymce.EditorManager = extend(tinymce, { + editors : [], - tinymce.create('static tinymce.EditorManager', { - editors : {}, i18n : {}, + activeEditor : null, - preInit : function() { - var t = this, lo = window.location; - - // Setup some URLs where the editor API is located and where the document is - tinymce.documentBaseURL = lo.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, ''); - if (!/[\/\\]$/.test(tinymce.documentBaseURL)) - tinymce.documentBaseURL += '/'; - - tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL); - tinymce.EditorManager.baseURI = new tinymce.util.URI(tinymce.baseURL); - - // Add before unload listener - // This was required since IE was leaking memory if you added and removed beforeunload listeners - // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event - t.onBeforeUnload = new tinymce.util.Dispatcher(t); - - // Must be on window or IE will leak if the editor is placed in frame or iframe - Event.add(window, 'beforeunload', function(e) { - t.onBeforeUnload.dispatch(t, e); - }); - }, - init : function(s) { - var t = this, pl, sl = tinymce.ScriptLoader, c, e, el = [], ed; + var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed; function execCallback(se, n, s) { var f = se[n]; @@ -8262,87 +8891,17 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { s = extend({ theme : "simple", - language : "en", - strict_loading_mode : document.contentType == 'application/xhtml+xml' + language : "en" }, s); t.settings = s; - // If page not loaded and strict mode isn't enabled then load them - if (!Event.domLoaded && !s.strict_loading_mode) { - // Load language - if (s.language) - sl.add(tinymce.baseURL + '/langs/' + s.language + '.js'); - - // Load theme - if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme]) - ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js'); - - // Load plugins - if (s.plugins) { - pl = explode(s.plugins); - - // Load compat2x first - if (tinymce.inArray(pl, 'compat2x') != -1) - PluginManager.load('compat2x', 'plugins/compat2x/editor_plugin' + tinymce.suffix + '.js'); - - // Load rest if plugins - each(pl, function(v) { - if (v && v.charAt(0) != '-' && !PluginManager.urls[v]) { - // Skip safari plugin for other browsers - if (!tinymce.isWebKit && v == 'safari') - return; - - PluginManager.load(v, 'plugins/' + v + '/editor_plugin' + tinymce.suffix + '.js'); - } - }); - } - - sl.loadQueue(); - } - // Legacy call Event.add(document, 'init', function() { var l, co; execCallback(s, 'onpageload'); - // Verify that it's a valid browser - if (s.browsers) { - l = false; - - each(explode(s.browsers), function(v) { - switch (v) { - case 'ie': - case 'msie': - if (tinymce.isIE) - l = true; - break; - - case 'gecko': - if (tinymce.isGecko) - l = true; - break; - - case 'safari': - case 'webkit': - if (tinymce.isWebKit) - l = true; - break; - - case 'opera': - if (tinymce.isOpera) - l = true; - - break; - } - }); - - // Not a valid one - if (!l) - return; - } - switch (s.mode) { case "exact": l = s.elements || ''; @@ -8354,12 +8913,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { el.push(ed); ed.render(1); } else { - c = 0; - each(document.forms, function(f) { each(f.elements, function(e) { if (e.name === v) { - v = 'mce_editor_' + c; + v = 'mce_editor_' + instanceCounter++; DOM.setAttrib(e, 'id', v); ed = new tinymce.Editor(v, s); @@ -8405,7 +8962,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { if (s.oninit) { l = co = 0; - each (el, function(ed) { + each(el, function(ed) { co++; if (!ed.initialized) { @@ -8429,6 +8986,9 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { }, get : function(id) { + if (id === undefined) + return this.editors; + return this.editors[id]; }, @@ -8436,35 +8996,44 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { return this.get(id); }, - add : function(e) { - this.editors[e.id] = e; - this._setActive(e); + add : function(editor) { + var self = this, editors = self.editors; - return e; + // Add named and index editor instance + editors[editor.id] = editor; + editors.push(editor); + + self._setActive(editor); + self.onAddEditor.dispatch(self, editor); + + + return editor; }, - remove : function(e) { - var t = this; + remove : function(editor) { + var t = this, i, editors = t.editors; // Not in the collection - if (!t.editors[e.id]) + if (!editors[editor.id]) return null; - delete t.editors[e.id]; + delete editors[editor.id]; - // Select another editor since the active one was removed - if (t.activeEditor == e) { - t._setActive(null); - - each(t.editors, function(e) { - t._setActive(e); - return false; // Break - }); + for (i = 0; i < editors.length; i++) { + if (editors[i] == editor) { + editors.splice(i, 1); + break; + } } - e.destroy(); + // Select another editor since the active one was removed + if (t.activeEditor == editor) + t._setActive(editors[0]); - return e; + editor.destroy(); + t.onRemoveEditor.dispatch(t, editor); + + return editor; }, execCommand : function(c, u, v) { @@ -8577,75 +9146,109 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { // Private methods - _setActive : function(e) { - this.selectedInstance = this.activeEditor = e; + _setActive : function(editor) { + this.selectedInstance = this.activeEditor = editor; } - - }); - - tinymce.EditorManager.preInit(); + }); })(tinymce); -// Short for editor manager window.tinyMCE is needed when TinyMCE gets loaded though a XHR call -var tinyMCE = window.tinyMCE = tinymce.EditorManager; (function(tinymce) { - var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, Dispatcher = tinymce.util.Dispatcher; - var each = tinymce.each, isGecko = tinymce.isGecko, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit; - var is = tinymce.is, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, EditorManager = tinymce.EditorManager; - var inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode; + // Shorten these names + var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, + Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko, + isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is, + ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, + inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode; tinymce.create('tinymce.Editor', { Editor : function(id, s) { var t = this; t.id = t.editorId = id; + t.execCommands = {}; t.queryStateCommands = {}; t.queryValueCommands = {}; + + t.isNotDirty = false; + t.plugins = {}; // Add events to the editor each([ 'onPreInit', + 'onBeforeRenderUI', + 'onPostRender', + 'onInit', + 'onRemove', + 'onActivate', + 'onDeactivate', + 'onClick', + 'onEvent', + 'onMouseUp', + 'onMouseDown', + 'onDblClick', + 'onKeyDown', + 'onKeyUp', + 'onKeyPress', + 'onContextMenu', + 'onSubmit', + 'onReset', + 'onPaste', + 'onPreProcess', + 'onPostProcess', + 'onBeforeSetContent', + 'onBeforeGetContent', + 'onSetContent', + 'onGetContent', + 'onLoadContent', + 'onSaveContent', + 'onNodeChange', + 'onChange', + 'onBeforeExecCommand', + 'onExecCommand', + 'onUndo', + 'onRedo', + 'onVisualAid', + 'onSetProgressState' ], function(e) { t[e] = new Dispatcher(t); }); - // Default editor config t.settings = s = extend({ id : id, language : 'en', @@ -8671,16 +9274,14 @@ var tinyMCE = window.tinyMCE = tinymce.EditorManager; custom_undo_redo_keyboard_shortcuts : 1, custom_undo_redo_restore_selection : 1, custom_undo_redo : 1, - doctype : '', + doctype : tinymce.isIE6 ? '' : '', // Use old doctype on IE 6 to avoid horizontal scroll visual_table_class : 'mceItemTable', visual : 1, - inline_styles : true, - convert_fonts_to_spans : true, font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large', apply_source_formatting : 1, directionality : 'ltr', forced_root_block : 'p', - valid_elements : '@[id|class|style|title|dir