diff options
Diffstat (limited to 'contexts/data/lib/closure-library/closure/goog/editor/plugins/enterhandler.js')
-rw-r--r-- | contexts/data/lib/closure-library/closure/goog/editor/plugins/enterhandler.js | 765 |
1 files changed, 0 insertions, 765 deletions
diff --git a/contexts/data/lib/closure-library/closure/goog/editor/plugins/enterhandler.js b/contexts/data/lib/closure-library/closure/goog/editor/plugins/enterhandler.js deleted file mode 100644 index 53aeaa5..0000000 --- a/contexts/data/lib/closure-library/closure/goog/editor/plugins/enterhandler.js +++ /dev/null @@ -1,765 +0,0 @@ -// Copyright 2008 The Closure Library Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS-IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * @fileoverview Plugin to handle enter keys. - * - */ - -goog.provide('goog.editor.plugins.EnterHandler'); - -goog.require('goog.dom'); -goog.require('goog.dom.AbstractRange'); -goog.require('goog.dom.NodeOffset'); -goog.require('goog.dom.NodeType'); -goog.require('goog.dom.TagName'); -goog.require('goog.editor.BrowserFeature'); -goog.require('goog.editor.Plugin'); -goog.require('goog.editor.node'); -goog.require('goog.editor.plugins.Blockquote'); -goog.require('goog.editor.range'); -goog.require('goog.editor.style'); -goog.require('goog.events.KeyCodes'); -goog.require('goog.string'); -goog.require('goog.userAgent'); - - - -/** - * Plugin to handle enter keys. This does all the crazy to normalize (as much as - * is reasonable) what happens when you hit enter. This also handles the - * special casing of hitting enter in a blockquote. - * - * In IE, Webkit, and Opera, the resulting HTML uses one DIV tag per line. In - * Firefox, the resulting HTML uses BR tags at the end of each line. - * - * @constructor - * @extends {goog.editor.Plugin} - */ -goog.editor.plugins.EnterHandler = function() { - goog.editor.Plugin.call(this); -}; -goog.inherits(goog.editor.plugins.EnterHandler, goog.editor.Plugin); - - -/** - * The type of block level tag to add on enter, for browsers that support - * specifying the default block-level tag. Can be overriden by subclasses; must - * be either DIV or P. - * @type {goog.dom.TagName} - * @protected - */ -goog.editor.plugins.EnterHandler.prototype.tag = goog.dom.TagName.DIV; - - -/** @override */ -goog.editor.plugins.EnterHandler.prototype.getTrogClassId = function() { - return 'EnterHandler'; -}; - - -/** @override */ -goog.editor.plugins.EnterHandler.prototype.enable = function(fieldObject) { - goog.base(this, 'enable', fieldObject); - - if (goog.editor.BrowserFeature.SUPPORTS_OPERA_DEFAULTBLOCK_COMMAND && - (this.tag == goog.dom.TagName.P || this.tag == goog.dom.TagName.DIV)) { - var doc = this.getFieldDomHelper().getDocument(); - doc.execCommand('opera-defaultBlock', false, this.tag); - } -}; - - -/** - * If the contents are empty, return the 'default' html for the field. - * The 'default' contents depend on the enter handling mode, so it - * makes the most sense in this plugin. - * @param {string} html The html to prepare. - * @return {string} The original HTML, or default contents if that - * html is empty. - * @override - */ -goog.editor.plugins.EnterHandler.prototype.prepareContentsHtml = function( - html) { - if (!html || goog.string.isBreakingWhitespace(html)) { - return goog.editor.BrowserFeature.COLLAPSES_EMPTY_NODES ? - this.getNonCollapsingBlankHtml() : ''; - } - return html; -}; - - -/** - * Gets HTML with no contents that won't collapse, for browsers that - * collapse the empty string. - * @return {string} Blank html. - * @protected - */ -goog.editor.plugins.EnterHandler.prototype.getNonCollapsingBlankHtml = - goog.functions.constant('<br>'); - - -/** - * Internal backspace handler. - * @param {goog.events.Event} e The keypress event. - * @param {goog.dom.AbstractRange} range The closure range object. - * @protected - */ -goog.editor.plugins.EnterHandler.prototype.handleBackspaceInternal = function(e, - range) { - var field = this.getFieldObject().getElement(); - var container = range && range.getStartNode(); - - if (field.firstChild == container && goog.editor.node.isEmpty(container)) { - e.preventDefault(); - // TODO(user): I think we probably don't need to stopPropagation here - e.stopPropagation(); - } -}; - - -/** - * Fix paragraphs to be the correct type of node. - * @param {goog.events.Event} e The <enter> key event. - * @param {boolean} split Whether we already split up a blockquote by - * manually inserting elements. - * @protected - */ -goog.editor.plugins.EnterHandler.prototype.processParagraphTagsInternal = - function(e, split) { - // Force IE to turn the node we are leaving into a DIV. If we do turn - // it into a DIV, the node IE creates in response to ENTER will also be - // a DIV. If we don't, it will be a P. We handle that case - // in handleKeyUpIE_ - if (goog.userAgent.IE || goog.userAgent.OPERA) { - this.ensureBlockIeOpera(goog.dom.TagName.DIV); - } else if (!split && goog.userAgent.WEBKIT) { - // WebKit duplicates a blockquote when the user hits enter. Let's cancel - // this and insert a BR instead, to make it more consistent with the other - // browsers. - var range = this.getFieldObject().getRange(); - if (!range || !goog.editor.plugins.EnterHandler.isDirectlyInBlockquote( - range.getContainerElement())) { - return; - } - - var dh = this.getFieldDomHelper(); - var br = dh.createElement(goog.dom.TagName.BR); - range.insertNode(br, true); - - // If the BR is at the end of a block element, Safari still thinks there is - // only one line instead of two, so we need to add another BR in that case. - if (goog.editor.node.isBlockTag(br.parentNode) && - !goog.editor.node.skipEmptyTextNodes(br.nextSibling)) { - goog.dom.insertSiblingBefore( - dh.createElement(goog.dom.TagName.BR), br); - } - - goog.editor.range.placeCursorNextTo(br, false); - e.preventDefault(); - } -}; - - -/** - * Determines whether the lowest containing block node is a blockquote. - * @param {Node} n The node. - * @return {boolean} Whether the deepest block ancestor of n is a blockquote. - */ -goog.editor.plugins.EnterHandler.isDirectlyInBlockquote = function(n) { - for (var current = n; current; current = current.parentNode) { - if (goog.editor.node.isBlockTag(current)) { - return current.tagName == goog.dom.TagName.BLOCKQUOTE; - } - } - - return false; -}; - - -/** - * Internal delete key handler. - * @param {goog.events.Event} e The keypress event. - * @protected - */ -goog.editor.plugins.EnterHandler.prototype.handleDeleteGecko = function(e) { - this.deleteBrGecko(e); -}; - - -/** - * Deletes the element at the cursor if it is a BR node, and if it does, calls - * e.preventDefault to stop the browser from deleting. Only necessary in Gecko - * as a workaround for mozilla bug 205350 where deleting a BR that is followed - * by a block element doesn't work (the BR gets immediately replaced). We also - * need to account for an ill-formed cursor which occurs from us trying to - * stop the browser from deleting. - * - * @param {goog.events.Event} e The DELETE keypress event. - * @protected - */ -goog.editor.plugins.EnterHandler.prototype.deleteBrGecko = function(e) { - var range = this.getFieldObject().getRange(); - if (range.isCollapsed()) { - var container = range.getEndNode(); - if (container.nodeType == goog.dom.NodeType.ELEMENT) { - var nextNode = container.childNodes[range.getEndOffset()]; - if (nextNode && nextNode.tagName == goog.dom.TagName.BR) { - // We want to retrieve the first non-whitespace previous sibling - // as we could have added an empty text node below and want to - // properly handle deleting a sequence of BR's. - var previousSibling = goog.editor.node.getPreviousSibling(nextNode); - var nextSibling = nextNode.nextSibling; - - container.removeChild(nextNode); - e.preventDefault(); - - // When we delete a BR followed by a block level element, the cursor - // has a line-height which spans the height of the block level element. - // e.g. If we delete a BR followed by a UL, the resulting HTML will - // appear to the end user like:- - // - // | * one - // | * two - // | * three - // - // There are a couple of cases that we have to account for in order to - // properly conform to what the user expects when DELETE is pressed. - // - // 1. If the BR has a previous sibling and the previous sibling is - // not a block level element or a BR, we place the cursor at the - // end of that. - // 2. If the BR doesn't have a previous sibling or the previous sibling - // is a block level element or a BR, we place the cursor at the - // beginning of the leftmost leaf of its next sibling. - if (nextSibling && goog.editor.node.isBlockTag(nextSibling)) { - if (previousSibling && - !(previousSibling.tagName == goog.dom.TagName.BR || - goog.editor.node.isBlockTag(previousSibling))) { - goog.dom.Range.createCaret( - previousSibling, - goog.editor.node.getLength(previousSibling)).select(); - } else { - var leftMostLeaf = goog.editor.node.getLeftMostLeaf(nextSibling); - goog.dom.Range.createCaret(leftMostLeaf, 0).select(); - } - } - } - } - } -}; - - -/** @override */ -goog.editor.plugins.EnterHandler.prototype.handleKeyPress = function(e) { - // If a dialog doesn't have selectable field, Gecko grabs the event and - // performs actions in editor window. This solves that problem and allows - // the event to be passed on to proper handlers. - if (goog.userAgent.GECKO && this.getFieldObject().inModalMode()) { - return false; - } - - // Firefox will allow the first node in an iframe to be deleted - // on a backspace. Disallow it if the node is empty. - if (e.keyCode == goog.events.KeyCodes.BACKSPACE) { - this.handleBackspaceInternal(e, this.getFieldObject().getRange()); - - } else if (e.keyCode == goog.events.KeyCodes.ENTER) { - if (goog.userAgent.GECKO) { - if (!e.shiftKey) { - // Behave similarly to IE's content editable return carriage: - // If the shift key is down or specified by the application, insert a - // BR, otherwise split paragraphs - this.handleEnterGecko_(e); - } - } else { - // In Gecko-based browsers, this is handled in the handleEnterGecko_ - // method. - this.getFieldObject().dispatchBeforeChange(); - var cursorPosition = this.deleteCursorSelection_(); - - var split = !!this.getFieldObject().execCommand( - goog.editor.plugins.Blockquote.SPLIT_COMMAND, cursorPosition); - if (split) { - // TODO(user): I think we probably don't need to stopPropagation here - e.preventDefault(); - e.stopPropagation(); - } - - this.releasePositionObject_(cursorPosition); - - if (goog.userAgent.WEBKIT) { - this.handleEnterWebkitInternal(e); - } - - this.processParagraphTagsInternal(e, split); - this.getFieldObject().dispatchChange(); - } - - } else if (goog.userAgent.GECKO && e.keyCode == goog.events.KeyCodes.DELETE) { - this.handleDeleteGecko(e); - } - - return false; -}; - - -/** @override */ -goog.editor.plugins.EnterHandler.prototype.handleKeyUp = function(e) { - // If a dialog doesn't have selectable field, Gecko grabs the event and - // performs actions in editor window. This solves that problem and allows - // the event to be passed on to proper handlers. - if (goog.userAgent.GECKO && this.getFieldObject().inModalMode()) { - return false; - } - this.handleKeyUpInternal(e); - return false; -}; - - -/** - * Internal handler for keyup events. - * @param {goog.events.Event} e The key event. - * @protected - */ -goog.editor.plugins.EnterHandler.prototype.handleKeyUpInternal = function(e) { - if ((goog.userAgent.IE || goog.userAgent.OPERA) && - e.keyCode == goog.events.KeyCodes.ENTER) { - this.ensureBlockIeOpera(goog.dom.TagName.DIV, true); - } -}; - - -/** - * Handles an enter keypress event on fields in Gecko. - * @param {goog.events.BrowserEvent} e The key event. - * @private - */ -goog.editor.plugins.EnterHandler.prototype.handleEnterGecko_ = function(e) { - // Retrieve whether the selection is collapsed before we delete it. - var range = this.getFieldObject().getRange(); - var wasCollapsed = !range || range.isCollapsed(); - var cursorPosition = this.deleteCursorSelection_(); - - var handled = this.getFieldObject().execCommand( - goog.editor.plugins.Blockquote.SPLIT_COMMAND, cursorPosition); - if (handled) { - // TODO(user): I think we probably don't need to stopPropagation here - e.preventDefault(); - e.stopPropagation(); - } - - this.releasePositionObject_(cursorPosition); - if (!handled) { - this.handleEnterAtCursorGeckoInternal(e, wasCollapsed, range); - } -}; - - -/** - * Handle an enter key press in WebKit. - * @param {goog.events.BrowserEvent} e The key press event. - * @protected - */ -goog.editor.plugins.EnterHandler.prototype.handleEnterWebkitInternal = - goog.nullFunction; - - -/** - * Handle an enter key press on collapsed selection. handleEnterGecko_ ensures - * the selection is collapsed by deleting its contents if it is not. The - * default implementation does nothing. - * @param {goog.events.BrowserEvent} e The key press event. - * @param {boolean} wasCollapsed Whether the selection was collapsed before - * the key press. If it was not, code before this function has already - * cleared the contents of the selection. - * @param {goog.dom.AbstractRange} range Object representing the selection. - * @protected - */ -goog.editor.plugins.EnterHandler.prototype.handleEnterAtCursorGeckoInternal = - goog.nullFunction; - - -/** - * Names of all the nodes that we don't want to turn into block nodes in IE when - * the user hits enter. - * @type {Object} - * @private - */ -goog.editor.plugins.EnterHandler.DO_NOT_ENSURE_BLOCK_NODES_ = - goog.object.createSet( - goog.dom.TagName.LI, goog.dom.TagName.DIV, goog.dom.TagName.H1, - goog.dom.TagName.H2, goog.dom.TagName.H3, goog.dom.TagName.H4, - goog.dom.TagName.H5, goog.dom.TagName.H6); - - -/** - * Whether this is a node that contains a single BR tag and non-nbsp - * whitespace. - * @param {Node} node Node to check. - * @return {boolean} Whether this is an element that only contains a BR. - * @protected - */ -goog.editor.plugins.EnterHandler.isBrElem = function(node) { - return goog.editor.node.isEmpty(node) && - node.getElementsByTagName(goog.dom.TagName.BR).length == 1; -}; - - -/** - * Ensures all text in IE and Opera to be in the given tag in order to control - * Enter spacing. Call this when Enter is pressed if desired. - * - * We want to make sure the user is always inside of a block (or other nodes - * listed in goog.editor.plugins.EnterHandler.IGNORE_ENSURE_BLOCK_NODES_). We - * listen to keypress to force nodes that the user is leaving to turn into - * blocks, but we also need to listen to keyup to force nodes that the user is - * entering to turn into blocks. - * Example: html is: "<h2>foo[cursor]</h2>", and the user hits enter. We - * don't want to format the h2, but we do want to format the P that is - * created on enter. The P node is not available until keyup. - * @param {goog.dom.TagName} tag The tag name to convert to. - * @param {boolean=} opt_keyUp Whether the function is being called on key up. - * When called on key up, the cursor is in the newly created node, so the - * semantics for when to change it to a block are different. Specifically, - * if the resulting node contains only a BR, it is converted to <tag>. - * @protected - */ -goog.editor.plugins.EnterHandler.prototype.ensureBlockIeOpera = function(tag, - opt_keyUp) { - var range = this.getFieldObject().getRange(); - var container = range.getContainer(); - var field = this.getFieldObject().getElement(); - - var paragraph; - while (container && container != field) { - // We don't need to ensure a block if we are already in the same block, or - // in another block level node that we don't want to change the format of - // (unless we're handling keyUp and that block node just contains a BR). - var nodeName = container.nodeName; - // Due to @bug 2455389, the call to isBrElem needs to be inlined in the if - // instead of done before and saved in a variable, so that it can be - // short-circuited and avoid a weird IE edge case. - if (nodeName == tag || - (goog.editor.plugins.EnterHandler. - DO_NOT_ENSURE_BLOCK_NODES_[nodeName] && !(opt_keyUp && - goog.editor.plugins.EnterHandler.isBrElem(container)))) { - // Opera can create a <p> inside of a <div> in some situations, - // such as when breaking out of a list that is contained in a <div>. - if (goog.userAgent.OPERA && paragraph) { - if (nodeName == tag && - paragraph == container.lastChild && - goog.editor.node.isEmpty(paragraph)) { - goog.dom.insertSiblingAfter(paragraph, container); - goog.dom.Range.createFromNodeContents(paragraph).select(); - } - break; - } - return; - } - if (goog.userAgent.OPERA && opt_keyUp && nodeName == goog.dom.TagName.P && - nodeName != tag) { - paragraph = container; - } - - container = container.parentNode; - } - - - if (goog.userAgent.IE && !goog.userAgent.isVersion(9)) { - // IE (before IE9) has a bug where if the cursor is directly before a block - // node (e.g., the content is "foo[cursor]<blockquote>bar</blockquote>"), - // the FormatBlock command actually formats the "bar" instead of the "foo". - // This is just wrong. To work-around this, we want to move the - // selection back one character, and then restore it to its prior position. - // NOTE: We use the following "range math" to detect this situation because - // using Closure ranges here triggers a bug in IE that causes a crash. - // parent2 != parent3 ensures moving the cursor forward one character - // crosses at least 1 element boundary, and therefore tests if the cursor is - // at such a boundary. The second check, parent3 != range.parentElement() - // weeds out some cases where the elements are siblings instead of cousins. - var needsHelp = false; - range = range.getBrowserRangeObject(); - var range2 = range.duplicate(); - range2.moveEnd('character', 1); - // In whitebox mode, when the cursor is at the end of the field, trying to - // move the end of the range will do nothing, and hence the range's text - // will be empty. In this case, the cursor clearly isn't sitting just - // before a block node, since it isn't before anything. - if (range2.text.length) { - var parent2 = range2.parentElement(); - - var range3 = range2.duplicate(); - range3.collapse(false); - var parent3 = range3.parentElement(); - - if ((needsHelp = parent2 != parent3 && - parent3 != range.parentElement())) { - range.move('character', -1); - range.select(); - } - } - } - - this.getFieldObject().getEditableDomHelper().getDocument().execCommand( - 'FormatBlock', false, '<' + tag + '>'); - - if (needsHelp) { - range.move('character', 1); - range.select(); - } -}; - - -/** - * Deletes the content at the current cursor position. - * @return {Node|Object} Something representing the current cursor position. - * See deleteCursorSelectionIE_ and deleteCursorSelectionW3C_ for details. - * Should be passed to releasePositionObject_ when no longer in use. - * @private - */ -goog.editor.plugins.EnterHandler.prototype.deleteCursorSelection_ = function() { - return goog.editor.BrowserFeature.HAS_W3C_RANGES ? - this.deleteCursorSelectionW3C_() : this.deleteCursorSelectionIE_(); -}; - - -/** - * Releases the object returned by deleteCursorSelection_. - * @param {Node|Object} position The object returned by deleteCursorSelection_. - * @private - */ -goog.editor.plugins.EnterHandler.prototype.releasePositionObject_ = - function(position) { - if (!goog.editor.BrowserFeature.HAS_W3C_RANGES) { - (/** @type {Node} */ (position)).removeNode(true); - } -}; - - -/** - * Delete the selection at the current cursor position, then returns a temporary - * node at the current position. - * @return {Node} A temporary node marking the current cursor position. This - * node should eventually be removed from the DOM. - * @private - */ -goog.editor.plugins.EnterHandler.prototype.deleteCursorSelectionIE_ = - function() { - var doc = this.getFieldDomHelper().getDocument(); - var range = doc.selection.createRange(); - - var id = goog.string.createUniqueString(); - range.pasteHTML('<span id="' + id + '"></span>'); - var splitNode = doc.getElementById(id); - splitNode.id = ''; - return splitNode; -}; - - -/** - * Delete the selection at the current cursor position, then returns the node - * at the current position. - * @return {goog.editor.range.Point} The current cursor position. Note that - * unlike simulateEnterIE_, this should not be removed from the DOM. - * @private - */ -goog.editor.plugins.EnterHandler.prototype.deleteCursorSelectionW3C_ = - function() { - var range = this.getFieldObject().getRange(); - - // Delete the current selection if it's is non-collapsed. - // Although this is redundant in FF, it's necessary for Safari - if (!range.isCollapsed()) { - var shouldDelete = true; - // Opera selects the <br> in an empty block if there is no text node - // preceding it. To preserve inline formatting when pressing [enter] inside - // an empty block, don't delete the selection if it only selects a <br> at - // the end of the block. - // TODO(user): Move this into goog.dom.Range. It should detect this state - // when creating a range from the window selection and fix it in the created - // range. - if (goog.userAgent.OPERA) { - var startNode = range.getStartNode(); - var startOffset = range.getStartOffset(); - if (startNode == range.getEndNode() && - // This weeds out cases where startNode is a text node. - startNode.lastChild && - startNode.lastChild.tagName == goog.dom.TagName.BR && - // If this check is true, then endOffset is implied to be - // startOffset + 1, because the selection is not collapsed and - // it starts and ends within the same element. - startOffset == startNode.childNodes.length - 1) { - shouldDelete = false; - } - } - if (shouldDelete) { - goog.editor.plugins.EnterHandler.deleteW3cRange_(range); - } - } - - return goog.editor.range.getDeepEndPoint(range, true); -}; - - -/** - * Deletes the contents of the selection from the DOM. - * @param {goog.dom.AbstractRange} range The range to remove contents from. - * @return {goog.dom.AbstractRange} The resulting range. Used for testing. - * @private - */ -goog.editor.plugins.EnterHandler.deleteW3cRange_ = function(range) { - if (range && !range.isCollapsed()) { - var reselect = true; - var baseNode = range.getContainerElement(); - var nodeOffset = new goog.dom.NodeOffset(range.getStartNode(), baseNode); - var rangeOffset = range.getStartOffset(); - - // Whether the selection crosses no container boundaries. - var isInOneContainer = - goog.editor.plugins.EnterHandler.isInOneContainerW3c_(range); - - // Whether the selection ends in a container it doesn't fully select. - var isPartialEnd = !isInOneContainer && - goog.editor.plugins.EnterHandler.isPartialEndW3c_(range); - - // Remove The range contents, and ensure the correct content stays selected. - range.removeContents(); - var node = nodeOffset.findTargetNode(baseNode); - if (node) { - range = goog.dom.Range.createCaret(node, rangeOffset); - } else { - // This occurs when the node that would have been referenced has now been - // deleted and there are no other nodes in the baseNode. Thus need to - // set the caret to the end of the base node. - range = - goog.dom.Range.createCaret(baseNode, baseNode.childNodes.length); - reselect = false; - } - range.select(); - - // If we just deleted everything from the container, add an nbsp - // to the container, and leave the cursor inside of it - if (isInOneContainer) { - var container = goog.editor.style.getContainer(range.getStartNode()); - if (goog.editor.node.isEmpty(container, true)) { - var html = ' '; - if (goog.userAgent.OPERA && - container.tagName == goog.dom.TagName.LI) { - // Don't break Opera's native break-out-of-lists behavior. - html = '<br>'; - } - container.innerHTML = html; - goog.editor.range.selectNodeStart(container.firstChild); - reselect = false; - } - } - - if (isPartialEnd) { - /* - This code handles the following, where | is the cursor: - <div>a|b</div><div>c|d</div> - After removeContents, the remaining HTML is - <div>a</div><div>d</div> - which means the line break between the two divs remains. This block - moves children of the second div in to the first div to get the correct - result: - <div>ad</div> - - TODO(robbyw): Should we wrap the second div's contents in a span if they - have inline style? - */ - var rangeStart = goog.editor.style.getContainer(range.getStartNode()); - var redundantContainer = goog.editor.node.getNextSibling(rangeStart); - if (rangeStart && redundantContainer) { - goog.dom.append(rangeStart, redundantContainer.childNodes); - goog.dom.removeNode(redundantContainer); - } - } - - if (reselect) { - // The contents of the original range are gone, so restore the cursor - // position at the start of where the range once was. - range = goog.dom.Range.createCaret(nodeOffset.findTargetNode(baseNode), - rangeOffset); - range.select(); - } - } - - return range; -}; - - -/** - * Checks whether the whole range is in a single block-level element. - * @param {goog.dom.AbstractRange} range The range to check. - * @return {boolean} Whether the whole range is in a single block-level element. - * @private - */ -goog.editor.plugins.EnterHandler.isInOneContainerW3c_ = function(range) { - // Find the block element containing the start of the selection. - var startContainer = range.getStartNode(); - if (goog.editor.style.isContainer(startContainer)) { - startContainer = startContainer.childNodes[range.getStartOffset()] || - startContainer; - } - startContainer = goog.editor.style.getContainer(startContainer); - - // Find the block element containing the end of the selection. - var endContainer = range.getEndNode(); - if (goog.editor.style.isContainer(endContainer)) { - endContainer = endContainer.childNodes[range.getEndOffset()] || - endContainer; - } - endContainer = goog.editor.style.getContainer(endContainer); - - // Compare the two. - return startContainer == endContainer; -}; - - -/** - * Checks whether the end of the range is not at the end of a block-level - * element. - * @param {goog.dom.AbstractRange} range The range to check. - * @return {boolean} Whether the end of the range is not at the end of a - * block-level element. - * @private - */ -goog.editor.plugins.EnterHandler.isPartialEndW3c_ = function(range) { - var endContainer = range.getEndNode(); - var endOffset = range.getEndOffset(); - var node = endContainer; - if (goog.editor.style.isContainer(node)) { - var child = node.childNodes[endOffset]; - // Child is null when end offset is >= length, which indicates the entire - // container is selected. Otherwise, we also know the entire container - // is selected if the selection ends at a new container. - if (!child || - child.nodeType == goog.dom.NodeType.ELEMENT && - goog.editor.style.isContainer(child)) { - return false; - } - } - - var container = goog.editor.style.getContainer(node); - while (container != node) { - if (goog.editor.node.getNextSibling(node)) { - return true; - } - node = node.parentNode; - } - - return endOffset != goog.editor.node.getLength(endContainer); -}; |