| // Copyright 2005 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 Object to store the offset from one node to another in a way |
| * that works on any similar DOM structure regardless of whether it is the same |
| * actual nodes. |
| * |
| * @author robbyw@google.com (Robby Walker) |
| */ |
| |
| goog.provide('goog.dom.NodeOffset'); |
| |
| goog.require('goog.Disposable'); |
| goog.require('goog.dom.TagName'); |
| |
| |
| |
| /** |
| * Object to store the offset from one node to another in a way that works on |
| * any similar DOM structure regardless of whether it is the same actual nodes. |
| * @param {Node} node The node to get the offset for. |
| * @param {Node} baseNode The node to calculate the offset from. |
| * @extends {goog.Disposable} |
| * @constructor |
| * @final |
| */ |
| goog.dom.NodeOffset = function(node, baseNode) { |
| goog.Disposable.call(this); |
| |
| /** |
| * A stack of childNode offsets. |
| * @type {Array<number>} |
| * @private |
| */ |
| this.offsetStack_ = []; |
| |
| /** |
| * A stack of childNode names. |
| * @type {Array<string>} |
| * @private |
| */ |
| this.nameStack_ = []; |
| |
| while (node && node.nodeName != goog.dom.TagName.BODY && node != baseNode) { |
| // Compute the sibling offset. |
| var siblingOffset = 0; |
| var sib = node.previousSibling; |
| while (sib) { |
| sib = sib.previousSibling; |
| ++siblingOffset; |
| } |
| this.offsetStack_.unshift(siblingOffset); |
| this.nameStack_.unshift(node.nodeName); |
| |
| node = node.parentNode; |
| } |
| }; |
| goog.inherits(goog.dom.NodeOffset, goog.Disposable); |
| |
| |
| /** |
| * @return {string} A string representation of this object. |
| * @override |
| */ |
| goog.dom.NodeOffset.prototype.toString = function() { |
| var strs = []; |
| var name; |
| for (var i = 0; name = this.nameStack_[i]; i++) { |
| strs.push(this.offsetStack_[i] + ',' + name); |
| } |
| return strs.join('\n'); |
| }; |
| |
| |
| /** |
| * Walk the dom and find the node relative to baseNode. Returns null on |
| * failure. |
| * @param {Node} baseNode The node to start walking from. Should be equivalent |
| * to the node passed in to the constructor, in that it should have the |
| * same contents. |
| * @return {Node} The node relative to baseNode, or null on failure. |
| */ |
| goog.dom.NodeOffset.prototype.findTargetNode = function(baseNode) { |
| var name; |
| var curNode = baseNode; |
| for (var i = 0; name = this.nameStack_[i]; ++i) { |
| curNode = curNode.childNodes[this.offsetStack_[i]]; |
| |
| // Sanity check and make sure the element names match. |
| if (!curNode || curNode.nodeName != name) { |
| return null; |
| } |
| } |
| return curNode; |
| }; |
| |
| |
| /** @override */ |
| goog.dom.NodeOffset.prototype.disposeInternal = function() { |
| delete this.offsetStack_; |
| delete this.nameStack_; |
| }; |