diff --git a/demo/content-kit-compiler.js b/demo/content-kit-compiler.js index 25a461119..70de24ed2 100755 --- a/demo/content-kit-compiler.js +++ b/demo/content-kit-compiler.js @@ -3,7 +3,7 @@ * @version 0.1.0 * @author Garth Poitras (http://garthpoitras.com/) * @license MIT - * Last modified: Jul 10, 2014 + * Last modified: Jul 12, 2014 */ (function(exports, document, undefined) { @@ -221,7 +221,7 @@ function inherit(Sub, Super) { var RegExpTrim = /^\s+|\s+$/g, RegExpTrimLeft = /^\s+/, RegExpWSChars = /(\r\n|\n|\r|\t|\u00A0)/gm, - RegExpMultiWS = / +/g; + RegExpMultiWS = /\s+/g; /** * String.prototype.trim polyfill @@ -325,7 +325,7 @@ HTMLParser.prototype.parse = function(html) { handleNonBlockElementAtRoot(currentNode, blocks); } } else if (currentNode.nodeType === 3) { - text = textOfNode(currentNode); + text = currentNode.nodeValue; if (trim(text)) { block = getLastBlockOrCreate(blocks); block.value += text; diff --git a/demo/demo.css b/demo/demo.css index 27fd171af..80af9b1ea 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -9,6 +9,7 @@ body { color: #333; margin: 0 1.45em 3em; padding: 0; + overflow-y: scroll; } @media only screen and (max-width: 767px) { body { @@ -50,11 +51,14 @@ header { .mode-buttons button { background-color: transparent; border: 1px solid #007aff; + outline: none; color: #007aff; border-radius: 5px; - padding: 0.5em 1.4em; - min-width: 8.7em; + padding: 0.6em 1em; + margin: 0; + min-width: 8.5em; font-size: 0.75em; + line-height: 1.1em; cursor: pointer; transition: background-color 0.15s; } @@ -68,7 +72,6 @@ header { } .mode-buttons button:focus { outline: none; - cursor: pointer; } #code-panes { diff --git a/dist/content-kit-editor.js b/dist/content-kit-editor.js index d24ce2c30..a66fe43ec 100644 --- a/dist/content-kit-editor.js +++ b/dist/content-kit-editor.js @@ -3,7 +3,7 @@ * @version 0.1.0 * @author Garth Poitras (http://garthpoitras.com/) * @license MIT - * Last modified: Jul 11, 2014 + * Last modified: Jul 13, 2014 */ (function(exports, document) { @@ -93,7 +93,8 @@ function swapElements(elementToShow, elementToHide) { showElement(elementToShow); } -function getTargetNodeDescendentWithTag(tag, target, container) { +function getEventTargetMatchingTag(tag, target, container) { + // Traverses up DOM from an event target to find the node matching specifed tag while (target && target !== container) { if (target.tagName === tag) { return target; @@ -102,17 +103,45 @@ function getTargetNodeDescendentWithTag(tag, target, container) { } } -function getElementOffset(element) { - var offset = { left: 0, top: 0 }; - var elementStyle = window.getComputedStyle(element); +function getElementRelativeOffset(element) { + var offset = { left: 0, top: -window.pageYOffset }; + var offsetParent = element.offsetParent; + var offsetParentPosition = window.getComputedStyle(offsetParent).position; + var offsetParentRect; - if (elementStyle.position === 'relative') { - offset.left = parseInt(elementStyle['margin-left'], 10); - offset.top = parseInt(elementStyle['margin-top'], 10); + if (offsetParentPosition === 'relative') { + offsetParentRect = offsetParent.getBoundingClientRect(); + offset.left = offsetParentRect.left; + offset.top = offsetParentRect.top; } return offset; } +function getElementComputedStyleNumericProp(element, prop) { + return parseFloat(window.getComputedStyle(element)[prop]); +} + +function positionElementToRect(element, rect, verticalOffset) { + var horizontalCenter = (rect.left + rect.right) / 2; + var relativeOffset = getElementRelativeOffset(element); + var style = element.style; + var round = Math.round; + + verticalOffset = verticalOffset || 0; + style.left = round(horizontalCenter - relativeOffset.left - (element.offsetWidth / 2)) + 'px'; + style.top = round(rect.top - relativeOffset.top - verticalOffset) + 'px'; +} + +function positionElementAbove(element, aboveElement) { + var elementMargin = getElementComputedStyleNumericProp(element, 'marginBottom'); + positionElementToRect(element, aboveElement.getBoundingClientRect(), element.offsetHeight + elementMargin); +} + +function positionElementBelow(element, belowElement) { + var elementMargin = getElementComputedStyleNumericProp(element, 'marginTop'); + positionElementToRect(element, belowElement.getBoundingClientRect(), -element.offsetHeight - elementMargin); +} + function getDirectionOfSelection(selection) { var position = selection.anchorNode.compareDocumentPosition(selection.focusNode); if (position & Node.DOCUMENT_POSITION_FOLLOWING) { @@ -123,15 +152,15 @@ function getDirectionOfSelection(selection) { return SelectionDirection.SAME_NODE; } -function getCurrentSelectionNode() { - var selection = window.getSelection(); +function getCurrentSelectionNode(selection) { + selection = selection || window.getSelection(); var node = getDirectionOfSelection(selection) === SelectionDirection.LEFT_TO_RIGHT ? selection.anchorNode : selection.focusNode; return node && (node.nodeType === 3 ? node.parentNode : node); } function getCurrentSelectionRootNode() { - var node = getCurrentSelectionNode(), - tag = node.tagName; + var node = getCurrentSelectionNode(); + var tag = node.tagName; while (tag && RootTags.indexOf(tag) === -1) { if (node.contentEditable === 'true') { break; } // Stop traversing up dom when hitting an editor element node = node.parentNode; @@ -151,8 +180,8 @@ function getCurrentSelectionRootTag() { } function tagsInSelection(selection) { - var node = selection.focusNode.parentNode, - tags = []; + var node = getCurrentSelectionNode(selection); + var tags = []; if (!selection.isCollapsed) { while(node) { if (node.contentEditable === 'true') { break; } // Stop traversing up dom when hitting an editor element @@ -166,8 +195,8 @@ function tagsInSelection(selection) { } function moveCursorToBeginningOfSelection(selection) { - var range = document.createRange(), - node = selection.anchorNode; + var range = document.createRange(); + var node = selection.anchorNode; range.setStart(node, 0); range.setEnd(node, 0); selection.removeAllRanges(); @@ -181,8 +210,8 @@ function restoreRange(range) { } function selectNode(node) { - var range = document.createRange(), - selection = window.getSelection(); + var range = document.createRange(); + var selection = window.getSelection(); range.setStart(node, 0); range.setEnd(node, node.length); selection.removeAllRanges(); @@ -234,15 +263,13 @@ var Prompt = (function() { }; function hiliteRange(range) { - var rangeBounds = range.getBoundingClientRect(); - var hiliterStyle = hiliter.style; - var offset = getElementOffset(container); - - hiliterStyle.width = rangeBounds.width + 'px'; - hiliterStyle.height = rangeBounds.height + 'px'; - hiliterStyle.left = rangeBounds.left - offset.left + 'px'; - hiliterStyle.top = rangeBounds.top + window.pageYOffset - offset.top + 'px'; + var rect = range.getBoundingClientRect(); + var style = hiliter.style; + + style.width = rect.width + 'px'; + style.height = rect.height + 'px'; container.appendChild(hiliter); + positionElementToRect(hiliter, rect); } function unhiliteRange() { @@ -485,6 +512,8 @@ ContentKit.Editor = (function() { if (element) { var className = element.className; var dataset = element.dataset; + var toolbar = new Toolbar({ commands: editor.commands }); + var linkTooltips; if (!editorClassNameRegExp.test(className)) { className += (className ? ' ' : '') + editorClassName; @@ -494,41 +523,25 @@ ContentKit.Editor = (function() { if (!dataset.placeholder) { dataset.placeholder = editor.placeholder; } - if(!editor.spellcheck) { element.spellcheck = false; } + element.setAttribute('contentEditable', true); editor.element = element; - editor.toolbar = new Toolbar({ commands: editor.commands }); + editor.toolbar = toolbar; + + linkTooltips = new Tooltip({ rootElement: element, showForTag: Tags.LINK }); bindTextSelectionEvents(editor); bindTypingEvents(editor); bindPasteEvents(editor); - bindLinkTooltips(editor); - editor.enable(); if(editor.autofocus) { element.focus(); } } } Editor.prototype = { - enable: function() { - var editor = this; - var element = editor.element; - if(element && !editor.enabled) { - element.setAttribute('contentEditable', true); - editor.enabled = true; - } - }, - disable: function() { - var editor = this; - var element = editor.element; - if(element && editor.enabled) { - element.removeAttribute('contentEditable'); - editor.enabled = false; - } - }, parse: function() { var editor = this; if (!editor.parser) { @@ -633,28 +646,6 @@ ContentKit.Editor = (function() { }); } - function bindLinkTooltips(editor) { - var linkTooltip = new Tooltip(); - var editorElement = editor.element; - var tooltipTimeout = null; - editorElement.addEventListener('mouseover', function(e) { - if (!editor.toolbar.isShowing) { - tooltipTimeout = setTimeout(function() { - var linkTarget = getTargetNodeDescendentWithTag(Tags.LINK, e.target, this); - if (linkTarget) { - linkTooltip.showLink(linkTarget.href, linkTarget); - } - }, 200); - } - }); - editorElement.addEventListener('mouseout', function(e) { - clearTimeout(tooltipTimeout); - if (e.toElement && e.toElement.className !== 'ck-tooltip') { - linkTooltip.hide(); - } - }); - } - function plainTextToBlocks(plainText, blockTag) { var blocks = plainText.split(Regex.NEWLINE), len = blocks.length, @@ -717,8 +708,12 @@ var Toolbar = (function() { }, hide: function() { var toolbar = this; + var element = toolbar.element; + var style = element.style; if(toolbar.isShowing) { - container.removeChild(toolbar.element); + container.removeChild(element); + style.left = ''; + style.top = ''; toolbar.dismissPrompt(); toolbar.isShowing = false; } @@ -750,20 +745,8 @@ var Toolbar = (function() { }, positionToSelection: function(selection) { if (!selection.isCollapsed) { - var clientRectBounds = selection.getRangeAt(0).getBoundingClientRect(); - this.setPosition( - (clientRectBounds.left + clientRectBounds.right) / 2, - clientRectBounds.top + window.pageYOffset - ); + positionElementAbove(this.element, selection.getRangeAt(0)); } - }, - setPosition: function(x, y) { - var element = this.element, - style = element.style, - offset = getElementOffset(container); - - style.left = parseInt(x - (element.offsetWidth / 2) - offset.left, 10) + 'px'; - style.top = parseInt(y - element.offsetHeight - offset.top, 10) + 'px'; } }; @@ -857,27 +840,46 @@ var ToolbarButton = (function() { var Tooltip = (function() { var container = document.body; + var className = 'ck-tooltip'; + var delay = 200; - function Tooltip() { + function Tooltip(options) { var tooltip = this; - tooltip.element = createDiv('ck-tooltip'); + var rootElement = options.rootElement; + var timeout; + + tooltip.element = createDiv(className); tooltip.isShowing = false; + + rootElement.addEventListener('mouseover', function(e) { + var target = getEventTargetMatchingTag(options.showForTag, e.target, rootElement); + if (target) { + timeout = setTimeout(function() { + tooltip.showLink(target.href, target); + }, delay); + } + }); + + rootElement.addEventListener('mouseout', function(e) { + clearTimeout(timeout); + var toElement = e.toElement || e.relatedTarget; + if (toElement && toElement.className !== className) { + tooltip.hide(); + } + }); } Tooltip.prototype = { showMessage: function(message, element) { var tooltip = this; var tooltipElement = tooltip.element; - var elementRect = element.getBoundingClientRect(); tooltipElement.innerHTML = message; if (!tooltip.isShowing) { container.appendChild(tooltipElement); tooltip.isShowing = true; } - - tooltipElement.style.left = parseInt(elementRect.left + (element.offsetWidth / 2) - (tooltipElement.offsetWidth / 2), 10) + 'px'; - tooltipElement.style.top = parseInt(window.pageYOffset + elementRect.top + element.offsetHeight + 2, 10) + 'px'; + positionElementBelow(tooltipElement, element); }, showLink: function(link, element) { var message = '' + link + ''; diff --git a/src/css/editor.css b/src/css/editor.css index 121eab24e..9efd5009b 100644 --- a/src/css/editor.css +++ b/src/css/editor.css @@ -31,11 +31,6 @@ text-align: center; position: absolute; z-index: 1; - padding-bottom: 0.5em; /* space for arrows */ -} - -.ck-toolbar-buttons, -.ck-toolbar-prompt { background: -moz-linear-gradient(top, rgba(74,74,74,0.97) 0%, rgba(43,43,43,1) 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(74,74,74,0.97)), color-stop(100%,rgba(43,43,43,1))); background: -webkit-linear-gradient(top, rgba(74,74,74,0.97) 0%,rgba(43,43,43,1) 100%); @@ -44,13 +39,11 @@ background: linear-gradient(to bottom, rgba(74,74,74,0.97) 0%,rgba(43,43,43,1) 100%); box-shadow: 0 1px 3px -1px rgba(0,0,0,0.8), inset 0 2px 0 rgba(255,255,255,0.12), inset 0 1px 0 rgba(0,0,0,0.65); border-radius: 5px; - position: relative; - -webkit-animation: toolbarPop 0.5s linear both; - animation: toolbarPop 0.5s linear both; + transition: left 0.1s, top 0.1s; + margin-bottom: 0.5em; /* space for arrow */ } -/* toolbar arrows */ -.ck-toolbar-buttons:after, -.ck-toolbar-prompt:after { + +.ck-toolbar:after { content: ''; position: absolute; left: 50%; @@ -63,6 +56,12 @@ margin-left: -0.5em; } +.ck-toolbar, +.ck-toolbar-prompt { + -webkit-animation: toolbarPop 0.5s linear both; + animation: toolbarPop 0.5s linear both; +} + .ck-toolbar-btn { background-color: transparent; border: none; @@ -113,6 +112,9 @@ .ck-toolbar-prompt input:focus { outline: none; } +.ck-toolbar-prompt input::-ms-clear { + display: none; +} .ck-toolbar-prompt ::-webkit-input-placeholder { background-color: #a2a2a2; background-image: -webkit-gradient(linear,left top,right top,color-stop(0, #a2a2a2),color-stop(0.4, #a2a2a2),color-stop(0.5, white),color-stop(0.6, #a2a2a2),color-stop(1, #a2a2a2)); @@ -125,10 +127,8 @@ .ck-editor-hilite { position: absolute; z-index: -1; - background-color: rgba(76,217,100,0.05); + background-color: rgba(76,217,100,0.08); border-bottom: 2px dotted #4CD964; - -webkit-animation: fadeIn 0.2s; - animation: fadeIn 0.2s; } .ck-tooltip { @@ -156,6 +156,14 @@ top: -0.4em; margin-left: -0.4em; } +.ck-tooltip:after { /* help keeps mouseover state when moving from link to tooltip */ + content: ''; + position: absolute; + left: 0; + right: 0; + top: -0.4em; + height: 0.4em; +} .ck-tooltip a { color: #FFF; text-decoration: none; @@ -164,6 +172,19 @@ text-decoration: underline; } +/* Safari fixs */ +@media \\0 screen { + /* text rendering when animating opacity */ + .ck-tooltip { + font-weight: 500; + -webkit-font-smoothing: antialiased; + } + /* always transitions from 0,0 */ + .ck-toolbar { + transition: none; + } +} + /* icons */ @font-face { font-family: 'ck-icons'; diff --git a/src/js/editor.js b/src/js/editor.js index 487b29677..8791a75c1 100644 --- a/src/js/editor.js +++ b/src/js/editor.js @@ -52,6 +52,8 @@ ContentKit.Editor = (function() { if (element) { var className = element.className; var dataset = element.dataset; + var toolbar = new Toolbar({ commands: editor.commands }); + var linkTooltips; if (!editorClassNameRegExp.test(className)) { className += (className ? ' ' : '') + editorClassName; @@ -61,41 +63,25 @@ ContentKit.Editor = (function() { if (!dataset.placeholder) { dataset.placeholder = editor.placeholder; } - if(!editor.spellcheck) { element.spellcheck = false; } + element.setAttribute('contentEditable', true); editor.element = element; - editor.toolbar = new Toolbar({ commands: editor.commands }); + editor.toolbar = toolbar; + + linkTooltips = new Tooltip({ rootElement: element, showForTag: Tags.LINK }); bindTextSelectionEvents(editor); bindTypingEvents(editor); bindPasteEvents(editor); - bindLinkTooltips(editor); - editor.enable(); if(editor.autofocus) { element.focus(); } } } Editor.prototype = { - enable: function() { - var editor = this; - var element = editor.element; - if(element && !editor.enabled) { - element.setAttribute('contentEditable', true); - editor.enabled = true; - } - }, - disable: function() { - var editor = this; - var element = editor.element; - if(element && editor.enabled) { - element.removeAttribute('contentEditable'); - editor.enabled = false; - } - }, parse: function() { var editor = this; if (!editor.parser) { @@ -200,28 +186,6 @@ ContentKit.Editor = (function() { }); } - function bindLinkTooltips(editor) { - var linkTooltip = new Tooltip(); - var editorElement = editor.element; - var tooltipTimeout = null; - editorElement.addEventListener('mouseover', function(e) { - if (!editor.toolbar.isShowing) { - tooltipTimeout = setTimeout(function() { - var linkTarget = getTargetNodeDescendentWithTag(Tags.LINK, e.target, this); - if (linkTarget) { - linkTooltip.showLink(linkTarget.href, linkTarget); - } - }, 200); - } - }); - editorElement.addEventListener('mouseout', function(e) { - clearTimeout(tooltipTimeout); - if (e.toElement && e.toElement.className !== 'ck-tooltip') { - linkTooltip.hide(); - } - }); - } - function plainTextToBlocks(plainText, blockTag) { var blocks = plainText.split(Regex.NEWLINE), len = blocks.length, diff --git a/src/js/prompt.js b/src/js/prompt.js index 2109e21e2..8c5883378 100644 --- a/src/js/prompt.js +++ b/src/js/prompt.js @@ -43,15 +43,13 @@ var Prompt = (function() { }; function hiliteRange(range) { - var rangeBounds = range.getBoundingClientRect(); - var hiliterStyle = hiliter.style; - var offset = getElementOffset(container); + var rect = range.getBoundingClientRect(); + var style = hiliter.style; - hiliterStyle.width = rangeBounds.width + 'px'; - hiliterStyle.height = rangeBounds.height + 'px'; - hiliterStyle.left = rangeBounds.left - offset.left + 'px'; - hiliterStyle.top = rangeBounds.top + window.pageYOffset - offset.top + 'px'; + style.width = rect.width + 'px'; + style.height = rect.height + 'px'; container.appendChild(hiliter); + positionElementToRect(hiliter, rect); } function unhiliteRange() { diff --git a/src/js/toolbar.js b/src/js/toolbar.js index 59d5e9f2e..0e85a7802 100644 --- a/src/js/toolbar.js +++ b/src/js/toolbar.js @@ -37,8 +37,12 @@ var Toolbar = (function() { }, hide: function() { var toolbar = this; + var element = toolbar.element; + var style = element.style; if(toolbar.isShowing) { - container.removeChild(toolbar.element); + container.removeChild(element); + style.left = ''; + style.top = ''; toolbar.dismissPrompt(); toolbar.isShowing = false; } @@ -70,20 +74,8 @@ var Toolbar = (function() { }, positionToSelection: function(selection) { if (!selection.isCollapsed) { - var clientRectBounds = selection.getRangeAt(0).getBoundingClientRect(); - this.setPosition( - (clientRectBounds.left + clientRectBounds.right) / 2, - clientRectBounds.top + window.pageYOffset - ); + positionElementAbove(this.element, selection.getRangeAt(0)); } - }, - setPosition: function(x, y) { - var element = this.element, - style = element.style, - offset = getElementOffset(container); - - style.left = parseInt(x - (element.offsetWidth / 2) - offset.left, 10) + 'px'; - style.top = parseInt(y - element.offsetHeight - offset.top, 10) + 'px'; } }; diff --git a/src/js/tooltip.js b/src/js/tooltip.js index 96793fafe..e58774a75 100644 --- a/src/js/tooltip.js +++ b/src/js/tooltip.js @@ -1,27 +1,46 @@ var Tooltip = (function() { var container = document.body; + var className = 'ck-tooltip'; + var delay = 200; - function Tooltip() { + function Tooltip(options) { var tooltip = this; - tooltip.element = createDiv('ck-tooltip'); + var rootElement = options.rootElement; + var timeout; + + tooltip.element = createDiv(className); tooltip.isShowing = false; + + rootElement.addEventListener('mouseover', function(e) { + var target = getEventTargetMatchingTag(options.showForTag, e.target, rootElement); + if (target) { + timeout = setTimeout(function() { + tooltip.showLink(target.href, target); + }, delay); + } + }); + + rootElement.addEventListener('mouseout', function(e) { + clearTimeout(timeout); + var toElement = e.toElement || e.relatedTarget; + if (toElement && toElement.className !== className) { + tooltip.hide(); + } + }); } Tooltip.prototype = { showMessage: function(message, element) { var tooltip = this; var tooltipElement = tooltip.element; - var elementRect = element.getBoundingClientRect(); tooltipElement.innerHTML = message; if (!tooltip.isShowing) { container.appendChild(tooltipElement); tooltip.isShowing = true; } - - tooltipElement.style.left = parseInt(elementRect.left + (element.offsetWidth / 2) - (tooltipElement.offsetWidth / 2), 10) + 'px'; - tooltipElement.style.top = parseInt(window.pageYOffset + elementRect.top + element.offsetHeight + 2, 10) + 'px'; + positionElementBelow(tooltipElement, element); }, showLink: function(link, element) { var message = '' + link + ''; diff --git a/src/js/utils/element-utils.js b/src/js/utils/element-utils.js index 328d7b47f..511fee33c 100644 --- a/src/js/utils/element-utils.js +++ b/src/js/utils/element-utils.js @@ -19,7 +19,8 @@ function swapElements(elementToShow, elementToHide) { showElement(elementToShow); } -function getTargetNodeDescendentWithTag(tag, target, container) { +function getEventTargetMatchingTag(tag, target, container) { + // Traverses up DOM from an event target to find the node matching specifed tag while (target && target !== container) { if (target.tagName === tag) { return target; @@ -28,13 +29,41 @@ function getTargetNodeDescendentWithTag(tag, target, container) { } } -function getElementOffset(element) { - var offset = { left: 0, top: 0 }; - var elementStyle = window.getComputedStyle(element); +function getElementRelativeOffset(element) { + var offset = { left: 0, top: -window.pageYOffset }; + var offsetParent = element.offsetParent; + var offsetParentPosition = window.getComputedStyle(offsetParent).position; + var offsetParentRect; - if (elementStyle.position === 'relative') { - offset.left = parseInt(elementStyle['margin-left'], 10); - offset.top = parseInt(elementStyle['margin-top'], 10); + if (offsetParentPosition === 'relative') { + offsetParentRect = offsetParent.getBoundingClientRect(); + offset.left = offsetParentRect.left; + offset.top = offsetParentRect.top; } return offset; } + +function getElementComputedStyleNumericProp(element, prop) { + return parseFloat(window.getComputedStyle(element)[prop]); +} + +function positionElementToRect(element, rect, verticalOffset) { + var horizontalCenter = (rect.left + rect.right) / 2; + var relativeOffset = getElementRelativeOffset(element); + var style = element.style; + var round = Math.round; + + verticalOffset = verticalOffset || 0; + style.left = round(horizontalCenter - relativeOffset.left - (element.offsetWidth / 2)) + 'px'; + style.top = round(rect.top - relativeOffset.top - verticalOffset) + 'px'; +} + +function positionElementAbove(element, aboveElement) { + var elementMargin = getElementComputedStyleNumericProp(element, 'marginBottom'); + positionElementToRect(element, aboveElement.getBoundingClientRect(), element.offsetHeight + elementMargin); +} + +function positionElementBelow(element, belowElement) { + var elementMargin = getElementComputedStyleNumericProp(element, 'marginTop'); + positionElementToRect(element, belowElement.getBoundingClientRect(), -element.offsetHeight - elementMargin); +} diff --git a/src/js/utils/selection-utils.js b/src/js/utils/selection-utils.js index 245426c01..11ecfbb84 100644 --- a/src/js/utils/selection-utils.js +++ b/src/js/utils/selection-utils.js @@ -8,15 +8,15 @@ function getDirectionOfSelection(selection) { return SelectionDirection.SAME_NODE; } -function getCurrentSelectionNode() { - var selection = window.getSelection(); +function getCurrentSelectionNode(selection) { + selection = selection || window.getSelection(); var node = getDirectionOfSelection(selection) === SelectionDirection.LEFT_TO_RIGHT ? selection.anchorNode : selection.focusNode; return node && (node.nodeType === 3 ? node.parentNode : node); } function getCurrentSelectionRootNode() { - var node = getCurrentSelectionNode(), - tag = node.tagName; + var node = getCurrentSelectionNode(); + var tag = node.tagName; while (tag && RootTags.indexOf(tag) === -1) { if (node.contentEditable === 'true') { break; } // Stop traversing up dom when hitting an editor element node = node.parentNode; @@ -36,8 +36,8 @@ function getCurrentSelectionRootTag() { } function tagsInSelection(selection) { - var node = selection.focusNode.parentNode, - tags = []; + var node = getCurrentSelectionNode(selection); + var tags = []; if (!selection.isCollapsed) { while(node) { if (node.contentEditable === 'true') { break; } // Stop traversing up dom when hitting an editor element @@ -51,8 +51,8 @@ function tagsInSelection(selection) { } function moveCursorToBeginningOfSelection(selection) { - var range = document.createRange(), - node = selection.anchorNode; + var range = document.createRange(); + var node = selection.anchorNode; range.setStart(node, 0); range.setEnd(node, 0); selection.removeAllRanges(); @@ -66,8 +66,8 @@ function restoreRange(range) { } function selectNode(node) { - var range = document.createRange(), - selection = window.getSelection(); + var range = document.createRange(); + var selection = window.getSelection(); range.setStart(node, 0); range.setEnd(node, node.length); selection.removeAllRanges();