Path: blob/trunk/third_party/closure/goog/dom/savedcaretrange.js
2868 views
// Copyright 2008 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview An API for saving and restoring ranges as HTML carets.16*17* @author [email protected] (Nick Santos)18*/192021goog.provide('goog.dom.SavedCaretRange');2223goog.require('goog.array');24goog.require('goog.dom');25goog.require('goog.dom.SavedRange');26goog.require('goog.dom.TagName');27goog.require('goog.string');28293031/**32* A struct for holding context about saved selections.33* This can be used to preserve the selection and restore while the DOM is34* manipulated, or through an asynchronous call. Use goog.dom.Range factory35* methods to obtain an {@see goog.dom.AbstractRange} instance, and use36* {@see goog.dom.AbstractRange#saveUsingCarets} to obtain a SavedCaretRange.37* For editor ranges under content-editable elements or design-mode iframes,38* prefer using {@see goog.editor.range.saveUsingNormalizedCarets}.39* @param {goog.dom.AbstractRange} range The range being saved.40* @constructor41* @extends {goog.dom.SavedRange}42*/43goog.dom.SavedCaretRange = function(range) {44goog.dom.SavedRange.call(this);4546/**47* The DOM id of the caret at the start of the range.48* @type {string}49* @private50*/51this.startCaretId_ = goog.string.createUniqueString();5253/**54* The DOM id of the caret at the end of the range.55* @type {string}56* @private57*/58this.endCaretId_ = goog.string.createUniqueString();5960/**61* Whether the range is reversed (anchor at the end).62* @private {boolean}63*/64this.reversed_ = range.isReversed();6566/**67* A DOM helper for storing the current document context.68* @type {goog.dom.DomHelper}69* @private70*/71this.dom_ = goog.dom.getDomHelper(range.getDocument());7273range.surroundWithNodes(this.createCaret_(true), this.createCaret_(false));74};75goog.inherits(goog.dom.SavedCaretRange, goog.dom.SavedRange);767778/**79* Gets the range that this SavedCaretRage represents, without selecting it80* or removing the carets from the DOM.81* @return {goog.dom.AbstractRange?} An abstract range.82* @suppress {missingRequire} circular dependency83*/84goog.dom.SavedCaretRange.prototype.toAbstractRange = function() {85var range = null;86var startCaret = this.getCaret(true);87var endCaret = this.getCaret(false);88if (startCaret && endCaret) {89range = goog.dom.Range.createFromNodes(startCaret, 0, endCaret, 0);90}91return range;92};939495/**96* Gets carets.97* @param {boolean} start If true, returns the start caret. Otherwise, get the98* end caret.99* @return {Element} The start or end caret in the given document.100*/101goog.dom.SavedCaretRange.prototype.getCaret = function(start) {102return this.dom_.getElement(start ? this.startCaretId_ : this.endCaretId_);103};104105106/**107* Removes the carets from the current restoration document.108* @param {goog.dom.AbstractRange=} opt_range A range whose offsets have already109* been adjusted for caret removal; it will be adjusted if it is also110* affected by post-removal operations, such as text node normalization.111* @return {goog.dom.AbstractRange|undefined} The adjusted range, if opt_range112* was provided.113*/114goog.dom.SavedCaretRange.prototype.removeCarets = function(opt_range) {115goog.dom.removeNode(this.getCaret(true));116goog.dom.removeNode(this.getCaret(false));117return opt_range;118};119120121/**122* Sets the document where the range will be restored.123* @param {!Document} doc An HTML document.124*/125goog.dom.SavedCaretRange.prototype.setRestorationDocument = function(doc) {126this.dom_.setDocument(doc);127};128129130/**131* Reconstruct the selection from the given saved range. Removes carets after132* restoring the selection. If restore does not dispose this saved range, it may133* only be restored a second time if innerHTML or some other mechanism is used134* to restore the carets to the dom.135* @return {goog.dom.AbstractRange?} Restored selection.136* @override137* @protected138*/139goog.dom.SavedCaretRange.prototype.restoreInternal = function() {140var range = null;141var anchorCaret = this.getCaret(!this.reversed_);142var focusCaret = this.getCaret(this.reversed_);143if (anchorCaret && focusCaret) {144var anchorNode = anchorCaret.parentNode;145var anchorOffset = goog.array.indexOf(anchorNode.childNodes, anchorCaret);146var focusNode = focusCaret.parentNode;147var focusOffset = goog.array.indexOf(focusNode.childNodes, focusCaret);148if (focusNode == anchorNode) {149// Compensate for the start caret being removed.150if (this.reversed_) {151anchorOffset--;152} else {153focusOffset--;154}155}156/** @suppress {missingRequire} circular dependency */157range = goog.dom.Range.createFromNodes(158anchorNode, anchorOffset, focusNode, focusOffset);159range = this.removeCarets(range);160range.select();161} else {162// If only one caret was found, remove it.163this.removeCarets();164}165return range;166};167168169/**170* Dispose the saved range and remove the carets from the DOM.171* @override172* @protected173*/174goog.dom.SavedCaretRange.prototype.disposeInternal = function() {175this.removeCarets();176this.dom_ = null;177};178179180/**181* Creates a caret element.182* @param {boolean} start If true, creates the start caret. Otherwise,183* creates the end caret.184* @return {!Element} The new caret element.185* @private186*/187goog.dom.SavedCaretRange.prototype.createCaret_ = function(start) {188return this.dom_.createDom(189goog.dom.TagName.SPAN,190{'id': start ? this.startCaretId_ : this.endCaretId_});191};192193194/**195* A regex that will match all saved range carets in a string.196* @type {RegExp}197*/198goog.dom.SavedCaretRange.CARET_REGEX = /<span\s+id="?goog_\d+"?><\/span>/ig;199200201/**202* Returns whether two strings of html are equal, ignoring any saved carets.203* Thus two strings of html whose only difference is the id of their saved204* carets will be considered equal, since they represent html with the205* same selection.206* @param {string} str1 The first string.207* @param {string} str2 The second string.208* @return {boolean} Whether two strings of html are equal, ignoring any209* saved carets.210*/211goog.dom.SavedCaretRange.htmlEqual = function(str1, str2) {212return str1 == str2 ||213str1.replace(goog.dom.SavedCaretRange.CARET_REGEX, '') ==214str2.replace(goog.dom.SavedCaretRange.CARET_REGEX, '');215};216217218