app/assets/source/tinymce/tinymce.jquery.js in tinymce-rails-4.0.18 vs app/assets/source/tinymce/tinymce.jquery.js in tinymce-rails-4.0.19

- old
+ new

@@ -1,6 +1,6 @@ -// 4.0.18 (2014-02-27) +// 4.0.19 (2014-03-11) /** * Compiled inline version. (Library mode) */ @@ -139,10 +139,11 @@ * @version 3.4 */ define("tinymce/html/Styles", [], function() { return function(settings, schema) { /*jshint maxlen:255 */ + /*eslint max-len:0 */ var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi, urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi, styleRegExp = /\s*([^:]+):\s*([^;]+);?/g, trimRightRegExp = /\s+$/, undef, i, encodingLookup = {}, encodingItems, invisibleChar = '\uFEFF'; @@ -445,10 +446,11 @@ * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ /*jshint loopfunc:true*/ +/*eslint no-loop-func:0 */ define("tinymce/dom/EventUtils", [], function() { "use strict"; var eventExpandoPrefix = "mce-data-"; @@ -1097,13 +1099,14 @@ * @method trim * @param {String} s String to remove whitespace from. * @return {String} New string with removed whitespace. */ var whiteSpaceRegExp = /^\s*|\s*$/g; - var trim = function(str) { + + function trim(str) { return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, ''); - }; + } /** * Returns true/false if the object is an array or not. * * @method isArray @@ -1209,11 +1212,11 @@ s = s || o; if (o.length !== undefined) { // Indexed arrays, needed for Safari - for (n=0, l = o.length; n < l; n++) { + for (n = 0, l = o.length; n < l; n++) { if (cb.call(s, o[n], n, o) === false) { return 0; } } } else { @@ -1318,18 +1321,18 @@ * // Some method * } * }); */ function create(s, p, root) { - var t = this, sp, ns, cn, scn, c, de = 0; + var self = this, sp, ns, cn, scn, c, de = 0; // Parse : <prefix> <class>:<super class> 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+$/, ''), root); + ns = self.createNS(s[3].replace(/\.\w+$/, ''), root); // Class already exists if (ns[cn]) { return; } @@ -1351,15 +1354,15 @@ de = 1; } // Add constructor and methods ns[cn] = p[cn]; - t.extend(ns[cn].prototype, p); + self.extend(ns[cn].prototype, p); // Extend if (s[5]) { - sp = t.resolve(s[5]).prototype; + sp = self.resolve(s[5]).prototype; scn = s[5].match(/\.(\w+)$/i)[1]; // Class name // Extend constructor c = ns[cn]; if (de) { @@ -1375,16 +1378,16 @@ }; } ns[cn].prototype[cn] = ns[cn]; // Add super methods - t.each(sp, function(f, n) { + self.each(sp, function(f, n) { ns[cn].prototype[n] = sp[n]; }); // Add overridden methods - t.each(p, function(f, n) { + self.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); @@ -1397,11 +1400,11 @@ }); } // Add static methods /*jshint sub:true*/ - t.each(p['static'], function(f, n) { + self.each(p['static'], function(f, n) { ns[cn][n] = f; }); } /** @@ -1497,11 +1500,11 @@ var i, v; o = o || window; n = n.split('.'); - for (i=0; i<n.length; i++) { + for (i = 0; i < n.length; i++) { v = n[i]; if (!o[v]) { o[v] = {}; } @@ -1592,11 +1595,11 @@ define("tinymce/dom/Range", [ "tinymce/util/Tools" ], function(Tools) { // Range constructor function Range(dom) { - var t = this, + var self = this, doc = dom.doc, EXTRACT = 0, CLONE = 1, DELETE = 2, TRUE = true, @@ -1636,18 +1639,18 @@ setEnd(n.parentNode, nodeIndex(n) + 1); } function collapse(ts) { if (ts) { - t[END_CONTAINER] = t[START_CONTAINER]; - t[END_OFFSET] = t[START_OFFSET]; + self[END_CONTAINER] = self[START_CONTAINER]; + self[END_OFFSET] = self[START_OFFSET]; } else { - t[START_CONTAINER] = t[END_CONTAINER]; - t[START_OFFSET] = t[END_OFFSET]; + self[START_CONTAINER] = self[END_CONTAINER]; + self[START_OFFSET] = self[END_OFFSET]; } - t.collapsed = TRUE; + self.collapsed = TRUE; } function selectNode(n) { setStartBefore(n); setEndAfter(n); @@ -1657,11 +1660,11 @@ setStart(n, 0); setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length); } function compareBoundaryPoints(h, r) { - var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET], + var sc = self[START_CONTAINER], so = self[START_OFFSET], ec = self[END_CONTAINER], eo = self[END_OFFSET], rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset; // Check START_TO_START if (h === 0) { return _compareBoundaryPoints(sc, so, rsc, rso); @@ -1729,25 +1732,25 @@ } } } function surroundContents(n) { - var f = t.extractContents(); + var f = self.extractContents(); - t.insertNode(n); + self.insertNode(n); n.appendChild(f); - t.selectNode(n); + self.selectNode(n); } 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 + startContainer: self[START_CONTAINER], + startOffset: self[START_OFFSET], + endContainer: self[END_CONTAINER], + endOffset: self[END_OFFSET], + collapsed: self.collapsed, + commonAncestorContainer: self.commonAncestorContainer }); } // Private methods @@ -1774,11 +1777,11 @@ return container; } function _isCollapsed() { - return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]); + return (self[START_CONTAINER] == self[END_CONTAINER] && self[START_OFFSET] == self[END_OFFSET]); } function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) { var c, offsetC, n, cmnRoot, childA, childB; @@ -1890,79 +1893,79 @@ function _setEndPoint(st, n, o) { var ec, sc; if (st) { - t[START_CONTAINER] = n; - t[START_OFFSET] = o; + self[START_CONTAINER] = n; + self[START_OFFSET] = o; } else { - t[END_CONTAINER] = n; - t[END_OFFSET] = o; + self[END_CONTAINER] = n; + self[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 // the new position. This enforces the restriction that both boundary- // points of a Range must have the same root container. - ec = t[END_CONTAINER]; + ec = self[END_CONTAINER]; while (ec.parentNode) { ec = ec.parentNode; } - sc = t[START_CONTAINER]; + sc = self[START_CONTAINER]; while (sc.parentNode) { sc = sc.parentNode; } 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 (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0) { - t.collapse(st); + if (_compareBoundaryPoints(self[START_CONTAINER], self[START_OFFSET], self[END_CONTAINER], self[END_OFFSET]) > 0) { + self.collapse(st); } } else { - t.collapse(st); + self.collapse(st); } - t.collapsed = _isCollapsed(); - t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]); + self.collapsed = _isCollapsed(); + self.commonAncestorContainer = dom.findCommonAncestor(self[START_CONTAINER], self[END_CONTAINER]); } function _traverse(how) { var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep; - if (t[START_CONTAINER] == t[END_CONTAINER]) { + if (self[START_CONTAINER] == self[END_CONTAINER]) { return _traverseSameContainer(how); } - for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) { - if (p == t[START_CONTAINER]) { + for (c = self[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) { + if (p == self[START_CONTAINER]) { return _traverseCommonStartContainer(c, how); } ++endContainerDepth; } - for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) { - if (p == t[END_CONTAINER]) { + for (c = self[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) { + if (p == self[END_CONTAINER]) { return _traverseCommonEndContainer(c, how); } ++startContainerDepth; } depthDiff = startContainerDepth - endContainerDepth; - startNode = t[START_CONTAINER]; + startNode = self[START_CONTAINER]; while (depthDiff > 0) { startNode = startNode.parentNode; depthDiff--; } - endNode = t[END_CONTAINER]; + endNode = self[END_CONTAINER]; while (depthDiff < 0) { endNode = endNode.parentNode; depthDiff++; } @@ -1981,34 +1984,34 @@ if (how != DELETE) { frag = createDocumentFragment(); } // If selection is empty, just return the fragment - if (t[START_OFFSET] == t[END_OFFSET]) { + if (self[START_OFFSET] == self[END_OFFSET]) { return frag; } // Text node needs special case handling - if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) { + if (self[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) { // get the substring - s = t[START_CONTAINER].nodeValue; - sub = s.substring(t[START_OFFSET], t[END_OFFSET]); + s = self[START_CONTAINER].nodeValue; + sub = s.substring(self[START_OFFSET], self[END_OFFSET]); // set the original text node to its new value if (how != CLONE) { - n = t[START_CONTAINER]; - start = t[START_OFFSET]; - len = t[END_OFFSET] - t[START_OFFSET]; + n = self[START_CONTAINER]; + start = self[START_OFFSET]; + len = self[END_OFFSET] - self[START_OFFSET]; if (start === 0 && len >= n.nodeValue.length - 1) { n.parentNode.removeChild(n); } else { n.deleteData(start, len); } // Nothing is partially selected, so collapse to start point - t.collapse(TRUE); + self.collapse(TRUE); } if (how == DELETE) { return; } @@ -2019,12 +2022,12 @@ return frag; } // Copy nodes between the start/end offsets. - n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]); - cnt = t[END_OFFSET] - t[START_OFFSET]; + n = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]); + cnt = self[END_OFFSET] - self[START_OFFSET]; while (n && cnt > 0) { sibling = n.nextSibling; xferNode = _traverseFullySelected(n, how); @@ -2036,11 +2039,11 @@ n = sibling; } // Nothing is partially selected, so collapse to start point if (how != CLONE) { - t.collapse(TRUE); + self.collapse(TRUE); } return frag; } @@ -2056,18 +2059,18 @@ if (frag) { frag.appendChild(n); } endIdx = nodeIndex(endAncestor); - cnt = endIdx - t[START_OFFSET]; + cnt = endIdx - self[START_OFFSET]; if (cnt <= 0) { // Collapse to just before the endAncestor, which // is partially selected. if (how != CLONE) { - t.setEndBefore(endAncestor); - t.collapse(FALSE); + self.setEndBefore(endAncestor); + self.collapse(FALSE); } return frag; } @@ -2085,12 +2088,12 @@ } // Collapse to just before the endAncestor, which // is partially selected. if (how != CLONE) { - t.setEndBefore(endAncestor); - t.collapse(FALSE); + self.setEndBefore(endAncestor); + self.collapse(FALSE); } return frag; } @@ -2107,11 +2110,11 @@ } startIdx = nodeIndex(startAncestor); ++startIdx; // Because we already traversed it - cnt = t[END_OFFSET] - startIdx; + cnt = self[END_OFFSET] - startIdx; n = startAncestor.nextSibling; while (n && cnt > 0) { sibling = n.nextSibling; xferNode = _traverseFullySelected(n, how); @@ -2122,30 +2125,29 @@ --cnt; n = sibling; } if (how != CLONE) { - t.setStartAfter(startAncestor); - t.collapse(TRUE); + self.setStartAfter(startAncestor); + self.collapse(TRUE); } return frag; } function _traverseCommonAncestors(startAncestor, endAncestor, how) { - var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling; + var n, frag, startOffset, endOffset, cnt, sibling, nextSibling; if (how != DELETE) { frag = createDocumentFragment(); } n = _traverseLeftBoundary(startAncestor, how); if (frag) { frag.appendChild(n); } - commonParent = startAncestor.parentNode; startOffset = nodeIndex(startAncestor); endOffset = nodeIndex(endAncestor); ++startOffset; cnt = endOffset - startOffset; @@ -2168,20 +2170,20 @@ if (frag) { frag.appendChild(n); } if (how != CLONE) { - t.setStartAfter(startAncestor); - t.collapse(TRUE); + self.setStartAfter(startAncestor); + self.collapse(TRUE); } return frag; } function _traverseRightBoundary(root, how) { - var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent; - var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER]; + var next = _getSelectedNode(self[END_CONTAINER], self[END_OFFSET] - 1), parent, clonedParent; + var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != self[END_CONTAINER]; if (next == root) { return _traverseNode(next, isFullySelected, FALSE, how); } @@ -2217,11 +2219,11 @@ clonedParent = clonedGrandParent; } } function _traverseLeftBoundary(root, how) { - var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER]; + var next = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]), isFullySelected = next != self[START_CONTAINER]; var parent, clonedParent, nextSibling, clonedChild, clonedGrandParent; if (next == root) { return _traverseNode(next, isFullySelected, TRUE, how); } @@ -2268,15 +2270,15 @@ if (n.nodeType == 3 /* TEXT_NODE */) { txtValue = n.nodeValue; if (isLeft) { - offset = t[START_OFFSET]; + offset = self[START_OFFSET]; newNodeValue = txtValue.substring(offset); oldNodeValue = txtValue.substring(0, offset); } else { - offset = t[END_OFFSET]; + offset = self[END_OFFSET]; newNodeValue = txtValue.substring(0, offset); oldNodeValue = txtValue.substring(offset); } if (how != CLONE) { @@ -2310,11 +2312,11 @@ function toStringIE() { return dom.create('body', null, cloneContents()).outerText; } - extend(t, { + extend(self, { // Inital states startContainer: doc, startOffset: 0, endContainer: doc, endOffset: 0, @@ -2345,11 +2347,11 @@ surroundContents: surroundContents, cloneRange: cloneRange, toStringIE: toStringIE }); - return t; + return self; } // Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype Range.prototype.toString = function() { return this.toStringIE(); @@ -2369,10 +2371,11 @@ * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ /*jshint bitwise:false */ +/*eslint no-bitwise:0 */ /** * Entity encoder class. * * @class tinymce.html.Entities @@ -2761,194 +2764,194 @@ }; }); // Included from: js/tinymce/classes/dom/StyleSheetLoader.js -/** - * StyleSheetLoader.js - * - * Copyright, Moxiecode Systems AB - * Released under LGPL License. - * - * License: http://www.tinymce.com/license - * Contributing: http://www.tinymce.com/contributing - */ - -/** - * This class handles loading of external stylesheets and fires events when these are loaded. - * - * @class tinymce.dom.StyleSheetLoader - * @private - */ -define("tinymce/dom/StyleSheetLoader", [], function() { - "use strict"; - - return function(document, settings) { - var idCount = 0, loadedStates = {}, maxLoadTime; - - settings = settings || {}; - maxLoadTime = settings.maxLoadTime || 5000; - - function appendToHead(node) { - document.getElementsByTagName('head')[0].appendChild(node); - } - - /** - * Loads the specified css style sheet file and call the loadedCallback once it's finished loading. - * - * @method load - * @param {String} url Url to be loaded. - * @param {Function} loadedCallback Callback to be executed when loaded. - * @param {Function} errorCallback Callback to be executed when failed loading. - */ - function load(url, loadedCallback, errorCallback) { - var link, style, startTime, state; - - function passed() { - var callbacks = state.passed, i = callbacks.length; - - while (i--) { - callbacks[i](); - } - - state.status = 2; - state.passed = []; - state.failed = []; - } - - function failed() { - var callbacks = state.failed, i = callbacks.length; - - while (i--) { - callbacks[i](); - } - - state.status = 3; - state.passed = []; - state.failed = []; - } - - // Sniffs for older WebKit versions that have the link.onload but a broken one - function isOldWebKit() { - var webKitChunks = navigator.userAgent.match(/WebKit\/(\d*)/); - return !!(webKitChunks && webKitChunks[1] < 536); - } - - // Calls the waitCallback until the test returns true or the timeout occurs - function wait(testCallback, waitCallback) { - if (!testCallback()) { - // Wait for timeout - if ((new Date().getTime()) - startTime < maxLoadTime) { - window.setTimeout(waitCallback, 0); - } else { - failed(); - } - } - } - - // Workaround for WebKit that doesn't properly support the onload event for link elements - // Or WebKit that fires the onload event before the StyleSheet is added to the document - function waitForWebKitLinkLoaded() { - wait(function() { - var styleSheets = document.styleSheets, styleSheet, i = styleSheets.length, owner; - - while (i--) { - styleSheet = styleSheets[i]; - owner = styleSheet.ownerNode ? styleSheet.ownerNode : styleSheet.owningElement; - if (owner && owner.id === link.id) { - passed(); - return true; - } - } - }, waitForWebKitLinkLoaded); - } - - // Workaround for older Geckos that doesn't have any onload event for StyleSheets - function waitForGeckoLinkLoaded() { - wait(function() { - try { - // Accessing the cssRules will throw an exception until the CSS file is loaded - var cssRules = style.sheet.cssRules; - passed(); - return !!cssRules; - } catch (ex) { - // Ignore - } - }, waitForGeckoLinkLoaded); - } - - if (!loadedStates[url]) { - state = { - passed: [], - failed: [] - }; - - loadedStates[url] = state; - } else { - state = loadedStates[url]; - } - - if (loadedCallback) { - state.passed.push(loadedCallback); - } - - if (errorCallback) { - state.failed.push(errorCallback); - } - - // Is loading wait for it to pass - if (state.status == 1) { - return; - } - - // Has finished loading and was success - if (state.status == 2) { - passed(); - return; - } - - // Has finished loading and was a failure - if (state.status == 3) { - failed(); - return; - } - - // Start loading - state.status = 1; - link = document.createElement('link'); - link.rel = 'stylesheet'; - link.type = 'text/css'; - link.id = 'u' + (idCount++); - link.async = false; - link.defer = false; - startTime = new Date().getTime(); - - // Feature detect onload on link element and sniff older webkits since it has an broken onload event - if ("onload" in link && !isOldWebKit()) { - link.onload = waitForWebKitLinkLoaded; - link.onerror = failed; - } else { - // Sniff for old Firefox that doesn't support the onload event on link elements - // TODO: Remove this in the future when everyone uses modern browsers - if (navigator.userAgent.indexOf("Firefox") > 0) { - style = document.createElement('style'); - style.textContent = '@import "' + url + '"'; - waitForGeckoLinkLoaded(); - appendToHead(style); - return; - } else { - // Use the id owner on older webkits - waitForWebKitLinkLoaded(); - } - } - - appendToHead(link); - link.href = url; - } - - this.load = load; - }; +/** + * StyleSheetLoader.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class handles loading of external stylesheets and fires events when these are loaded. + * + * @class tinymce.dom.StyleSheetLoader + * @private + */ +define("tinymce/dom/StyleSheetLoader", [], function() { + "use strict"; + + return function(document, settings) { + var idCount = 0, loadedStates = {}, maxLoadTime; + + settings = settings || {}; + maxLoadTime = settings.maxLoadTime || 5000; + + function appendToHead(node) { + document.getElementsByTagName('head')[0].appendChild(node); + } + + /** + * Loads the specified css style sheet file and call the loadedCallback once it's finished loading. + * + * @method load + * @param {String} url Url to be loaded. + * @param {Function} loadedCallback Callback to be executed when loaded. + * @param {Function} errorCallback Callback to be executed when failed loading. + */ + function load(url, loadedCallback, errorCallback) { + var link, style, startTime, state; + + function passed() { + var callbacks = state.passed, i = callbacks.length; + + while (i--) { + callbacks[i](); + } + + state.status = 2; + state.passed = []; + state.failed = []; + } + + function failed() { + var callbacks = state.failed, i = callbacks.length; + + while (i--) { + callbacks[i](); + } + + state.status = 3; + state.passed = []; + state.failed = []; + } + + // Sniffs for older WebKit versions that have the link.onload but a broken one + function isOldWebKit() { + var webKitChunks = navigator.userAgent.match(/WebKit\/(\d*)/); + return !!(webKitChunks && webKitChunks[1] < 536); + } + + // Calls the waitCallback until the test returns true or the timeout occurs + function wait(testCallback, waitCallback) { + if (!testCallback()) { + // Wait for timeout + if ((new Date().getTime()) - startTime < maxLoadTime) { + window.setTimeout(waitCallback, 0); + } else { + failed(); + } + } + } + + // Workaround for WebKit that doesn't properly support the onload event for link elements + // Or WebKit that fires the onload event before the StyleSheet is added to the document + function waitForWebKitLinkLoaded() { + wait(function() { + var styleSheets = document.styleSheets, styleSheet, i = styleSheets.length, owner; + + while (i--) { + styleSheet = styleSheets[i]; + owner = styleSheet.ownerNode ? styleSheet.ownerNode : styleSheet.owningElement; + if (owner && owner.id === link.id) { + passed(); + return true; + } + } + }, waitForWebKitLinkLoaded); + } + + // Workaround for older Geckos that doesn't have any onload event for StyleSheets + function waitForGeckoLinkLoaded() { + wait(function() { + try { + // Accessing the cssRules will throw an exception until the CSS file is loaded + var cssRules = style.sheet.cssRules; + passed(); + return !!cssRules; + } catch (ex) { + // Ignore + } + }, waitForGeckoLinkLoaded); + } + + if (!loadedStates[url]) { + state = { + passed: [], + failed: [] + }; + + loadedStates[url] = state; + } else { + state = loadedStates[url]; + } + + if (loadedCallback) { + state.passed.push(loadedCallback); + } + + if (errorCallback) { + state.failed.push(errorCallback); + } + + // Is loading wait for it to pass + if (state.status == 1) { + return; + } + + // Has finished loading and was success + if (state.status == 2) { + passed(); + return; + } + + // Has finished loading and was a failure + if (state.status == 3) { + failed(); + return; + } + + // Start loading + state.status = 1; + link = document.createElement('link'); + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.id = 'u' + (idCount++); + link.async = false; + link.defer = false; + startTime = new Date().getTime(); + + // Feature detect onload on link element and sniff older webkits since it has an broken onload event + if ("onload" in link && !isOldWebKit()) { + link.onload = waitForWebKitLinkLoaded; + link.onerror = failed; + } else { + // Sniff for old Firefox that doesn't support the onload event on link elements + // TODO: Remove this in the future when everyone uses modern browsers + if (navigator.userAgent.indexOf("Firefox") > 0) { + style = document.createElement('style'); + style.textContent = '@import "' + url + '"'; + waitForGeckoLinkLoaded(); + appendToHead(style); + return; + } else { + // Use the id owner on older webkits + waitForWebKitLinkLoaded(); + } + } + + appendToHead(link); + link.href = url; + } + + this.load = load; + }; }); // Included from: js/tinymce/classes/dom/DOMUtils.js /** @@ -3697,26 +3700,26 @@ * * // Sets class attribute on a specific element in the current page * tinymce.dom.setAttrib('mydiv', 'class', 'myclass'); */ setAttrib: function(e, n, v) { - var t = this; + var self = this; // What's the point if (!e || !n) { return; } return this.run(e, function(e) { - var s = t.settings; + var s = self.settings; var originalValue = e.getAttribute(n); if (v !== null) { switch (n) { case "style": if (!is(v, 'string')) { each(v, function(v, n) { - t.setStyle(e, n, v); + self.setStyle(e, n, v); }); return; } @@ -3738,14 +3741,14 @@ case "src": case "href": if (s.keep_values) { if (s.url_converter) { - v = s.url_converter.call(s.url_converter_scope || t, v, n, e); + v = s.url_converter.call(s.url_converter_scope || self, v, n, e); } - t.setAttrib(e, 'data-mce-' + n, v, 2); + self.setAttrib(e, 'data-mce-' + n, v, 2); } break; case "shape": @@ -4310,11 +4313,11 @@ // Create new div with HTML contents and a BR in front to keep comments var newElement = self.create('div'); newElement.innerHTML = '<br />' + html; // Add all children from div to target - each (grep(newElement.childNodes), function(node, i) { + each(grep(newElement.childNodes), function(node, i) { // Skip br element if (i && element.canHaveHTML) { element.appendChild(node); } }); @@ -4722,14 +4725,14 @@ * @param {Node} node Node to look for. * @param {boolean} normalized Optional true/false state if the index is what it would be after a normalization. * @return {Number} Index of the specified node. */ nodeIndex: function(node, normalized) { - var idx = 0, lastNodeType, lastNode, nodeType; + var idx = 0, lastNodeType, nodeType; if (node) { - for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) { + for (lastNodeType = node.nodeType, node = node.previousSibling; node; node = node.previousSibling) { nodeType = node.nodeType; // Normalize text nodes if (normalized && nodeType == 3) { if (nodeType == lastNodeType || !node.nodeValue.length) { @@ -5107,10 +5110,12 @@ callback(); } function error() { + /*eslint no-console:0 */ + // Report the error so it's easier for people to spot loading errors if (typeof(console) !== "undefined" && console.log) { console.log("Failed to load: " + url); } @@ -5419,63 +5424,63 @@ /** * Loads an add-on from a specific url. * * @method load - * @param {String} n Short name of the add-on that gets loaded. - * @param {String} u URL to the add-on that will get loaded. - * @param {function} cb Optional callback to execute ones the add-on is loaded. - * @param {Object} s Optional scope to execute the callback in. + * @param {String} name Short name of the add-on that gets loaded. + * @param {String} addOnUrl URL to the add-on that will get loaded. + * @param {function} callback Optional callback to execute ones the add-on is loaded. + * @param {Object} scope Optional scope to execute the callback in. * @example * // Loads a plugin from an external URL * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js'); * * // Initialize TinyMCE * tinymce.init({ * ... * plugins: '-myplugin' // Don't try to load it again * }); */ - load: function(n, u, cb, s) { - var t = this, url = u; + load: function(name, addOnUrl, callback, scope) { + var self = this, url = addOnUrl; function loadDependencies() { - var dependencies = t.dependencies(n); + var dependencies = self.dependencies(name); each(dependencies, function(dep) { - var newUrl = t.createUrl(u, dep); + var newUrl = self.createUrl(addOnUrl, dep); - t.load(newUrl.resource, newUrl, undefined, undefined); + self.load(newUrl.resource, newUrl, undefined, undefined); }); - if (cb) { - if (s) { - cb.call(s); + if (callback) { + if (scope) { + callback.call(scope); } else { - cb.call(ScriptLoader); + callback.call(ScriptLoader); } } } - if (t.urls[n]) { + if (self.urls[name]) { return; } - if (typeof u === "object") { - url = u.prefix + u.resource + u.suffix; + if (typeof addOnUrl === "object") { + url = addOnUrl.prefix + addOnUrl.resource + addOnUrl.suffix; } if (url.indexOf('/') !== 0 && url.indexOf('://') == -1) { url = AddOnManager.baseURL + '/' + url; } - t.urls[n] = url.substring(0, url.lastIndexOf('/')); + self.urls[name] = url.substring(0, url.lastIndexOf('/')); - if (t.lookup[n]) { + if (self.lookup[name]) { loadDependencies(); } else { - ScriptLoader.ScriptLoader.add(url, loadDependencies, s); + ScriptLoader.ScriptLoader.add(url, loadDependencies, scope); } } }; AddOnManager.PluginManager = new AddOnManager(); @@ -6085,11 +6090,11 @@ * @private * @param {String} type html4, html5 or html5-strict schema type. * @return {Object} Schema lookup table. */ function compileSchema(type) { - var schema = {}, globalAttributes, eventAttributes, blockContent; + var schema = {}, globalAttributes, blockContent; var phrasingContent, flowContent, html4BlockContent, html4PhrasingContent; function add(name, attributes, children) { var ni, i, attributesOrder, args = arguments; @@ -6153,17 +6158,17 @@ // Attributes present on all elements globalAttributes = split("id accesskey class dir lang style tabindex title"); // Event attributes can be opt-in/opt-out - eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " + + /*eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " + "ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " + "onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " + "onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " + "onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " + "onwaiting" - ); + );*/ // Block content elements blockContent = split( "address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul" ); @@ -6986,10 +6991,12 @@ * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ +/*eslint max-depth:[2, 9] */ + /** * This class parses HTML code using pure JavaScript and executes various events for each item it finds. It will * always execute the events in the right order for tag soup code like <b><p></b></p>. It will also remove elements * and attributes that doesn't fit the schema if the validate setting is enabled. * @@ -7042,12 +7049,14 @@ * @method SaxParser * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks. * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing. */ return function(settings, schema) { - var self = this, noop = function() {}; + var self = this; + function noop() {} + settings = settings || {}; self.schema = schema = schema || new Schema(); if (settings.fix_self_closing !== false) { settings.fix_self_closing = true; @@ -8650,10 +8659,11 @@ htmlParser.addNodeFilter('script,style', function(nodes, name) { var i = nodes.length, node, value; function trim(value) { /*jshint maxlen:255 */ + /*eslint max-len:0 */ return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n') .replace(/^[\r\n]*|[\r\n]*$/g, '') .replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '') .replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, ''); } @@ -9521,23 +9531,25 @@ 'z-index: 10000' + '}' ); function isResizable(elm) { - if (editor.settings.object_resizing === false) { + var selector = editor.settings.object_resizing; + + if (selector === false || Env.iOS) { return false; } - if (!/TABLE|IMG|DIV/.test(elm.nodeName)) { - return false; + if (typeof selector != 'string') { + selector = 'table,img,div'; } if (elm.getAttribute('data-mce-resize') === 'false') { return false; } - return true; + return editor.dom.is(elm, selector); } function resizeGhostElement(e) { var deltaX, deltaY; @@ -9903,10 +9915,18 @@ }); editor.dom.bind(editor.getBody(), 'mscontrolselect', function(e) { if (/^(TABLE|IMG|HR)$/.test(e.target.nodeName)) { e.preventDefault(); + + // This moves the selection from being a control selection to a text like selection like in WebKit #6753 + // TODO: Fix this the day IE works like other browsers without this nasty native ugly control selections. + if (e.target.tagName == 'IMG') { + window.setTimeout(function() { + editor.selection.select(e.target); + }, 0); + } } }); } } @@ -10768,11 +10788,11 @@ * * // Restore the selection bookmark * tinymce.activeEditor.selection.moveToBookmark(bm); */ getBookmark: function(type, normalized) { - var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, chr = '&#xFEFF;', styles; + var self = this, dom = self.dom, rng, rng2, id, collapsed, name, element, chr = '&#xFEFF;', styles; function findIndex(name, element) { var index = 0; each(dom.select(name), function(node, i) { @@ -10806,11 +10826,11 @@ return rng; } function getLocation() { - var rng = t.getRng(true), root = dom.getRoot(), bookmark = {}; + var rng = self.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; @@ -10828,52 +10848,52 @@ if (offset >= childNodes.length && childNodes.length) { after = 1; offset = Math.max(0, childNodes.length - 1); } - point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after); + point.push(self.dom.nodeIndex(childNodes[offset], normalized) + after); } for (; container && container != root; container = container.parentNode) { - point.push(t.dom.nodeIndex(container, normalized)); + point.push(self.dom.nodeIndex(container, normalized)); } return point; } bookmark.start = getPoint(rng, true); - if (!t.isCollapsed()) { + if (!self.isCollapsed()) { bookmark.end = getPoint(rng); } return bookmark; } if (type == 2) { - element = t.getNode(); + element = self.getNode(); name = element ? element.nodeName : null; if (name == 'IMG') { return {name: name, index: findIndex(name, element)}; } - if (t.tridentSel) { - return t.tridentSel.getBookmark(type); + if (self.tridentSel) { + return self.tridentSel.getBookmark(type); } return getLocation(); } // Handle simple range if (type) { - return {rng: t.getRng()}; + return {rng: self.getRng()}; } - rng = t.getRng(); + rng = self.getRng(); id = dom.uniqueId(); - collapsed = t.isCollapsed(); + collapsed = self.isCollapsed(); styles = 'overflow:hidden;line-height:0px'; // Explorer method if (rng.duplicate || rng.item) { // Text selection @@ -10908,11 +10928,11 @@ name = element.nodeName; return {name: name, index: findIndex(name, element)}; } } else { - element = t.getNode(); + element = self.getNode(); name = element.nodeName; if (name == 'IMG') { return {name: name, index: findIndex(name, element)}; } @@ -10928,11 +10948,11 @@ rng = normalizeTableCellSelection(rng); rng.collapse(true); rng.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_start', style: styles}, chr)); } - t.moveToBookmark({id: id, keep: 1}); + self.moveToBookmark({id: id, keep: 1}); return {id: id}; }, /** @@ -10949,11 +10969,11 @@ * * // Restore the selection bookmark * tinymce.activeEditor.selection.moveToBookmark(bm); */ moveToBookmark: function(bookmark) { - var t = this, dom = t.dom, rng, root, startContainer, endContainer, startOffset, endOffset; + var self = this, dom = self.dom, rng, root, startContainer, endContainer, startOffset, endOffset; function setEndPoint(start) { var point = bookmark[start ? 'start' : 'end'], i, node, offset, children; if (point) { @@ -11069,32 +11089,32 @@ if (bookmark) { if (bookmark.start) { rng = dom.createRng(); root = dom.getRoot(); - if (t.tridentSel) { - return t.tridentSel.moveToBookmark(bookmark); + if (self.tridentSel) { + return self.tridentSel.moveToBookmark(bookmark); } if (setEndPoint(true) && setEndPoint()) { - t.setRng(rng); + self.setRng(rng); } } else if (bookmark.id) { // 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); + self.setRng(rng); } } else if (bookmark.name) { - t.select(dom.select(bookmark.name)[bookmark.index]); + self.select(dom.select(bookmark.name)[bookmark.index]); } else if (bookmark.rng) { - t.setRng(bookmark.rng); + self.setRng(bookmark.rng); } } }, /** @@ -13061,11 +13081,12 @@ if (start ? startOffset > 0 : endOffset < container.nodeValue.length) { return container; } } - for (;;) { + /*eslint no-constant-condition:0 */ + while (true) { // Stop expanding on block elements if (!format[0].block_expand && isBlock(parent)) { return parent; } @@ -14140,11 +14161,11 @@ '<div[^>]+data-mce-bogus[^>]+><\\/div>', // Trim bogus divs like resize handles '\\s?data-mce-selected="[^"]+"' // Trim temporaty data-mce prefixed attributes like data-mce-selected ].join('|'), 'gi'); return function(editor) { - var self, index = 0, data = [], beforeBookmark, isFirstTypedCharacter, lock; + var self = this, index = 0, data = [], beforeBookmark, isFirstTypedCharacter, lock; // Returns a trimmed version of the current editor contents function getContent() { return trim(editor.getContent({format: 'raw', no_events: 1}).replace(trimContentRegExp, '')); } @@ -16048,10 +16069,11 @@ // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri) url = url.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something /*jshint maxlen: 255 */ + /*eslint max-len: 0 */ url = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(url); each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) { var part = url[i]; @@ -16381,20 +16403,18 @@ function Class() { } // Provides classical inheritance, based on code made by John Resig Class.extend = extendClass = function(prop) { - var Self = this, _super = Self.prototype, prototype, name, member; + var self = this, _super = self.prototype, prototype, name, member; // The dummy class constructor function Class() { - var i, mixins, mixin, self; + var i, mixins, mixin, self = this; // All construction is actually done in the init method if (!initializing) { - self = this; - // Run class constuctor if (self.init) { self.init.apply(self, arguments); } @@ -16432,11 +16452,11 @@ } // Instantiate a base class (but only create the instance, // don't run the init constructor) initializing = true; - prototype = new Self(); + prototype = new self(); initializing = false; // Add mixins if (prop.Mixins) { each(prop.Mixins, function(mixin) { @@ -16530,10 +16550,12 @@ * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ +/*eslint no-nested-ternary:0 */ + /** * Selector engine, enables you to select controls by using CSS like expressions. * We currently only support basic CSS expressions to reduce the size of the core * and the ones we support should be enough for most cases. * @@ -16594,10 +16616,11 @@ } var expression = /^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i; /*jshint maxlen:255 */ + /*eslint max-len:0 */ var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, whiteSpace = /^\s*|\s*$/g, Collection; var Selector = Class.extend({ @@ -17430,10 +17453,12 @@ * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ +/*eslint consistent-this:0 */ + /** * This is the base class for all controls and containers. All UI control instances inherit * from this one as it has the base logic needed by all of them. * * @class tinymce.ui.Control @@ -18141,14 +18166,14 @@ * @method parents * @param {String} selector Optional selector expression to find parents. * @return {tinymce.ui.Collection} Collection with all parent controls. */ parents: function(selector) { - var ctrl = this, parents = new Collection(); + var self = this, ctrl, parents = new Collection(); // Add each parent to collection - for (ctrl = ctrl.parent(); ctrl; ctrl = ctrl.parent()) { + for (ctrl = self.parent(); ctrl; ctrl = ctrl.parent()) { parents.add(ctrl); } // Filter away everything that doesn't match the selector if (selector) { @@ -18789,14 +18814,14 @@ function fixWheelEvent(e) { e.preventDefault(); if (e.type == "mousewheel") { - e.deltaY = - 1/40 * e.wheelDelta; + e.deltaY = -1 / 40 * e.wheelDelta; if (e.wheelDeltaX) { - e.deltaX = -1/40 * e.wheelDeltaX; + e.deltaX = -1 / 40 * e.wheelDeltaX; } } else { e.deltaX = 0; e.deltaY = e.detail; } @@ -19860,11 +19885,11 @@ self.preRender(); layout.preRender(self); return ( '<div id="' + self._id + '" class="' + self.classes() + '"' + (role ? ' role="' + this.settings.role + '"' : '') + '>' + - '<div id="' + self._id + '-body" class="' + self.classes('body') + '">'+ + '<div id="' + self._id + '-body" class="' + self.classes('body') + '">' + (self.settings.html || '') + layout.renderHtml(self) + '</div>' + '</div>' ); }, @@ -19951,17 +19976,17 @@ * * @method reflow * @return {tinymce.ui.Container} Current container instance. */ reflow: function() { - var i, items; + var i; if (this.visible()) { Control.repaintControls = []; Control.repaintControls.map = {}; - items = this.recalc(); + this.recalc(); i = Control.repaintControls.length; while (i--) { Control.repaintControls[i].repaint(); } @@ -21882,11 +21907,11 @@ * * This code is a ugly hack since writing full custom delete logic for just this bug * fix seemed like a huge task. I hope we can remove this before the year 2030. */ function cleanupStylesWhenDeleting() { - var doc = editor.getDoc(); + var doc = editor.getDoc(), urlPrefix = 'data:text/mce-internal,'; if (!window.MutationObserver) { return; } @@ -21997,18 +22022,24 @@ editor.addCommand('ForwardDelete', function() { customDelete(true); }); editor.on('dragstart', function(e) { - e.dataTransfer.setData('mce-internal', editor.selection.getContent()); + // Safari doesn't support custom dataTransfer items so we can only use URL and Text + e.dataTransfer.setData('URL', 'data:text/mce-internal,' + escape(editor.selection.getContent())); }); editor.on('drop', function(e) { if (!isDefaultPrevented(e)) { - var internalContent = e.dataTransfer.getData('mce-internal'); + var internalContent = e.dataTransfer.getData('URL'); - if (internalContent && doc.caretRangeFromPoint) { + if (!internalContent || internalContent.indexOf(urlPrefix) == -1 || !doc.caretRangeFromPoint) { + return; + } + + internalContent = unescape(internalContent.substr(urlPrefix.length)); + if (doc.caretRangeFromPoint) { e.preventDefault(); customDelete(); editor.selection.setRng(doc.caretRangeFromPoint(e.x, e.y)); editor.insertContent(internalContent); } @@ -22790,20 +22821,24 @@ setEditorCommandState("AutoUrlDetect", false); } /** * IE 11 has a fantastic bug where it will produce two trailing BR elements to iframe bodies when - * the iframe is hidden by display: none. This workaround solves this by switching - * on designMode on the whole document. + * the iframe is hidden by display: none on a parent container. The DOM is actually out of sync + * with innerHTML in this case. It's like IE adds shadow DOM BR elements that appears on innerHTML + * but not as the lastChild of the body. However is we add a BR element to the body then remove it + * it doesn't seem to add these BR elements makes sence right?! * - * Example this: <body>text</body> becomes <body>text<br><br></body> + * Example of what happens: <body>text</body> becomes <body>text<br><br></body> */ function doubleTrailingBrElements() { if (!editor.inline) { - editor.on('init', function() { - editor.getDoc().designMode = 'on'; - }); + editor.on('focus blur', function() { + var br = editor.dom.create('br'); + editor.getBody().appendChild(br); + br.parentNode.removeChild(br); + }, true); } } // All browsers disableBackspaceIntoATable(); @@ -22911,10 +22946,14 @@ * instance.fire('event', {...}); */ fire: function(name, args, bubble) { var self = this, handlers, i, l, callback, parent; + if (self.removed) { + return; + } + name = name.toLowerCase(); args = args || {}; args.type = name; // Setup target is there isn't one @@ -22984,17 +23023,18 @@ * Binds an event listener to a specific event by name. * * @method on * @param {String} name Event name or space separated list of events to bind. * @param {callback} callback Callback to be executed when the event occurs. + * @param {Boolean} first Optional flag if the event should be prepended. Use this with care. * @return {Object} Current class instance. * @example * instance.on('event', function(e) { * // Callback logic * }); */ - on: function(name, callback) { + on: function(name, callback, prepend) { var self = this, bindings, handlers, names, i; if (callback === false) { callback = function() { return false; @@ -23018,11 +23058,15 @@ if (self.bindNative && nativeEvents[name]) { self.bindNative(name); } } - handlers.push(callback); + if (prepend) { + handlers.unshift(callback); + } else { + handlers.push(callback); + } } } return self; }, @@ -23691,11 +23735,11 @@ * * @method init */ init: function() { var self = this, settings = self.settings, elm = self.getElement(); - var w, h, minHeight, n, o, url, bodyId, bodyClass, re, i, initializedPlugins = []; + var w, h, minHeight, n, o, Theme, url, bodyId, bodyClass, re, i, initializedPlugins = []; self.rtl = this.editorManager.i18n.rtl; self.editorManager.add(self); settings.aria_label = settings.aria_label || DOM.getAttrib(elm, 'aria-label', self.getLang('aria.rich_text_area')); @@ -23710,37 +23754,37 @@ * tinymce.activeEditor.theme.someMethod(); */ if (settings.theme) { if (typeof settings.theme != "function") { settings.theme = settings.theme.replace(/-/, ''); - o = ThemeManager.get(settings.theme); - self.theme = new o(self, ThemeManager.urls[settings.theme]); + Theme = ThemeManager.get(settings.theme); + self.theme = new Theme(self, ThemeManager.urls[settings.theme]); if (self.theme.init) { self.theme.init(self, ThemeManager.urls[settings.theme] || self.documentBaseUrl.replace(/\/$/, '')); } } else { self.theme = settings.theme; } } function initPlugin(plugin) { - var constr = PluginManager.get(plugin), url, pluginInstance; + var Plugin = PluginManager.get(plugin), pluginUrl, pluginInstance; - url = PluginManager.urls[plugin] || self.documentBaseUrl.replace(/\/$/, ''); + pluginUrl = PluginManager.urls[plugin] || self.documentBaseUrl.replace(/\/$/, ''); plugin = trim(plugin); - if (constr && inArray(initializedPlugins, plugin) === -1) { + if (Plugin && inArray(initializedPlugins, plugin) === -1) { each(PluginManager.dependencies(plugin), function(dep){ initPlugin(dep); }); - pluginInstance = new constr(self, url); + pluginInstance = new Plugin(self, pluginUrl); self.plugins[plugin] = pluginInstance; if (pluginInstance.init) { - pluginInstance.init(self, url); + pluginInstance.init(self, pluginUrl); initializedPlugins.push(plugin); } } } @@ -23756,15 +23800,15 @@ h = settings.height || elm.style.height || elm.offsetHeight; minHeight = settings.min_height || 100; re = /^[0-9\.]+(|px)$/i; if (re.test('' + w)) { - w = Math.max(parseInt(w, 10) + (o.deltaWidth || 0), 100); + w = Math.max(parseInt(w, 10), 100); } if (re.test('' + h)) { - h = Math.max(parseInt(h, 10) + (o.deltaHeight || 0), minHeight); + h = Math.max(parseInt(h, 10), minHeight); } // Render UI o = self.theme.renderUI({ targetNode: elm, @@ -23860,11 +23904,12 @@ } self.iframeHTML += '</head><body id="' + bodyId + '" class="mce-content-body ' + bodyClass + '" ' + 'onload="window.parent.tinymce.get(\'' + self.id + '\').fire(\'load\');"><br></body></html>'; - var domainRelaxUrl = 'javascript:(function(){'+ + /*eslint no-script-url:0 */ + var domainRelaxUrl = 'javascript:(function(){' + 'document.open();document.domain="' + document.domain + '";' + 'var ed = window.parent.tinymce.get("' + self.id + '");document.write(ed.iframeHTML);' + 'document.close();ed.initContentBody(true);})()'; // Domain relaxing is required since the user has messed around with document.domain @@ -23938,16 +23983,16 @@ doc.close(); } if (settings.content_editable) { self.on('remove', function() { - var body = this.getBody(); + var bodyEl = this.getBody(); - DOM.removeClass(body, 'mce-content-body'); - DOM.removeClass(body, 'mce-edit-focus'); - DOM.setAttrib(body, 'tabIndex', null); - DOM.setAttrib(body, 'contentEditable', null); + DOM.removeClass(bodyEl, 'mce-content-body'); + DOM.removeClass(bodyEl, 'mce-edit-focus'); + DOM.setAttrib(bodyEl, 'tabIndex', null); + DOM.setAttrib(bodyEl, 'contentEditable', null); }); DOM.addClass(targetElm, 'mce-content-body'); targetElm.tabIndex = -1; self.contentDocument = doc = settings.content_document || document; @@ -24403,11 +24448,11 @@ */ nodeChanged: function() { var self = this, selection = self.selection, node, parents, root; // Fix for bug #1896577 it seems that this can not be fired while the editor is loading - if (self.initialized && !self.settings.disable_nodechange) { + if (self.initialized && !self.settings.disable_nodechange && !self.settings.readonly) { // Get start node root = self.getBody(); node = selection.getStart() || root; node = ie && node.ownerDocument != self.getDoc() ? self.getBody() : node; // Fix for IE initial state @@ -24935,11 +24980,11 @@ // Check if forcedRootBlock is configured and that the block is a valid child of the body if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) { // Padd with bogus BR elements on modern browsers and IE 7 and 8 since they don't render empty P tags properly content = ie && ie < 11 ? '' : '<br data-mce-bogus="1">'; content = self.dom.createHTML(forcedRootBlockName, self.settings.forced_root_block_attrs, content); - } else if (!ie || ie < 11) { + } else if (!ie) { // We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret content = '<br data-mce-bogus="1">'; } body.innerHTML = content; @@ -25234,23 +25279,19 @@ */ remove: function() { var self = this; if (!self.removed) { + self.fire('remove'); + self.off(); self.removed = 1; // Cancels post remove event execution // Remove any hidden input if (self.hasHiddenInput) { DOM.remove(self.getElement().nextSibling); } - // Fixed bug where IE has a blinking cursor left from the editor - var doc = self.getDoc(); - if (ie && doc && !self.inline) { - doc.execCommand('SelectAll'); - } - // We must save before we hide so Safari doesn't crash self.save(); DOM.setStyle(self.id, 'display', self.orgDisplay); @@ -25263,12 +25304,10 @@ var elm = self.getContainer(); Event.unbind(self.getBody()); Event.unbind(elm); - self.fire('remove'); - self.editorManager.remove(self); DOM.remove(elm); self.destroy(); } }, @@ -25353,11 +25392,11 @@ } DOM.unbind(form, 'submit reset', self.formEventDelegate); } - self.contentAreaContainer = self.formElement = self.container = null; + self.contentAreaContainer = self.formElement = self.container = self.editorContainer = null; self.settings.content_element = self.bodyElement = self.contentDocument = self.contentWindow = null; if (self.selection) { self.selection = self.selection.win = self.selection.dom = self.selection.dom.doc = null; } @@ -25506,10 +25545,12 @@ */ define("tinymce/FocusManager", [ "tinymce/dom/DOMUtils", "tinymce/Env" ], function(DOMUtils, Env) { + var selectionChangeHandler, documentFocusInHandler, DOM = DOMUtils.DOM; + /** * Constructs a new focus manager instance. * * @constructor FocusManager * @param {tinymce.EditorManager} editorManager Editor manager instance to handle focus for. @@ -25553,11 +25594,11 @@ return rng; } function isUIElement(elm) { - return !!DOMUtils.DOM.getParent(elm, FocusManager.isEditorUIElement); + return !!DOM.getParent(elm, FocusManager.isEditorUIElement); } function isNodeInBodyOfEditor(node, editor) { var body = editor.getBody(); @@ -25569,11 +25610,11 @@ node = node.parentNode; } } function registerEvents(e) { - var editor = e.editor, selectionChangeHandler; + var editor = e.editor; editor.on('init', function() { // On IE take selection snapshot onbeforedeactivate if ("onbeforedeactivate" in document && Env.ie < 11) { // Gets fired when the editor is about to be blurred but also when the selection @@ -25609,29 +25650,26 @@ editor.lastRng = editor.selection.getRng(); } }); // Handles the issue with WebKit not retaining selection within inline document - // If the user releases the mouse out side the body while selecting a nodeChange won't - // fire and there for the selection snapshot won't be stored - // TODO: Optimize this since we only need to bind these on the active editor - if (Env.webkit) { + // If the user releases the mouse out side the body since a mouse up event wont occur on the body + if (Env.webkit && !selectionChangeHandler) { selectionChangeHandler = function() { - var rng = editor.selection.getRng(); + var activeEditor = editorManager.activeEditor; - // Store when it's non collapsed - if (!rng.collapsed) { - editor.lastRng = rng; + if (activeEditor && activeEditor.selection) { + var rng = activeEditor.selection.getRng(); + + // Store when it's non collapsed + if (rng && !rng.collapsed) { + editor.lastRng = rng; + } } }; - // Bind selection handler - DOMUtils.DOM.bind(document, 'selectionchange', selectionChangeHandler); - - editor.on('remove', function() { - DOMUtils.DOM.unbind(document, 'selectionchange', selectionChangeHandler); - }); + DOM.bind(document, 'selectionchange', selectionChangeHandler); } } }); editor.on('setcontent', function() { @@ -25679,32 +25717,45 @@ editor.selection.lastFocusBookmark = null; } } }, 0); }); - } - // Check if focus is moved to an element outside the active editor by checking if the target node - // isn't within the body of the activeEditor nor a UI element such as a dialog child control - DOMUtils.DOM.bind(document, 'focusin', function(e) { - var activeEditor = editorManager.activeEditor; + if (!documentFocusInHandler) { + documentFocusInHandler = function(e) { + var activeEditor = editorManager.activeEditor; - if (activeEditor && e.target.ownerDocument == document) { - // Check to make sure we have a valid selection - if (activeEditor.selection) { - activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.lastRng); - } + if (activeEditor && e.target.ownerDocument == document) { + // Check to make sure we have a valid selection + if (activeEditor.selection) { + activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.lastRng); + } - // Fire a blur event if the element isn't a UI element - if (!isUIElement(e.target) && editorManager.focusedEditor == activeEditor) { - activeEditor.fire('blur', {focusedEditor: null}); - editorManager.focusedEditor = null; - } + // Fire a blur event if the element isn't a UI element + if (!isUIElement(e.target) && editorManager.focusedEditor == activeEditor) { + activeEditor.fire('blur', {focusedEditor: null}); + editorManager.focusedEditor = null; + } + } + }; + + // Check if focus is moved to an element outside the active editor by checking if the target node + // isn't within the body of the activeEditor nor a UI element such as a dialog child control + DOM.bind(document, 'focusin', documentFocusInHandler); } - }); + } + function unregisterDocumentEvents() { + if (!editorManager.activeEditor) { + DOM.unbind(document, 'selectionchange', selectionChangeHandler); + DOM.unbind(document, 'focusin', documentFocusInHandler); + selectionChangeHandler = documentFocusInHandler = null; + } + } + editorManager.on('AddEditor', registerEvents); + editorManager.on('RemoveEditor', unregisterDocumentEvents); } /** * Returns true if the specified element is part of the UI for example an button or text input. * @@ -25768,19 +25819,19 @@ * Minor version of TinyMCE build. * * @property minorVersion * @type String */ - minorVersion : '0.18', + minorVersion : '0.19', /** * Release date of TinyMCE build. * * @property releaseDate * @type String */ - releaseDate: '2014-02-27', + releaseDate: '2014-03-11', /** * Collection of editor instances. * * @property editors @@ -26516,11 +26567,11 @@ }) + quote; } if (t == 'object') { if (o.hasOwnProperty && Object.prototype.toString.call(o) === '[object Array]') { - for (i=0, v = '['; i<o.length; i++) { + for (i = 0, v = '['; i < o.length; i++) { v += (i > 0 ? ',' : '') + serialize(o[i], quote); } return v + ']'; } @@ -26528,11 +26579,11 @@ v = '{'; for (name in o) { if (o.hasOwnProperty(name)) { v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name + - quote +':' + serialize(o[name], quote) : ''; + quote + ':' + serialize(o[name], quote) : ''; } } return v + '}'; } @@ -27656,12 +27707,12 @@ self.addClass('btn-group'); self.preRender(); layout.preRender(self); return ( - '<div id="' + self._id + '" class="' + self.classes() + '">'+ - '<div id="' + self._id + '-body">'+ + '<div id="' + self._id + '" class="' + self.classes() + '">' + + '<div id="' + self._id + '-body">' + (self.settings.html || '') + layout.renderHtml(self) + '</div>' + '</div>' ); } @@ -27781,11 +27832,11 @@ var self = this, id = self._id, prefix = self.classPrefix; return ( '<div id="' + id + '" class="' + self.classes() + '" unselectable="on" aria-labelledby="' + id + '-al" tabindex="-1">' + '<i class="' + prefix + 'ico ' + prefix + 'i-checkbox"></i>' + - '<span id="' + id +'-al" class="' + prefix + 'label">' + self.encode(self._text) + '</span>' + + '<span id="' + id + '-al" class="' + prefix + 'label">' + self.encode(self._text) + '</span>' + '</div>' ); } }); }); @@ -28469,13 +28520,13 @@ _getPathHtml: function() { var self = this, parts = self._data || [], i, l, html = '', prefix = self.classPrefix; for (i = 0, l = parts.length; i < l; i++) { html += ( - (i > 0 ? '<div class="'+ prefix + 'divider" aria-hidden="true"> ' + self.settings.delimiter + ' </div>' : '') + + (i > 0 ? '<div class="' + prefix + 'divider" aria-hidden="true"> ' + self.settings.delimiter + ' </div>' : '') + '<div role="button" class="' + prefix + 'path-item' + (i == l - 1 ? ' ' + prefix + 'last' : '') + '" data-index="' + - i + '" tabindex="-1" id="' + self._id + '-' + i +'" aria-level="' + i + '">' + parts[i].name + '</div>' + i + '" tabindex="-1" id="' + self._id + '-' + i + '" aria-level="' + i + '">' + parts[i].name + '</div>' ); } if (!html) { html = '<div class="' + prefix + 'path-item">&nbsp;</div>'; @@ -28994,12 +29045,12 @@ */ recalc: function(container) { // A ton of variables, needs to be in the same scope for performance var i, l, items, contLayoutRect, contPaddingBox, contSettings, align, pack, spacing, totalFlex, availableSpace, direction; var ctrl, ctrlLayoutRect, ctrlSettings, flex, maxSizeItems = [], size, maxSize, ratio, rect, pos, maxAlignEndPos; - var sizeName, minSizeName, posName, maxSizeName, beforeName, innerSizeName, afterName, deltaSizeName, contentSizeName; - var alignAxisName, alignInnerSizeName, alignSizeName, alignMinSizeName, alignMaxSizeName, alignBeforeName, alignAfterName; + var sizeName, minSizeName, posName, maxSizeName, beforeName, innerSizeName, deltaSizeName, contentSizeName; + var alignAxisName, alignInnerSizeName, alignSizeName, alignMinSizeName, alignBeforeName, alignAfterName; var alignDeltaSizeName, alignContentSizeName; var max = Math.max, min = Math.min; // Get container items, properties and settings items = container.items().filter(':visible'); @@ -29022,40 +29073,36 @@ sizeName = "h"; minSizeName = "minH"; maxSizeName = "maxH"; innerSizeName = "innerH"; beforeName = 'top'; - afterName = 'bottom'; deltaSizeName = "deltaH"; contentSizeName = "contentH"; alignBeforeName = "left"; alignSizeName = "w"; alignAxisName = "x"; alignInnerSizeName = "innerW"; alignMinSizeName = "minW"; - alignMaxSizeName = "maxW"; alignAfterName = "right"; alignDeltaSizeName = "deltaW"; alignContentSizeName = "contentW"; } else { posName = "x"; sizeName = "w"; minSizeName = "minW"; maxSizeName = "maxW"; innerSizeName = "innerW"; beforeName = 'left'; - afterName = 'right'; deltaSizeName = "deltaW"; contentSizeName = "contentW"; alignBeforeName = "top"; alignSizeName = "h"; alignAxisName = "y"; alignInnerSizeName = "innerH"; alignMinSizeName = "minH"; - alignMaxSizeName = "maxH"; alignAfterName = "bottom"; alignDeltaSizeName = "deltaH"; alignContentSizeName = "contentH"; } @@ -29949,11 +29996,11 @@ * @param {tinymce.ui.Container} container Container instance to recalc. */ recalc: function(container) { var settings = container.settings, rows, cols, items, contLayoutRect, width, height, rect, ctrlLayoutRect, ctrl, x, y, posX, posY, ctrlSettings, contPaddingBox, align, spacingH, spacingV, alignH, alignV, maxX, maxY, - colWidths = [], rowHeights = [], ctrlMinWidth, ctrlMinHeight, alignX, alignY, availableWidth, availableHeight; + colWidths = [], rowHeights = [], ctrlMinWidth, ctrlMinHeight, availableWidth, availableHeight; // Get layout settings settings = container.settings; items = container.items().filter(':visible'); contLayoutRect = container.layoutRect(); @@ -30099,11 +30146,10 @@ // Get control settings and calculate x, y ctrlSettings = ctrl.settings; ctrlLayoutRect = ctrl.layoutRect(); width = Math.max(colWidths[x], ctrlLayoutRect.startMinWidth); - alignX = alignY = 0; ctrlLayoutRect.x = posX; ctrlLayoutRect.y = posY; // Align control horizontal align = ctrlSettings.alignH || (alignH ? (alignH[x] || alignH[0]) : null); @@ -30344,11 +30390,11 @@ */ renderHtml: function() { var self = this, forId = self.settings.forId; return ( - '<label id="' + self._id + '" class="' + self.classes() + '"' + (forId ? ' for="' + forId +'"' : '') + '>' + + '<label id="' + self._id + '" class="' + self.classes() + '"' + (forId ? ' for="' + forId + '"' : '') + '>' + self.encode(self._text) + '</label>' ); } }); @@ -31662,10 +31708,10 @@ ctrl.aria('role', 'tabpanel'); ctrl.aria('labelledby', id); tabsHtml += ( - '<div id="' + id + '" class="' + prefix + 'tab" '+ + '<div id="' + id + '" class="' + prefix + 'tab" ' + 'unselectable="on" role="tab" aria-controls="' + ctrl._id + '" aria-selected="false" tabIndex="-1">' + self.encode(ctrl.settings.title) + '</div>' ); }); \ No newline at end of file