vendor/assets/javascripts/wysihtml5x.js in wysihtml5x-rails-0.4.5 vs vendor/assets/javascripts/wysihtml5x.js in wysihtml5x-rails-0.4.6

- old
+ new

@@ -23,22 +23,22 @@ if(!Array.isArray) { Array.isArray = function(arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }; };/** - * @license wysihtml5x v0.4.5 + * @license wysihtml5x v0.4.6 * https://github.com/Edicy/wysihtml5 * * Author: Christopher Blum (https://github.com/tiff) * Secondary author of extended features: Oliver Pulges (https://github.com/pulges) * * Copyright (C) 2012 XING AG * Licensed under the MIT license (MIT) * */ var wysihtml5 = { - version: "0.4.5", + version: "0.4.6", // namespaces commands: {}, dom: {}, quirks: {}, @@ -5360,78 +5360,26 @@ return !!styles.length; } return styles[styles.length - 1] === cssStyle; } - function _getParentElementWithNodeName(node, nodeName, levels) { - while (levels-- && node && node.nodeName !== "BODY") { - if (_isSameNodeName(node.nodeName, nodeName)) { - return node; - } - node = node.parentNode; - } - return null; - } + return function(node, matchingSet, levels, container) { + var findByStyle = (matchingSet.cssStyle || matchingSet.styleRegExp), + findByClass = (matchingSet.className || matchingSet.classRegExp); - function _getParentElementWithNodeNameAndClassName(node, nodeName, className, classRegExp, levels) { - while (levels-- && node && node.nodeName !== "BODY") { - if (_isElement(node) && - _isSameNodeName(node.nodeName, nodeName) && - _hasClassName(node, className, classRegExp)) { - return node; - } - node = node.parentNode; - } - return null; - } + levels = levels || 50; // Go max 50 nodes upwards from current node - function _getParentElementWithNodeNameAndStyle(node, nodeName, cssStyle, styleRegExp, levels) { - while (levels-- && node && node.nodeName !== "BODY") { - if (_isElement(node) && - _isSameNodeName(node.nodeName, nodeName) && - _hasStyle(node, cssStyle, styleRegExp) + while (levels-- && node && node.nodeName !== "BODY" && (!container || node !== container)) { + if (_isElement(node) && _isSameNodeName(node.nodeName, matchingSet.nodeName) && + (!findByStyle || _hasStyle(node, matchingSet.cssStyle, matchingSet.styleRegExp)) && + (!findByClass || _hasClassName(node, matchingSet.className, matchingSet.classRegExp)) ) { return node; } node = node.parentNode; } return null; - } - - function _getParentElementWithNodeNameAndClassNameAndStyle(node, nodeName, className, classRegExp, cssStyle, styleRegExp, levels) { - while (levels-- && node && node.nodeName !== "BODY") { - if (_isElement(node) && - _isSameNodeName(node.nodeName, nodeName) && - _hasStyle(node, cssStyle, styleRegExp) && - _hasClassName(node, className, classRegExp) - ) { - return node; - } - node = node.parentNode; - } - return null; - } - - return function(node, matchingSet, levels) { - levels = levels || 50; // Go max 50 nodes upwards from current node - if ((matchingSet.className || matchingSet.classRegExp) && (matchingSet.cssStyle || matchingSet.styleRegExp)) { - return _getParentElementWithNodeNameAndClassNameAndStyle( - node, matchingSet.nodeName, matchingSet.className, matchingSet.classRegExp, matchingSet.cssStyle, matchingSet.styleRegExp, levels - ); - } else if (matchingSet.className || matchingSet.classRegExp) { - return _getParentElementWithNodeNameAndClassName( - node, matchingSet.nodeName, matchingSet.className, matchingSet.classRegExp, levels - ); - } else if (matchingSet.cssStyle || matchingSet.styleRegExp) { - return _getParentElementWithNodeNameAndStyle( - node, matchingSet.nodeName, matchingSet.cssStyle, matchingSet.styleRegExp, levels - ); - } else { - return _getParentElementWithNodeName( - node, matchingSet.nodeName, levels - ); - } }; })(); ;wysihtml5.dom.getNextElement = function(node){ var nextSibling = node.nextSibling; while(nextSibling && nextSibling.nodeType != 1) { @@ -5883,11 +5831,10 @@ newNode, tagRules = currentRules.tags, nodeName = oldNode.nodeName.toLowerCase(), scopeName = oldNode.scopeName; - /** * We already parsed that element * ignore it! (yes, this sometimes happens in IE8 when the html is invalid) */ if (oldNode._wysihtml5) { @@ -5938,11 +5885,11 @@ newNode = oldNode.ownerDocument.createElement(rule.rename_tag || nodeName); _handleAttributes(oldNode, newNode, rule); _handleStyles(oldNode, newNode, rule); // tests if type condition is met or node should be removed/unwrapped - if (rule.one_of_type && !_testTypes(newNode, currentRules, rule.one_of_type)) { + if (rule.one_of_type && !_testTypes(oldNode, currentRules, rule.one_of_type)) { return (rule.remove_action && rule.remove_action == "unwrap") ? false : null; } oldNode = null; @@ -5983,10 +5930,22 @@ var nodeClasses = oldNode.getAttribute("class"), nodeStyles = oldNode.getAttribute("style"), classesLength, s, s_corrected, a, attr, currentClass, styleProp; + // test for methods + if (definition.methods) { + for (var m in definition.methods) { + if (definition.methods.hasOwnProperty(m) && typeCeckMethods[m]) { + + if (typeCeckMethods[m](oldNode)) { + return true; + } + } + } + } + // test for classes, if one found return true if (nodeClasses && definition.classes) { nodeClasses = nodeClasses.replace(/^\s+/g, '').replace(/\s+$/g, '').split(WHITE_SPACE_REG_EXP); classesLength = nodeClasses.length; for (var i = 0; i < classesLength; i++) { @@ -6362,10 +6321,44 @@ return mapping[String(attributeValue).charAt(0)]; }; })() }; + // checks if element is possibly visible + var typeCeckMethods = { + has_visible_contet: (function() { + var txt, + isVisible = false, + visibleElements = ['img', 'video', 'picture', 'br', 'script', 'noscript', + 'style', 'table', 'iframe', 'object', 'embed', 'audio', + 'svg', 'input', 'button', 'select','textarea', 'canvas']; + + return function(el) { + + // has visible innertext. so is visible + txt = (el.innerText || el.textContent).replace(/\s/g, ''); + if (txt && txt.length > 0) { + return true; + } + + // matches list of visible dimensioned elements + for (var i = visibleElements.length; i--;) { + if (el.querySelector(visibleElements[i])) { + return true; + } + } + + // try to measure dimesions in last resort. (can find only of elements in dom) + if (el.offsetWidth && el.offsetWidth > 0 && el.offsetHeight && el.offsetHeight > 0) { + return true; + } + + return false; + }; + })() + }; + return parse; })(); ;/** * Checks for empty text node childs and removes them * @@ -8553,10 +8546,23 @@ } return (ret !== this.contain) ? ret : false; }, + getSelectionParentsByTag: function(tagName) { + var nodes = this.getSelectedOwnNodes(), + curEl, parents = []; + + for (var i = 0, maxi = nodes.length; i < maxi; i++) { + curEl = (nodes[i].nodeName && nodes[i].nodeName === 'LI') ? nodes[i] : wysihtml5.dom.getParentElement(nodes[i], { nodeName: ['LI']}, false, this.contain); + if (curEl) { + parents.push(curEl); + } + } + return (parents.length) ? parents : null; + }, + getRangeToNodeEnd: function() { if (this.isCollapsed()) { var range = this.getRange(), sNode = range.startContainer, pos = range.startOffset, @@ -9604,10 +9610,11 @@ isAppliedToRange: function(range) { var ancestors = [], ancestor, styleAncestor, textNodes; for (var ri = range.length; ri--;) { + textNodes = range[ri].getNodes([wysihtml5.TEXT_NODE]); if (!textNodes.length) { ancestor = this.getAncestorWithClass(range[ri].startContainer); if (!ancestor) { ancestor = this.getAncestorWithStyle(range[ri].startContainer); @@ -9619,14 +9626,15 @@ selectedText = this.getTextSelectedByRange(textNodes[i], range[ri]); ancestor = this.getAncestorWithClass(textNodes[i]); if (!ancestor) { ancestor = this.getAncestorWithStyle(textNodes[i]); } - if (!(selectedText != "" && !ancestor)) { + if (ancestor && selectedText != "") { ancestors.push(ancestor); } } + } return (ancestors.length) ? ancestors : false; }, @@ -10459,17 +10467,23 @@ var alias = ALIAS_MAPPING[tagName]; return alias ? [tagName.toLowerCase(), alias.toLowerCase()] : [tagName.toLowerCase()]; } function _getApplier(tagName, className, classRegExp, cssStyle, styleRegExp, container) { - var identifier = tagName + ":" + className; + var identifier = tagName; + + if (className) { + identifier += ":" + className; + } if (cssStyle) { identifier += ":" + cssStyle; } + if (!htmlApplier[identifier]) { htmlApplier[identifier] = new wysihtml5.selection.HTMLApplier(_getTagNames(tagName), className, classRegExp, true, cssStyle, styleRegExp, container); } + return htmlApplier[identifier]; } wysihtml5.commands.formatInline = { exec: function(composer, command, tagName, className, classRegExp, cssStyle, styleRegExp, dontRestoreSelect, noCleanup) { @@ -10544,11 +10558,11 @@ return false; } ownRanges = composer.selection.getOwnRanges(); - if (ownRanges.length == 0) { + if (!ownRanges || ownRanges.length === 0) { return false; } return _getApplier(tagName, className, classRegExp, cssStyle, styleRegExp, composer.element).isAppliedToRange(ownRanges); } @@ -11080,11 +11094,129 @@ state: function(composer, command) { return false; } }; -;/** +;wysihtml5.commands.indentList = { + exec: function(composer, command, value) { + var listEls = composer.selection.getSelectionParentsByTag('LI'); + if (listEls) { + return this.tryToPushLiLevel(listEls, composer.selection); + } + return false; + }, + + state: function(composer, command) { + return false; + }, + + tryToPushLiLevel: function(liNodes, selection) { + var listTag, list, prevLi, liNode, prevLiList, + found = false; + + selection.executeAndRestoreRangy(function() { + + for (var i = liNodes.length; i--;) { + liNode = liNodes[i]; + listTag = (liNode.parentNode.nodeName === 'OL') ? 'OL' : 'UL'; + list = liNode.ownerDocument.createElement(listTag); + prevLi = wysihtml5.dom.getPreviousElement(liNode); + prevLiList = (prevLi) ? prevLi.querySelector('ul, ol') : null; + + if (prevLi) { + if (prevLiList) { + prevLiList.appendChild(liNode); + } else { + list.appendChild(liNode); + prevLi.appendChild(list); + } + found = true; + } + } + + }); + return found; + } +}; +;wysihtml5.commands.outdentList = { + exec: function(composer, command, value) { + var listEls = composer.selection.getSelectionParentsByTag('LI'); + if (listEls) { + return this.tryToPullLiLevel(listEls, composer); + } + return false; + }, + + state: function(composer, command) { + return false; + }, + + tryToPullLiLevel: function(liNodes, composer) { + var listNode, outerListNode, outerLiNode, list, prevLi, liNode, afterList, + found = false, + that = this; + + composer.selection.executeAndRestoreRangy(function() { + + for (var i = liNodes.length; i--;) { + liNode = liNodes[i]; + if (liNode.parentNode) { + listNode = liNode.parentNode; + + if (listNode.tagName === 'OL' || listNode.tagName === 'UL') { + found = true; + + outerListNode = wysihtml5.dom.getParentElement(listNode.parentNode, { nodeName: ['OL', 'UL']}, false, composer.element); + outerLiNode = wysihtml5.dom.getParentElement(listNode.parentNode, { nodeName: ['LI']}, false, composer.element); + + if (outerListNode && outerLiNode) { + + if (liNode.nextSibling) { + afterList = that.getAfterList(listNode, liNode); + liNode.appendChild(afterList); + } + outerListNode.insertBefore(liNode, outerLiNode.nextSibling); + + } else { + + if (liNode.nextSibling) { + afterList = that.getAfterList(listNode, liNode); + liNode.appendChild(afterList); + } + + for (var j = liNode.childNodes.length; j--;) { + listNode.parentNode.insertBefore(liNode.childNodes[j], listNode.nextSibling); + } + + listNode.parentNode.insertBefore(document.createElement('br'), listNode.nextSibling); + liNode.parentNode.removeChild(liNode); + + } + + // cleanup + if (listNode.childNodes.length === 0) { + listNode.parentNode.removeChild(listNode); + } + } + } + } + + }); + return found; + }, + + getAfterList: function(listNode, liNode) { + var nodeName = listNode.nodeName, + newList = document.createElement(nodeName); + + while (liNode.nextSibling) { + newList.appendChild(liNode.nextSibling); + } + return newList; + } + +};;/** * Undo Manager for wysihtml5 * slightly inspired by http://rniwa.com/editing/undomanager.html#the-undomanager-interface */ (function(wysihtml5) { var Z_KEY = 90, @@ -12057,14 +12189,17 @@ selection.setBefore(curNode); } } }; - var handleDeleteKeyPress = function(event, selection, element) { + var handleDeleteKeyPress = function(event, selection, element, composer) { if (selection.isCollapsed()) { - if (selection.caretIsInTheBeginnig()) { + if (selection.caretIsInTheBeginnig('LI')) { event.preventDefault(); + composer.commands.exec('outdentList'); + } else if (selection.caretIsInTheBeginnig()) { + event.preventDefault(); } else { var beforeUneditable = selection.caretIsBeforeUneditable(); // Do a special delete if caret would delete uneditable if (beforeUneditable) { @@ -12076,42 +12211,15 @@ event.preventDefault(); selection.deleteContents(); } }; - var tryToPushLiLevel = function(selection) { - var prevLi; - selection.executeAndRestoreRangy(function() { - var selNode = selection.getSelectedNode(), - liNode = (selNode.nodeName && selNode.nodeName === 'LI') ? selNode : selNode.parentNode, - listTag, list; - - if (liNode.getAttribute('class') === "rangySelectionBoundary") { - liNode = liNode.parentNode; - } - - if (liNode.nodeName === 'LI') { - listTag = (liNode.parentNode.nodeName === 'OL') ? 'OL' : 'UL'; - list = selNode.ownerDocument.createElement(listTag); - prevLi = wysihtml5.dom.getPreviousElement(liNode); - - if (prevLi) { - list.appendChild(liNode); - prevLi.appendChild(list); - } - } - - }); - return (prevLi) ? true : false; - }; - - var handleTabKeyDown = function(composer, element) { if (!composer.selection.isCollapsed()) { composer.selection.deleteContents(); } else if (composer.selection.caretIsInTheBeginnig('LI')) { - if (tryToPushLiLevel(composer.selection)) return; + if (composer.commands.exec('indentList')) return; } // Is &emsp; close enough to tab. Could not find enough counter arguments for now. composer.commands.exec("insertHTML", "&emsp;"); }; @@ -12258,12 +12366,12 @@ that.commands.exec(command); event.preventDefault(); } if (keyCode === 8) { // delete key - handleDeleteKeyPress(event, that.selection, element); - } else if (keyCode === 9) { + handleDeleteKeyPress(event, that.selection, element, that); + } else if (that.config.handleTabKey && keyCode === 9) { event.preventDefault(); handleTabKeyDown(that, element); } }); @@ -12538,10 +12646,12 @@ showToolbarAfterInit: true, // Whether urls, entered by the user should automatically become clickable-links autoLink: true, // Includes table editing events and cell selection tracking handleTables: true, + // Tab key inserts tab into text as default behaviour. It can be disabled to regain keyboard navigation + handleTabKey: true, // Object which includes parser rules to apply when html gets inserted via copy & paste // See parser_rules/*.js for examples parserRules: { tags: { br: {}, span: {}, div: {}, p: {} }, classes: {} }, // Parser method to use when the user inserts content via copy & paste parser: wysihtml5.dom.parse, @@ -12559,11 +12669,10 @@ supportTouchDevices: true, // Whether senseless <span> elements (empty or without attributes) should be removed/replaced with their content cleanUp: true, // Whether to use div instead of secure iframe contentEditableMode: false, - xingAlert: false, // Classname of container that editor should not touch and pass through // Pass false to disable uneditableContainerClassname: "wysihtml5-uneditable-container" }; @@ -12599,14 +12708,9 @@ if (typeof(this.config.parser) === "function") { this._initParser(); } this.on("beforeload", this.handleBeforeLoad); - - - if (this.config.xingAlert) { - try { console.log("Heya! This page is using wysihtml5 for rich text editing. Check out https://github.com/xing/wysihtml5");} catch(e) {} - } }, handleBeforeLoad: function() { if (!this.config.noTextarea) { this.synchronizer = new wysihtml5.views.Synchronizer(this, this.textarea, this.composer);