app/assets/source/tinymce/tinymce.jquery.js in tinymce-rails-4.1.3 vs app/assets/source/tinymce/tinymce.jquery.js in tinymce-rails-4.1.4

- old
+ new

@@ -1,6 +1,6 @@ -// 4.1.3 (2014-07-29) +// 4.1.4 (2014-08-21) /** * Compiled inline version. (Library mode) */ @@ -190,15 +190,15 @@ if (originalEvent && mouseEventRe.test(originalEvent.type) && originalEvent.pageX === undef && originalEvent.clientX !== undef) { var eventDoc = event.target.ownerDocument || document; var doc = eventDoc.documentElement; var body = eventDoc.body; - event.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - - ( doc && doc.clientLeft || body && body.clientLeft || 0); + event.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - + (doc && doc.clientLeft || body && body.clientLeft || 0); - event.pageY = originalEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0 ) - - ( doc && doc.clientTop || body && body.clientTop || 0); + event.pageY = originalEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - + (doc && doc.clientTop || body && body.clientTop || 0); } // Add preventDefault method event.preventDefault = function() { event.isDefaultPrevented = returnTrue; @@ -1919,11 +1919,11 @@ try { while (i--) { self[i].innerHTML = value; } } catch (ex) { - // Workaround for "Unkown runtime error" when DIV is added to P on IE + // Workaround for "Unknown runtime error" when DIV is added to P on IE DomQuery(self[i]).empty().append(value); } return self; } @@ -2510,14 +2510,22 @@ expr: Sizzle.selectors, unique: Sizzle.uniqueSort, text: Sizzle.getText, contains: Sizzle.contains, filter: function(expr, elems, not) { + var i = elems.length; + if (not) { expr = ":not(" + expr + ")"; } + while (i--) { + if (elems[i].nodeType != 1) { + elems.splice(i, 1); + } + } + if (elems.length === 1) { elems = DomQuery.find.matchesSelector(elems[0], expr) ? [elems[0]] : []; } else { elems = DomQuery.find.matches(expr, elems); } @@ -7506,12 +7514,12 @@ }); editor.on('SelectionChange', function() { var startElm = editor.selection.getStart(true); - // Selection change might fire when focus is lost so check if the start is still within the body - if (!isSameElementPath(startElm) && editor.dom.isChildOf(startElm, editor.getBody())) { + // Fire a nodechange only when the selection isn't collapsed since focusout will collapse and remove the selection + if (!editor.selection.isCollapsed() && !isSameElementPath(startElm) && editor.dom.isChildOf(startElm, editor.getBody())) { editor.nodeChanged({selectionChange: true}); } }); // Fire an extra nodeChange on mouseup for compatibility reasons @@ -7794,11 +7802,11 @@ * @method unwrap */ unwrap: function() { var self = this, node, next; - for (node = self.firstChild; node; ) { + for (node = self.firstChild; node;) { next = node.next; self.insert(node, self, true); node = next; } @@ -7996,15 +8004,15 @@ // Keep empty elements like <img /> if (elements[node.name]) { return false; } - // Keep elements with data attributes or name attribute like <a name="1"></a> + // Keep bookmark nodes and name attribute like <a name="1"></a> i = node.attributes.length; while (i--) { name = node.attributes[i].name; - if (name === "name" || name.indexOf('data-mce-') === 0) { + if (name === "name" || name.indexOf('data-mce-bookmark') === 0) { return false; } } } @@ -9639,11 +9647,11 @@ currentNode.append(tempNode); } else { tempNode = currentNode; } - for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1]; ) { + for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1];) { nextNode = childNode.next; tempNode.append(childNode); childNode = nextNode; } @@ -9888,11 +9896,11 @@ } function removeWhitespaceBefore(node) { var textNode, textVal, sibling; - for (textNode = node.prev; textNode && textNode.type === 3; ) { + for (textNode = node.prev; textNode && textNode.type === 3;) { textVal = textNode.value.replace(endWhiteSpaceRegExp, ''); if (textVal.length > 0) { textNode.value = textVal; textNode = textNode.prev; @@ -11715,12 +11723,12 @@ 'opacity: .5;' + 'filter: alpha(opacity=50);' + 'z-index: 10000' + '}' + rootClass + ' .mce-resize-helper {' + - 'background-color: #555;' + - 'background-color: rgba(0,0,0,0.75);' + + 'background: #555;' + + 'background: rgba(0,0,0,0.75);' + 'border-radius: 3px;' + 'border: 1px;' + 'color: white;' + 'display: none;' + 'font-family: sans-serif;' + @@ -12026,10 +12034,15 @@ } } while ((node = node.parentNode)); } } + // Ignore all events while resizing + if (resizeStarted) { + return; + } + // Remove data-mce-selected from all elements since they might have been copied using Ctrl+c/v each(dom.select('img[data-mce-selected],hr[data-mce-selected]'), function(img) { img.removeAttribute('data-mce-selected'); }); @@ -13108,11 +13121,11 @@ * @see http://www.dotvoid.com/2001/03/using-the-range-object-in-mozilla/ */ getRng: function(w3c) { var self = this, selection, rng, elm, doc = self.win.document, ieRng; - function tryCompareBounderyPoints(how, sourceRange, destinationRange) { + function tryCompareBoundaryPoints(how, sourceRange, destinationRange) { try { return sourceRange.compareBoundaryPoints(how, destinationRange); } catch (ex) { // Gecko throws wrong document exception if the range points // to nodes that where removed from the dom #6690 @@ -13188,12 +13201,12 @@ rng.setStart(elm, 0); rng.setEnd(elm, 0); } if (self.selectedRange && self.explicitRange) { - if (tryCompareBounderyPoints(rng.START_TO_START, rng, self.selectedRange) === 0 && - tryCompareBounderyPoints(rng.END_TO_END, rng, self.selectedRange) === 0) { + if (tryCompareBoundaryPoints(rng.START_TO_START, rng, self.selectedRange) === 0 && + tryCompareBoundaryPoints(rng.END_TO_END, rng, self.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. rng = self.explicitRange; } else { self.selectedRange = null; @@ -13530,10 +13543,34 @@ if (y < viewPort.y || y + 25 > viewPortY + viewPortH) { self.editor.getWin().scrollTo(0, y < viewPortY ? y : y - viewPortH + 25); } }, + placeCaretAt: function(clientX, clientY) { + var doc = this.editor.getDoc(), rng, point; + + if (doc.caretPositionFromPoint) { + point = doc.caretPositionFromPoint(clientX, clientY); + rng = doc.createRange(); + rng.setStart(point.offsetNode, point.offset); + rng.collapse(true); + } else if (doc.caretRangeFromPoint) { + rng = doc.caretRangeFromPoint(clientX, clientY); + } else if (doc.body.createTextRange) { + rng = doc.body.createTextRange(); + + try { + rng.moveToPoint(clientX, clientY); + rng.collapse(true); + } catch (ex) { + rng.collapse(clientY < doc.body.clientHeight); + } + } + + this.setRng(rng); + }, + _moveEndPoint: function(rng, node, start) { var root = node, walker = new TreeWalker(node, root); var nonEmptyElementsMap = this.dom.schema.getNonEmptyElements(); do { @@ -15246,11 +15283,11 @@ node = node.childNodes[offset]; if (node) { offset = node.nodeType === 3 ? node.length : node.childNodes.length; } } - return { node: node, offset: offset }; + return {node: node, offset: offset}; } // If index based start position then resolve it if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) { lastIdx = startContainer.childNodes.length - 1; @@ -16210,11 +16247,11 @@ * * @private * @return {String} HTML contents of the editor excluding some internal bogus elements. */ function getContent() { - var content = trim(editor.getContent({format: 'raw', no_events: 1})); + var content = editor.getContent({format: 'raw', no_events: 1}); var bogusAllRegExp = /<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g; var endTagIndex, index, matchLength, matches, shortEndedElements, schema = editor.schema; content = content.replace(trimContentRegExp, ''); shortEndedElements = schema.getShortEndedElements(); @@ -16232,11 +16269,11 @@ content = content.substring(0, index - matchLength) + content.substring(endTagIndex); bogusAllRegExp.lastIndex = index - matchLength; } - return content; + return trim(content); } function addNonTypingUndoLevel(e) { self.typing = false; self.add({}, e); @@ -17167,11 +17204,11 @@ } dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique // Allow custom handling of new blocks - editor.fire('NewBlock', { newBlock: newBlock }); + editor.fire('NewBlock', {newBlock: newBlock}); undoManager.add(); } editor.on('keydown', function(evt) { @@ -17707,11 +17744,11 @@ } // Setup parser and serializer parser = editor.parser; serializer = new Serializer({}, editor.schema); - bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;&#200B;</span>'; + bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">&#xFEFF;&#x200B;</span>'; // Run beforeSetContent handlers on the HTML to be inserted args = {content: value, format: 'html', selection: true}; editor.fire('BeforeSetContent', args); value = args.content; @@ -17755,11 +17792,13 @@ if (node.attr('id') == 'mce_marker') { marker = node; for (node = node.prev; node; node = node.walk(true)) { if (node.type == 3 || !dom.isBlock(node.name)) { - node.parent.insert(marker, node, node.name === 'br'); + if (editor.schema.isValidChild(node.parent.name, 'span')) { + node.parent.insert(marker, node, node.name === 'br'); + } break; } } } @@ -21670,11 +21709,11 @@ } /** * Returns the navigation root control for the specified control. The navigation root * is the control that the keyboard navigation gets scoped to for example a menubar or toolbar group. - * It will look for parents of the specified target control or the currenty focused control if this option is omitted. + * It will look for parents of the specified target control or the currently focused control if this option is omitted. * * @private * @param {tinymce.ui.Control} targetControl Optional target control to find root of. * @return {tinymce.ui.Control} Navigation root control. */ @@ -24122,11 +24161,11 @@ * larger than the popup size specified). */ self.open = function(args, params) { var win; - editor.editorManager.activeEditor = editor; + editor.editorManager.setActive(editor); args.title = args.title || ' '; // Handle URL args.url = args.url || args.file; // Legacy @@ -24779,21 +24818,23 @@ * WebKit has a bug where it isn't possible to select image, hr or anchor elements * by clicking on them so we need to fake that. */ function selectControlElements() { editor.on('click', function(e) { - e = e.target; + var target = e.target; // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 // WebKit can't even do simple things like selecting an image // Needs tobe the setBaseAndExtend or it will fail to select floated images - if (/^(IMG|HR)$/.test(e.nodeName)) { - selection.getSel().setBaseAndExtent(e, 0, e, 1); + if (/^(IMG|HR)$/.test(target.nodeName)) { + e.preventDefault(); + selection.getSel().setBaseAndExtent(target, 0, target, 1); } - if (e.nodeName == 'A' && dom.hasClass(e, 'mce-item-anchor')) { - selection.select(e); + if (target.nodeName == 'A' && dom.hasClass(target, 'mce-item-anchor')) { + e.preventDefault(); + selection.select(target); } }); } /** @@ -25422,10 +25463,17 @@ each('pageX pageY clientX clientY screenX screenY'.split(' '), function(key) { args[key] = endTouch[key]; }); args = editor.fire('click', args); + + if (!args.isDefaultPrevented()) { + // iOS WebKit can't place the caret properly once + // you bind touch events so we need to do this manually + // TODO: Expand to the closest word? Touble tap still works. + editor.selection.placeCaretAt(endTouch.clientX, endTouch.clientY); + } }); }); } /** @@ -26619,10 +26667,14 @@ if (bodyClass.indexOf('=') != -1) { bodyClass = self.getParam('body_class', '', 'hash'); bodyClass = bodyClass[self.id] || ''; } + if (settings.content_security_policy) { + self.iframeHTML += '<meta http-equiv="Content-Security-Policy" content="' + settings.content_security_policy + '" />'; + } + self.iframeHTML += '</head><body id="' + bodyId + '" class="mce-content-body ' + bodyClass + '" data-id="' + self.id + '"><br></body></html>'; /*eslint no-script-url:0 */ @@ -26657,11 +26709,11 @@ ifr.onload = function() { ifr.onload = null; self.fire("load"); }; - DOM.setAttrib("src", url || 'javascript:""'); + DOM.setAttrib(ifr, "src", url || 'javascript:""'); self.contentAreaContainer = o.iframeContainer; self.iframeElement = ifr; n = DOM.add(o.iframeContainer, ifr); @@ -27008,11 +27060,11 @@ * * @method focus * @param {Boolean} skipFocus Skip DOM focus. Just set is as the active editor. */ focus: function(skipFocus) { - var oed, self = this, selection = self.selection, contentEditable = self.settings.content_editable, rng; + var self = this, selection = self.selection, contentEditable = self.settings.content_editable, rng; var controlElm, doc = self.getDoc(), body; if (!skipFocus) { // Get selected control element rng = selection.getRng(); @@ -27062,19 +27114,11 @@ rng.addElement(controlElm); rng.select(); } } - if (self.editorManager.activeEditor != self) { - if ((oed = self.editorManager.activeEditor)) { - oed.fire('deactivate', {relatedTarget: self}); - } - - self.fire('activate', {relatedTarget: oed}); - } - - self.editorManager.activeEditor = self; + self.editorManager.setActive(self); }, /** * Executes a legacy callback. This method is useful to call old 2.x option callbacks. * There new event model is a better way to add callback so this method might be removed in the future. @@ -28326,11 +28370,15 @@ editor.on('init', function() { // Gecko/WebKit has ghost selections in iframes and IE only has one selection per browser tab if (editor.inline || Env.ie) { // Use the onbeforedeactivate event when available since it works better see #7023 if ("onbeforedeactivate" in document && Env.ie < 9) { - editor.dom.bind(editor.getBody(), 'beforedeactivate', function() { + editor.dom.bind(editor.getBody(), 'beforedeactivate', function(e) { + if (e.target != editor.getBody()) { + return; + } + try { editor.lastRng = editor.selection.getRng(); } catch (ex) { // IE throws "Unexcpected call to method or property access" some times so lets ignore it } @@ -28397,11 +28445,11 @@ if (focusedEditor != editor) { if (focusedEditor) { focusedEditor.fire('blur', {focusedEditor: editor}); } - editorManager.activeEditor = editor; + editorManager.setActive(editor); editorManager.focusedEditor = editor; editor.fire('focus', {blurredEditor: focusedEditor}); editor.focus(true); } @@ -28437,11 +28485,11 @@ if (activeEditor.selection && e.target != activeEditor.getBody()) { activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.dom, activeEditor.lastRng); } // Fire a blur event if the element isn't a UI element - if (!isUIElement(e.target) && editorManager.focusedEditor == activeEditor) { + if (e.target != document.body && !isUIElement(e.target) && editorManager.focusedEditor == activeEditor) { activeEditor.fire('blur', {focusedEditor: null}); editorManager.focusedEditor = null; } } }; @@ -28597,19 +28645,19 @@ * Minor version of TinyMCE build. * * @property minorVersion * @type String */ - minorVersion: '1.3', + minorVersion: '1.4', /** * Release date of TinyMCE build. * * @property releaseDate * @type String */ - releaseDate: '2014-07-29', + releaseDate: '2014-08-21', /** * Collection of editor instances. * * @property editors @@ -28743,11 +28791,11 @@ * tinyMCE.init({ * some_settings : 'some value' * }); */ init: function(settings) { - var self = this, editors = [], editor; + var self = this, editors = []; function createId(elm) { var id = elm.id; // Use element id, or unique name or generate a unique id @@ -28768,10 +28816,11 @@ } function createEditor(id, settings, targetElm) { if (!purgeDestroyedEditor(self.get(id))) { var editor = new Editor(id, settings, self); + editor.targetElm = editor.targetElm || targetElm; editors.push(editor); editor.render(); } } @@ -28821,22 +28870,22 @@ switch (settings.mode) { case "exact": l = settings.elements || ''; if (l.length > 0) { - each(explode(l), function(v) { - if (DOM.get(v)) { - editor = new Editor(v, settings, self); - editors.push(editor); - editor.render(); + each(explode(l), function(id) { + var elm; + + if ((elm = DOM.get(id))) { + createEditor(id, settings, elm); } else { each(document.forms, function(f) { each(f.elements, function(e) { - if (e.name === v) { - v = 'mce_editor_' + instanceCounter++; - DOM.setAttrib(e, 'id', v); - createEditor(v, settings, e); + if (e.name === id) { + id = 'mce_editor_' + instanceCounter++; + DOM.setAttrib(e, 'id', id); + createEditor(id, settings, e); } }); }); } }); @@ -28928,10 +28977,12 @@ // Add named and index editor instance editors[editor.id] = editor; editors.push(editor); + // Doesn't call setActive method since we don't want + // to fire a bunch of activate/deactivate calls while initializing self.activeEditor = editor; /** * Fires when an editor is added to the EditorManager collection. * @@ -29120,10 +29171,30 @@ * @param {String/Array/Object} text String to translate * @return {String} Translated string. */ translate: function(text) { return I18n.translate(text); + }, + + /** + * Sets the active editor instance and fires the deactivate/activate events. + * + * @method setActive + * @param {tinymce.Editor} editor Editor instance to set as the active instance. + */ + setActive: function(editor) { + var activeEditor = this.activeEditor; + + if (this.activeEditor != editor) { + if (activeEditor) { + activeEditor.fire('deactivate', {relatedTarget: editor}); + } + + editor.fire('activate', {relatedTarget: activeEditor}); + } + + this.activeEditor = editor; } }; extend(EditorManager, Observable); @@ -31046,21 +31117,32 @@ */ init: function(settings) { var self = this; settings.spellcheck = false; - settings.icon = 'none'; + if (settings.onaction) { + settings.icon = 'none'; + } + self._super(settings); self.addClass('colorbox'); self.on('change keyup postrender', function() { self.repaintColor(self.value()); }); }, repaintColor: function(value) { - this.getEl().getElementsByTagName('i')[0].style.background = value; + var elm = this.getEl().getElementsByTagName('i')[0]; + + if (elm) { + try { + elm.style.background = value; + } catch (ex) { + // Ignore + } + } }, value: function(value) { var self = this; \ No newline at end of file