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 3c04ff60e..99a742dfc 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 7608442bd..e7d59700d 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 e4c734484..3991b8141 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 a8f297c60..096e7550c 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 2d8c3aa7d..c5a1719a7 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 ccac36f54..e46de5333 100755 Binary files a/chrome/content/zotero/tinymce/themes/advanced/img/icons.gif and b/chrome/content/zotero/tinymce/themes/advanced/img/icons.gif differ diff --git a/chrome/content/zotero/tinymce/themes/advanced/js/anchor.js b/chrome/content/zotero/tinymce/themes/advanced/js/anchor.js index b5efd1ec9..7fe781055 100755 --- a/chrome/content/zotero/tinymce/themes/advanced/js/anchor.js +++ b/chrome/content/zotero/tinymce/themes/advanced/js/anchor.js @@ -5,7 +5,7 @@ var AnchorDialog = { var action, elm, f = document.forms[0]; this.editor = ed; - elm = ed.dom.getParent(ed.selection.getNode(), 'A,IMG'); + elm = ed.dom.getParent(ed.selection.getNode(), 'A'); v = ed.dom.getAttrib(elm, 'name'); if (v) { @@ -17,18 +17,18 @@ var AnchorDialog = { }, update : function() { - var ed = this.editor; - + var ed = this.editor, elm, name = document.forms[0].anchorName.value; + tinyMCEPopup.restoreSelection(); if (this.action != 'update') ed.selection.collapse(1); - // Webkit acts weird if empty inline element is inserted so we need to use a image instead - if (tinymce.isWebKit) - ed.execCommand('mceInsertContent', 0, ed.dom.createHTML('img', {mce_name : 'a', name : document.forms[0].anchorName.value, 'class' : 'mceItemAnchor'})); + elm = ed.dom.getParent(ed.selection.getNode(), 'A'); + if (elm) + elm.name = name; else - ed.execCommand('mceInsertContent', 0, ed.dom.createHTML('a', {name : document.forms[0].anchorName.value, 'class' : 'mceItemAnchor'}, '')); + ed.execCommand('mceInsertContent', 0, ed.dom.createHTML('a', {name : name, 'class' : 'mceItemAnchor'}, '')); tinyMCEPopup.close(); } diff --git a/chrome/content/zotero/tinymce/themes/advanced/js/charmap.js b/chrome/content/zotero/tinymce/themes/advanced/js/charmap.js index 8467ef603..8c5aea172 100755 --- a/chrome/content/zotero/tinymce/themes/advanced/js/charmap.js +++ b/chrome/content/zotero/tinymce/themes/advanced/js/charmap.js @@ -1,3 +1,13 @@ +/** + * charmap.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + tinyMCEPopup.requireLangPack(); var charmap = [ diff --git a/chrome/content/zotero/tinymce/themes/advanced/js/image.js b/chrome/content/zotero/tinymce/themes/advanced/js/image.js index 4982ce0c8..6423d9080 100755 --- a/chrome/content/zotero/tinymce/themes/advanced/js/image.js +++ b/chrome/content/zotero/tinymce/themes/advanced/js/image.js @@ -151,8 +151,8 @@ var ImageDialog = { } // Merge - st = tinyMCEPopup.dom.parseStyle(dom.serializeStyle(st)); - this.styleVal = dom.serializeStyle(st); + st = tinyMCEPopup.dom.parseStyle(dom.serializeStyle(st), 'img'); + this.styleVal = dom.serializeStyle(st, 'img'); } }, diff --git a/chrome/content/zotero/tinymce/themes/advanced/js/link.js b/chrome/content/zotero/tinymce/themes/advanced/js/link.js index 2974878e1..f67a5bc82 100755 --- a/chrome/content/zotero/tinymce/themes/advanced/js/link.js +++ b/chrome/content/zotero/tinymce/themes/advanced/js/link.js @@ -53,6 +53,7 @@ var LinkDialog = { // Create new anchor elements if (e == null) { + ed.getDoc().execCommand("unlink", false, null); tinyMCEPopup.execCommand("CreateLink", false, "#mce_temp_url#", {skip_undo : 1}); tinymce.each(ed.dom.select("a"), function(n) { @@ -62,8 +63,8 @@ var LinkDialog = { ed.dom.setAttribs(e, { href : f.href.value, title : f.linktitle.value, - target : f.target_list ? f.target_list.options[f.target_list.selectedIndex].value : null, - 'class' : f.class_list ? f.class_list.options[f.class_list.selectedIndex].value : null + target : f.target_list ? getSelectValue(f, "target_list") : null, + 'class' : f.class_list ? getSelectValue(f, "class_list") : null }); } }); @@ -71,8 +72,8 @@ var LinkDialog = { ed.dom.setAttribs(e, { href : f.href.value, title : f.linktitle.value, - target : f.target_list ? f.target_list.options[f.target_list.selectedIndex].value : null, - 'class' : f.class_list ? f.class_list.options[f.class_list.selectedIndex].value : null + target : f.target_list ? getSelectValue(f, "target_list") : null, + 'class' : f.class_list ? getSelectValue(f, "class_list") : null }); } @@ -92,7 +93,7 @@ var LinkDialog = { if (n.value && Validator.isEmail(n) && !/^\s*mailto:/i.test(n.value) && confirm(tinyMCEPopup.getLang('advanced_dlg.link_is_email'))) n.value = 'mailto:' + n.value; - if (/^\s*www./i.test(n.value) && confirm(tinyMCEPopup.getLang('advanced_dlg.link_is_external'))) + if (/^\s*www\./i.test(n.value) && confirm(tinyMCEPopup.getLang('advanced_dlg.link_is_external'))) n.value = 'http://' + n.value; }, diff --git a/chrome/content/zotero/tinymce/themes/advanced/js/source_editor.js b/chrome/content/zotero/tinymce/themes/advanced/js/source_editor.js index af2231cad..aca38bd88 100755 --- a/chrome/content/zotero/tinymce/themes/advanced/js/source_editor.js +++ b/chrome/content/zotero/tinymce/themes/advanced/js/source_editor.js @@ -2,7 +2,7 @@ tinyMCEPopup.requireLangPack(); tinyMCEPopup.onInit.add(onLoadInit); function saveContent() { - tinyMCEPopup.editor.setContent(document.getElementById('htmlSource').value); + tinyMCEPopup.editor.setContent(document.getElementById('htmlSource').value, {source_view : true}); tinyMCEPopup.close(); } @@ -13,7 +13,7 @@ function onLoadInit() { if (tinymce.isGecko) document.body.spellcheck = tinyMCEPopup.editor.getParam("gecko_spellcheck"); - document.getElementById('htmlSource').value = tinyMCEPopup.editor.getContent(); + document.getElementById('htmlSource').value = tinyMCEPopup.editor.getContent({source_view : true}); if (tinyMCEPopup.editor.getParam("theme_advanced_source_editor_wrap", true)) { setWrap('soft'); @@ -44,19 +44,13 @@ function toggleWordWrap(elm) { setWrap('off'); } -var wHeight=0, wWidth=0, owHeight=0, owWidth=0; - function resizeInputs() { - var el = document.getElementById('htmlSource'); + var vp = tinyMCEPopup.dom.getViewPort(window), el; - if (!tinymce.isIE) { - wHeight = self.innerHeight - 65; - wWidth = self.innerWidth - 16; - } else { - wHeight = document.body.clientHeight - 70; - wWidth = document.body.clientWidth - 16; + el = document.getElementById('htmlSource'); + + if (el) { + el.style.width = (vp.w - 20) + 'px'; + el.style.height = (vp.h - 65) + 'px'; } - - el.style.height = Math.abs(wHeight) + 'px'; - el.style.width = Math.abs(wWidth) + 'px'; } diff --git a/chrome/content/zotero/tinymce/themes/advanced/link.htm b/chrome/content/zotero/tinymce/themes/advanced/link.htm index 286cc9247..7565b9ae8 100755 --- a/chrome/content/zotero/tinymce/themes/advanced/link.htm +++ b/chrome/content/zotero/tinymce/themes/advanced/link.htm @@ -7,7 +7,6 @@ -
@@ -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 19da1943b..9fba0431d 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 c944a60ba..f01222650 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 e2aec12e3..0049c7b3d 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 119a913c9..5957bbd17 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 c48b13ada..c1c868759 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