Path: blob/trunk/third_party/closure/goog/dom/dom.js
2868 views
// Copyright 2006 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 Utilities for manipulating the browser's Document Object Model16* Inspiration taken *heavily* from mochikit (http://mochikit.com/).17*18* You can use {@link goog.dom.DomHelper} to create new dom helpers that refer19* to a different document object. This is useful if you are working with20* frames or multiple windows.21*22* @author [email protected] (Erik Arvidsson)23*/242526// TODO(arv): Rename/refactor getTextContent and getRawTextContent. The problem27// is that getTextContent should mimic the DOM3 textContent. We should add a28// getInnerText (or getText) which tries to return the visible text, innerText.293031goog.provide('goog.dom');32goog.provide('goog.dom.Appendable');33goog.provide('goog.dom.DomHelper');3435goog.require('goog.array');36goog.require('goog.asserts');37goog.require('goog.dom.BrowserFeature');38goog.require('goog.dom.NodeType');39goog.require('goog.dom.TagName');40goog.require('goog.dom.safe');41goog.require('goog.html.SafeHtml');42goog.require('goog.html.uncheckedconversions');43goog.require('goog.math.Coordinate');44goog.require('goog.math.Size');45goog.require('goog.object');46goog.require('goog.string');47goog.require('goog.string.Unicode');48goog.require('goog.userAgent');495051/**52* @define {boolean} Whether we know at compile time that the browser is in53* quirks mode.54*/55goog.define('goog.dom.ASSUME_QUIRKS_MODE', false);565758/**59* @define {boolean} Whether we know at compile time that the browser is in60* standards compliance mode.61*/62goog.define('goog.dom.ASSUME_STANDARDS_MODE', false);636465/**66* Whether we know the compatibility mode at compile time.67* @type {boolean}68* @private69*/70goog.dom.COMPAT_MODE_KNOWN_ =71goog.dom.ASSUME_QUIRKS_MODE || goog.dom.ASSUME_STANDARDS_MODE;727374/**75* Gets the DomHelper object for the document where the element resides.76* @param {(Node|Window)=} opt_element If present, gets the DomHelper for this77* element.78* @return {!goog.dom.DomHelper} The DomHelper.79*/80goog.dom.getDomHelper = function(opt_element) {81return opt_element ?82new goog.dom.DomHelper(goog.dom.getOwnerDocument(opt_element)) :83(goog.dom.defaultDomHelper_ ||84(goog.dom.defaultDomHelper_ = new goog.dom.DomHelper()));85};868788/**89* Cached default DOM helper.90* @type {!goog.dom.DomHelper|undefined}91* @private92*/93goog.dom.defaultDomHelper_;949596/**97* Gets the document object being used by the dom library.98* @return {!Document} Document object.99*/100goog.dom.getDocument = function() {101return document;102};103104105/**106* Gets an element from the current document by element id.107*108* If an Element is passed in, it is returned.109*110* @param {string|Element} element Element ID or a DOM node.111* @return {Element} The element with the given ID, or the node passed in.112*/113goog.dom.getElement = function(element) {114return goog.dom.getElementHelper_(document, element);115};116117118/**119* Gets an element by id from the given document (if present).120* If an element is given, it is returned.121* @param {!Document} doc122* @param {string|Element} element Element ID or a DOM node.123* @return {Element} The resulting element.124* @private125*/126goog.dom.getElementHelper_ = function(doc, element) {127return goog.isString(element) ? doc.getElementById(element) : element;128};129130131/**132* Gets an element by id, asserting that the element is found.133*134* This is used when an element is expected to exist, and should fail with135* an assertion error if it does not (if assertions are enabled).136*137* @param {string} id Element ID.138* @return {!Element} The element with the given ID, if it exists.139*/140goog.dom.getRequiredElement = function(id) {141return goog.dom.getRequiredElementHelper_(document, id);142};143144145/**146* Helper function for getRequiredElementHelper functions, both static and147* on DomHelper. Asserts the element with the given id exists.148* @param {!Document} doc149* @param {string} id150* @return {!Element} The element with the given ID, if it exists.151* @private152*/153goog.dom.getRequiredElementHelper_ = function(doc, id) {154// To prevent users passing in Elements as is permitted in getElement().155goog.asserts.assertString(id);156var element = goog.dom.getElementHelper_(doc, id);157element =158goog.asserts.assertElement(element, 'No element found with id: ' + id);159return element;160};161162163/**164* Alias for getElement.165* @param {string|Element} element Element ID or a DOM node.166* @return {Element} The element with the given ID, or the node passed in.167* @deprecated Use {@link goog.dom.getElement} instead.168*/169goog.dom.$ = goog.dom.getElement;170171172/**173* Gets elements by tag name.174* @param {!goog.dom.TagName<T>} tagName175* @param {(!Document|!Element)=} opt_parent Parent element or document where to176* look for elements. Defaults to document.177* @return {!NodeList<R>} List of elements. The members of the list are178* {!Element} if tagName is not a member of goog.dom.TagName or more179* specific types if it is (e.g. {!HTMLAnchorElement} for180* goog.dom.TagName.A).181* @template T182* @template R := cond(isUnknown(T), 'Element', T) =:183*/184goog.dom.getElementsByTagName = function(tagName, opt_parent) {185var parent = opt_parent || document;186return parent.getElementsByTagName(String(tagName));187};188189190/**191* Looks up elements by both tag and class name, using browser native functions192* ({@code querySelectorAll}, {@code getElementsByTagName} or193* {@code getElementsByClassName}) where possible. This function194* is a useful, if limited, way of collecting a list of DOM elements195* with certain characteristics. {@code goog.dom.query} offers a196* more powerful and general solution which allows matching on CSS3197* selector expressions, but at increased cost in code size. If all you198* need is particular tags belonging to a single class, this function199* is fast and sleek.200*201* Note that tag names are case sensitive in the SVG namespace, and this202* function converts opt_tag to uppercase for comparisons. For queries in the203* SVG namespace you should use querySelector or querySelectorAll instead.204* https://bugzilla.mozilla.org/show_bug.cgi?id=963870205* https://bugs.webkit.org/show_bug.cgi?id=83438206*207* @see {goog.dom.query}208*209* @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.210* @param {?string=} opt_class Optional class name.211* @param {(Document|Element)=} opt_el Optional element to look in.212* @return {!IArrayLike<R>} Array-like list of elements (only a length property213* and numerical indices are guaranteed to exist). The members of the array214* are {!Element} if opt_tag is not a member of goog.dom.TagName or more215* specific types if it is (e.g. {!HTMLAnchorElement} for216* goog.dom.TagName.A).217* @template T218* @template R := cond(isUnknown(T), 'Element', T) =:219*/220goog.dom.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) {221return goog.dom.getElementsByTagNameAndClass_(222document, opt_tag, opt_class, opt_el);223};224225226/**227* Returns a static, array-like list of the elements with the provided228* className.229* @see {goog.dom.query}230* @param {string} className the name of the class to look for.231* @param {(Document|Element)=} opt_el Optional element to look in.232* @return {!IArrayLike<!Element>} The items found with the class name provided.233*/234goog.dom.getElementsByClass = function(className, opt_el) {235var parent = opt_el || document;236if (goog.dom.canUseQuerySelector_(parent)) {237return parent.querySelectorAll('.' + className);238}239return goog.dom.getElementsByTagNameAndClass_(240document, '*', className, opt_el);241};242243244/**245* Returns the first element with the provided className.246* @see {goog.dom.query}247* @param {string} className the name of the class to look for.248* @param {Element|Document=} opt_el Optional element to look in.249* @return {Element} The first item with the class name provided.250*/251goog.dom.getElementByClass = function(className, opt_el) {252var parent = opt_el || document;253var retVal = null;254if (parent.getElementsByClassName) {255retVal = parent.getElementsByClassName(className)[0];256} else if (goog.dom.canUseQuerySelector_(parent)) {257retVal = parent.querySelector('.' + className);258} else {259retVal = goog.dom.getElementsByTagNameAndClass_(260document, '*', className, opt_el)[0];261}262return retVal || null;263};264265266/**267* Ensures an element with the given className exists, and then returns the268* first element with the provided className.269* @see {goog.dom.query}270* @param {string} className the name of the class to look for.271* @param {!Element|!Document=} opt_root Optional element or document to look272* in.273* @return {!Element} The first item with the class name provided.274* @throws {goog.asserts.AssertionError} Thrown if no element is found.275*/276goog.dom.getRequiredElementByClass = function(className, opt_root) {277var retValue = goog.dom.getElementByClass(className, opt_root);278return goog.asserts.assert(279retValue, 'No element found with className: ' + className);280};281282283/**284* Prefer the standardized (http://www.w3.org/TR/selectors-api/), native and285* fast W3C Selectors API.286* @param {!(Element|Document)} parent The parent document object.287* @return {boolean} whether or not we can use parent.querySelector* APIs.288* @private289*/290goog.dom.canUseQuerySelector_ = function(parent) {291return !!(parent.querySelectorAll && parent.querySelector);292};293294295/**296* Helper for {@code getElementsByTagNameAndClass}.297* @param {!Document} doc The document to get the elements in.298* @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.299* @param {?string=} opt_class Optional class name.300* @param {(Document|Element)=} opt_el Optional element to look in.301* @return {!IArrayLike<R>} Array-like list of elements (only a length property302* and numerical indices are guaranteed to exist). The members of the array303* are {!Element} if opt_tag is not a member of goog.dom.TagName or more304* specific types if it is (e.g. {!HTMLAnchorElement} for305* goog.dom.TagName.A).306* @template T307* @template R := cond(isUnknown(T), 'Element', T) =:308* @private309*/310goog.dom.getElementsByTagNameAndClass_ = function(311doc, opt_tag, opt_class, opt_el) {312var parent = opt_el || doc;313var tagName =314(opt_tag && opt_tag != '*') ? String(opt_tag).toUpperCase() : '';315316if (goog.dom.canUseQuerySelector_(parent) && (tagName || opt_class)) {317var query = tagName + (opt_class ? '.' + opt_class : '');318return parent.querySelectorAll(query);319}320321// Use the native getElementsByClassName if available, under the assumption322// that even when the tag name is specified, there will be fewer elements to323// filter through when going by class than by tag name324if (opt_class && parent.getElementsByClassName) {325var els = parent.getElementsByClassName(opt_class);326327if (tagName) {328var arrayLike = {};329var len = 0;330331// Filter for specific tags if requested.332for (var i = 0, el; el = els[i]; i++) {333if (tagName == el.nodeName) {334arrayLike[len++] = el;335}336}337arrayLike.length = len;338339return /** @type {!IArrayLike<!Element>} */ (arrayLike);340} else {341return els;342}343}344345var els = parent.getElementsByTagName(tagName || '*');346347if (opt_class) {348var arrayLike = {};349var len = 0;350for (var i = 0, el; el = els[i]; i++) {351var className = el.className;352// Check if className has a split function since SVG className does not.353if (typeof className.split == 'function' &&354goog.array.contains(className.split(/\s+/), opt_class)) {355arrayLike[len++] = el;356}357}358arrayLike.length = len;359return /** @type {!IArrayLike<!Element>} */ (arrayLike);360} else {361return els;362}363};364365366/**367* Alias for {@code getElementsByTagNameAndClass}.368* @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.369* @param {?string=} opt_class Optional class name.370* @param {Element=} opt_el Optional element to look in.371* @return {!IArrayLike<R>} Array-like list of elements (only a length property372* and numerical indices are guaranteed to exist). The members of the array373* are {!Element} if opt_tag is not a member of goog.dom.TagName or more374* specific types if it is (e.g. {!HTMLAnchorElement} for375* goog.dom.TagName.A).376* @template T377* @template R := cond(isUnknown(T), 'Element', T) =:378* @deprecated Use {@link goog.dom.getElementsByTagNameAndClass} instead.379*/380goog.dom.$$ = goog.dom.getElementsByTagNameAndClass;381382383/**384* Sets multiple properties, and sometimes attributes, on an element. Note that385* properties are simply object properties on the element instance, while386* attributes are visible in the DOM. Many properties map to attributes with the387* same names, some with different names, and there are also unmappable cases.388*389* This method sets properties by default (which means that custom attributes390* are not supported). These are the exeptions (some of which is legacy):391* - "style": Even though this is an attribute name, it is translated to a392* property, "style.cssText". Note that this property sanitizes and formats393* its value, unlike the attribute.394* - "class": This is an attribute name, it is translated to the "className"395* property.396* - "for": This is an attribute name, it is translated to the "htmlFor"397* property.398* - Entries in {@see goog.dom.DIRECT_ATTRIBUTE_MAP_} are set as attributes,399* this is probably due to browser quirks.400* - "aria-*", "data-*": Always set as attributes, they have no property401* counterparts.402*403* @param {Element} element DOM node to set properties on.404* @param {Object} properties Hash of property:value pairs.405* Property values can be strings or goog.string.TypedString values (such as406* goog.html.SafeUrl).407*/408goog.dom.setProperties = function(element, properties) {409goog.object.forEach(properties, function(val, key) {410if (val && val.implementsGoogStringTypedString) {411val = val.getTypedStringValue();412}413if (key == 'style') {414element.style.cssText = val;415} else if (key == 'class') {416element.className = val;417} else if (key == 'for') {418element.htmlFor = val;419} else if (goog.dom.DIRECT_ATTRIBUTE_MAP_.hasOwnProperty(key)) {420element.setAttribute(goog.dom.DIRECT_ATTRIBUTE_MAP_[key], val);421} else if (422goog.string.startsWith(key, 'aria-') ||423goog.string.startsWith(key, 'data-')) {424element.setAttribute(key, val);425} else {426element[key] = val;427}428});429};430431432/**433* Map of attributes that should be set using434* element.setAttribute(key, val) instead of element[key] = val. Used435* by goog.dom.setProperties.436*437* @private {!Object<string, string>}438* @const439*/440goog.dom.DIRECT_ATTRIBUTE_MAP_ = {441'cellpadding': 'cellPadding',442'cellspacing': 'cellSpacing',443'colspan': 'colSpan',444'frameborder': 'frameBorder',445'height': 'height',446'maxlength': 'maxLength',447'nonce': 'nonce',448'role': 'role',449'rowspan': 'rowSpan',450'type': 'type',451'usemap': 'useMap',452'valign': 'vAlign',453'width': 'width'454};455456457/**458* Gets the dimensions of the viewport.459*460* Gecko Standards mode:461* docEl.clientWidth Width of viewport excluding scrollbar.462* win.innerWidth Width of viewport including scrollbar.463* body.clientWidth Width of body element.464*465* docEl.clientHeight Height of viewport excluding scrollbar.466* win.innerHeight Height of viewport including scrollbar.467* body.clientHeight Height of document.468*469* Gecko Backwards compatible mode:470* docEl.clientWidth Width of viewport excluding scrollbar.471* win.innerWidth Width of viewport including scrollbar.472* body.clientWidth Width of viewport excluding scrollbar.473*474* docEl.clientHeight Height of document.475* win.innerHeight Height of viewport including scrollbar.476* body.clientHeight Height of viewport excluding scrollbar.477*478* IE6/7 Standards mode:479* docEl.clientWidth Width of viewport excluding scrollbar.480* win.innerWidth Undefined.481* body.clientWidth Width of body element.482*483* docEl.clientHeight Height of viewport excluding scrollbar.484* win.innerHeight Undefined.485* body.clientHeight Height of document element.486*487* IE5 + IE6/7 Backwards compatible mode:488* docEl.clientWidth 0.489* win.innerWidth Undefined.490* body.clientWidth Width of viewport excluding scrollbar.491*492* docEl.clientHeight 0.493* win.innerHeight Undefined.494* body.clientHeight Height of viewport excluding scrollbar.495*496* Opera 9 Standards and backwards compatible mode:497* docEl.clientWidth Width of viewport excluding scrollbar.498* win.innerWidth Width of viewport including scrollbar.499* body.clientWidth Width of viewport excluding scrollbar.500*501* docEl.clientHeight Height of document.502* win.innerHeight Height of viewport including scrollbar.503* body.clientHeight Height of viewport excluding scrollbar.504*505* WebKit:506* Safari 2507* docEl.clientHeight Same as scrollHeight.508* docEl.clientWidth Same as innerWidth.509* win.innerWidth Width of viewport excluding scrollbar.510* win.innerHeight Height of the viewport including scrollbar.511* frame.innerHeight Height of the viewport exluding scrollbar.512*513* Safari 3 (tested in 522)514*515* docEl.clientWidth Width of viewport excluding scrollbar.516* docEl.clientHeight Height of viewport excluding scrollbar in strict mode.517* body.clientHeight Height of viewport excluding scrollbar in quirks mode.518*519* @param {Window=} opt_window Optional window element to test.520* @return {!goog.math.Size} Object with values 'width' and 'height'.521*/522goog.dom.getViewportSize = function(opt_window) {523// TODO(arv): This should not take an argument524return goog.dom.getViewportSize_(opt_window || window);525};526527528/**529* Helper for {@code getViewportSize}.530* @param {Window} win The window to get the view port size for.531* @return {!goog.math.Size} Object with values 'width' and 'height'.532* @private533*/534goog.dom.getViewportSize_ = function(win) {535var doc = win.document;536var el = goog.dom.isCss1CompatMode_(doc) ? doc.documentElement : doc.body;537return new goog.math.Size(el.clientWidth, el.clientHeight);538};539540541/**542* Calculates the height of the document.543*544* @return {number} The height of the current document.545*/546goog.dom.getDocumentHeight = function() {547return goog.dom.getDocumentHeight_(window);548};549550/**551* Calculates the height of the document of the given window.552*553* @param {!Window} win The window whose document height to retrieve.554* @return {number} The height of the document of the given window.555*/556goog.dom.getDocumentHeightForWindow = function(win) {557return goog.dom.getDocumentHeight_(win);558};559560/**561* Calculates the height of the document of the given window.562*563* Function code copied from the opensocial gadget api:564* gadgets.window.adjustHeight(opt_height)565*566* @private567* @param {!Window} win The window whose document height to retrieve.568* @return {number} The height of the document of the given window.569*/570goog.dom.getDocumentHeight_ = function(win) {571// NOTE(eae): This method will return the window size rather than the document572// size in webkit quirks mode.573var doc = win.document;574var height = 0;575576if (doc) {577// Calculating inner content height is hard and different between578// browsers rendering in Strict vs. Quirks mode. We use a combination of579// three properties within document.body and document.documentElement:580// - scrollHeight581// - offsetHeight582// - clientHeight583// These values differ significantly between browsers and rendering modes.584// But there are patterns. It just takes a lot of time and persistence585// to figure out.586587var body = doc.body;588var docEl = /** @type {!HTMLElement} */ (doc.documentElement);589if (!(docEl && body)) {590return 0;591}592593// Get the height of the viewport594var vh = goog.dom.getViewportSize_(win).height;595if (goog.dom.isCss1CompatMode_(doc) && docEl.scrollHeight) {596// In Strict mode:597// The inner content height is contained in either:598// document.documentElement.scrollHeight599// document.documentElement.offsetHeight600// Based on studying the values output by different browsers,601// use the value that's NOT equal to the viewport height found above.602height =603docEl.scrollHeight != vh ? docEl.scrollHeight : docEl.offsetHeight;604} else {605// In Quirks mode:606// documentElement.clientHeight is equal to documentElement.offsetHeight607// except in IE. In most browsers, document.documentElement can be used608// to calculate the inner content height.609// However, in other browsers (e.g. IE), document.body must be used610// instead. How do we know which one to use?611// If document.documentElement.clientHeight does NOT equal612// document.documentElement.offsetHeight, then use document.body.613var sh = docEl.scrollHeight;614var oh = docEl.offsetHeight;615if (docEl.clientHeight != oh) {616sh = body.scrollHeight;617oh = body.offsetHeight;618}619620// Detect whether the inner content height is bigger or smaller621// than the bounding box (viewport). If bigger, take the larger622// value. If smaller, take the smaller value.623if (sh > vh) {624// Content is larger625height = sh > oh ? sh : oh;626} else {627// Content is smaller628height = sh < oh ? sh : oh;629}630}631}632633return height;634};635636637/**638* Gets the page scroll distance as a coordinate object.639*640* @param {Window=} opt_window Optional window element to test.641* @return {!goog.math.Coordinate} Object with values 'x' and 'y'.642* @deprecated Use {@link goog.dom.getDocumentScroll} instead.643*/644goog.dom.getPageScroll = function(opt_window) {645var win = opt_window || goog.global || window;646return goog.dom.getDomHelper(win.document).getDocumentScroll();647};648649650/**651* Gets the document scroll distance as a coordinate object.652*653* @return {!goog.math.Coordinate} Object with values 'x' and 'y'.654*/655goog.dom.getDocumentScroll = function() {656return goog.dom.getDocumentScroll_(document);657};658659660/**661* Helper for {@code getDocumentScroll}.662*663* @param {!Document} doc The document to get the scroll for.664* @return {!goog.math.Coordinate} Object with values 'x' and 'y'.665* @private666*/667goog.dom.getDocumentScroll_ = function(doc) {668var el = goog.dom.getDocumentScrollElement_(doc);669var win = goog.dom.getWindow_(doc);670if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('10') &&671win.pageYOffset != el.scrollTop) {672// The keyboard on IE10 touch devices shifts the page using the pageYOffset673// without modifying scrollTop. For this case, we want the body scroll674// offsets.675return new goog.math.Coordinate(el.scrollLeft, el.scrollTop);676}677return new goog.math.Coordinate(678win.pageXOffset || el.scrollLeft, win.pageYOffset || el.scrollTop);679};680681682/**683* Gets the document scroll element.684* @return {!Element} Scrolling element.685*/686goog.dom.getDocumentScrollElement = function() {687return goog.dom.getDocumentScrollElement_(document);688};689690691/**692* Helper for {@code getDocumentScrollElement}.693* @param {!Document} doc The document to get the scroll element for.694* @return {!Element} Scrolling element.695* @private696*/697goog.dom.getDocumentScrollElement_ = function(doc) {698// Old WebKit needs body.scrollLeft in both quirks mode and strict mode. We699// also default to the documentElement if the document does not have a body700// (e.g. a SVG document).701// Uses http://dev.w3.org/csswg/cssom-view/#dom-document-scrollingelement to702// avoid trying to guess about browser behavior from the UA string.703if (doc.scrollingElement) {704return doc.scrollingElement;705}706if (!goog.userAgent.WEBKIT && goog.dom.isCss1CompatMode_(doc)) {707return doc.documentElement;708}709return doc.body || doc.documentElement;710};711712713/**714* Gets the window object associated with the given document.715*716* @param {Document=} opt_doc Document object to get window for.717* @return {!Window} The window associated with the given document.718*/719goog.dom.getWindow = function(opt_doc) {720// TODO(arv): This should not take an argument.721return opt_doc ? goog.dom.getWindow_(opt_doc) : window;722};723724725/**726* Helper for {@code getWindow}.727*728* @param {!Document} doc Document object to get window for.729* @return {!Window} The window associated with the given document.730* @private731*/732goog.dom.getWindow_ = function(doc) {733return /** @type {!Window} */ (doc.parentWindow || doc.defaultView);734};735736737/**738* Returns a dom node with a set of attributes. This function accepts varargs739* for subsequent nodes to be added. Subsequent nodes will be added to the740* first node as childNodes.741*742* So:743* <code>createDom(goog.dom.TagName.DIV, null, createDom(goog.dom.TagName.P), createDom(goog.dom.TagName.P));</code>744* would return a div with two child paragraphs745*746* For passing properties, please see {@link goog.dom.setProperties} for more747* information.748*749* @param {string|!goog.dom.TagName<T>} tagName Tag to create.750* @param {(Object|Array<string>|string)=} opt_properties If object, then a map751* of name-value pairs for properties. If a string, then this is the752* className of the new element. If an array, the elements will be joined753* together as the className of the new element.754* @param {...(Object|string|Array|NodeList)} var_args Further DOM nodes or755* strings for text nodes. If one of the var_args is an array or NodeList,756* its elements will be added as childNodes instead.757* @return {R} Reference to a DOM node. The return type is {!Element} if tagName758* is a string or a more specific type if it is a member of759* goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).760* @template T761* @template R := cond(isUnknown(T), 'Element', T) =:762*/763goog.dom.createDom = function(tagName, opt_properties, var_args) {764return goog.dom.createDom_(document, arguments);765};766767768/**769* Helper for {@code createDom}.770* @param {!Document} doc The document to create the DOM in.771* @param {!Arguments} args Argument object passed from the callers. See772* {@code goog.dom.createDom} for details.773* @return {!Element} Reference to a DOM node.774* @private775*/776goog.dom.createDom_ = function(doc, args) {777var tagName = String(args[0]);778var attributes = args[1];779780// Internet Explorer is dumb:781// name: https://msdn.microsoft.com/en-us/library/ms534184(v=vs.85).aspx782// type: https://msdn.microsoft.com/en-us/library/ms534700(v=vs.85).aspx783// Also does not allow setting of 'type' attribute on 'input' or 'button'.784if (!goog.dom.BrowserFeature.CAN_ADD_NAME_OR_TYPE_ATTRIBUTES && attributes &&785(attributes.name || attributes.type)) {786var tagNameArr = ['<', tagName];787if (attributes.name) {788tagNameArr.push(' name="', goog.string.htmlEscape(attributes.name), '"');789}790if (attributes.type) {791tagNameArr.push(' type="', goog.string.htmlEscape(attributes.type), '"');792793// Clone attributes map to remove 'type' without mutating the input.794var clone = {};795goog.object.extend(clone, attributes);796797// JSCompiler can't see how goog.object.extend added this property,798// because it was essentially added by reflection.799// So it needs to be quoted.800delete clone['type'];801802attributes = clone;803}804tagNameArr.push('>');805tagName = tagNameArr.join('');806}807808var element = doc.createElement(tagName);809810if (attributes) {811if (goog.isString(attributes)) {812element.className = attributes;813} else if (goog.isArray(attributes)) {814element.className = attributes.join(' ');815} else {816goog.dom.setProperties(element, attributes);817}818}819820if (args.length > 2) {821goog.dom.append_(doc, element, args, 2);822}823824return element;825};826827828/**829* Appends a node with text or other nodes.830* @param {!Document} doc The document to create new nodes in.831* @param {!Node} parent The node to append nodes to.832* @param {!Arguments} args The values to add. See {@code goog.dom.append}.833* @param {number} startIndex The index of the array to start from.834* @private835*/836goog.dom.append_ = function(doc, parent, args, startIndex) {837function childHandler(child) {838// TODO(user): More coercion, ala MochiKit?839if (child) {840parent.appendChild(841goog.isString(child) ? doc.createTextNode(child) : child);842}843}844845for (var i = startIndex; i < args.length; i++) {846var arg = args[i];847// TODO(attila): Fix isArrayLike to return false for a text node.848if (goog.isArrayLike(arg) && !goog.dom.isNodeLike(arg)) {849// If the argument is a node list, not a real array, use a clone,850// because forEach can't be used to mutate a NodeList.851goog.array.forEach(852goog.dom.isNodeList(arg) ? goog.array.toArray(arg) : arg,853childHandler);854} else {855childHandler(arg);856}857}858};859860861/**862* Alias for {@code createDom}.863* @param {string|!goog.dom.TagName<T>} tagName Tag to create.864* @param {(string|Object)=} opt_properties If object, then a map of name-value865* pairs for properties. If a string, then this is the className of the new866* element.867* @param {...(Object|string|Array|NodeList)} var_args Further DOM nodes or868* strings for text nodes. If one of the var_args is an array, its869* children will be added as childNodes instead.870* @return {R} Reference to a DOM node. The return type is {!Element} if tagName871* is a string or a more specific type if it is a member of872* goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).873* @template T874* @template R := cond(isUnknown(T), 'Element', T) =:875* @deprecated Use {@link goog.dom.createDom} instead.876*/877goog.dom.$dom = goog.dom.createDom;878879880/**881* Creates a new element.882* @param {string|!goog.dom.TagName<T>} name Tag to create.883* @return {R} The new element. The return type is {!Element} if name is884* a string or a more specific type if it is a member of goog.dom.TagName885* (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).886* @template T887* @template R := cond(isUnknown(T), 'Element', T) =:888*/889goog.dom.createElement = function(name) {890return goog.dom.createElement_(document, name);891};892893894/**895* Creates a new element.896* @param {!Document} doc The document to create the element in.897* @param {string|!goog.dom.TagName<T>} name Tag to create.898* @return {R} The new element. The return type is {!Element} if name is899* a string or a more specific type if it is a member of goog.dom.TagName900* (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).901* @template T902* @template R := cond(isUnknown(T), 'Element', T) =:903* @private904*/905goog.dom.createElement_ = function(doc, name) {906return doc.createElement(String(name));907};908909910/**911* Creates a new text node.912* @param {number|string} content Content.913* @return {!Text} The new text node.914*/915goog.dom.createTextNode = function(content) {916return document.createTextNode(String(content));917};918919920/**921* Create a table.922* @param {number} rows The number of rows in the table. Must be >= 1.923* @param {number} columns The number of columns in the table. Must be >= 1.924* @param {boolean=} opt_fillWithNbsp If true, fills table entries with925* {@code goog.string.Unicode.NBSP} characters.926* @return {!Element} The created table.927*/928goog.dom.createTable = function(rows, columns, opt_fillWithNbsp) {929// TODO(mlourenco): Return HTMLTableElement, also in prototype function.930// Callers need to be updated to e.g. not assign numbers to table.cellSpacing.931return goog.dom.createTable_(document, rows, columns, !!opt_fillWithNbsp);932};933934935/**936* Create a table.937* @param {!Document} doc Document object to use to create the table.938* @param {number} rows The number of rows in the table. Must be >= 1.939* @param {number} columns The number of columns in the table. Must be >= 1.940* @param {boolean} fillWithNbsp If true, fills table entries with941* {@code goog.string.Unicode.NBSP} characters.942* @return {!HTMLTableElement} The created table.943* @private944*/945goog.dom.createTable_ = function(doc, rows, columns, fillWithNbsp) {946var table = goog.dom.createElement_(doc, goog.dom.TagName.TABLE);947var tbody =948table.appendChild(goog.dom.createElement_(doc, goog.dom.TagName.TBODY));949for (var i = 0; i < rows; i++) {950var tr = goog.dom.createElement_(doc, goog.dom.TagName.TR);951for (var j = 0; j < columns; j++) {952var td = goog.dom.createElement_(doc, goog.dom.TagName.TD);953// IE <= 9 will create a text node if we set text content to the empty954// string, so we avoid doing it unless necessary. This ensures that the955// same DOM tree is returned on all browsers.956if (fillWithNbsp) {957goog.dom.setTextContent(td, goog.string.Unicode.NBSP);958}959tr.appendChild(td);960}961tbody.appendChild(tr);962}963return table;964};965966967968/**969* Creates a new Node from constant strings of HTML markup.970* @param {...!goog.string.Const} var_args The HTML strings to concatenate then971* convert into a node.972* @return {!Node}973*/974goog.dom.constHtmlToNode = function(var_args) {975var stringArray = goog.array.map(arguments, goog.string.Const.unwrap);976var safeHtml =977goog.html.uncheckedconversions978.safeHtmlFromStringKnownToSatisfyTypeContract(979goog.string.Const.from(980'Constant HTML string, that gets turned into a ' +981'Node later, so it will be automatically balanced.'),982stringArray.join(''));983return goog.dom.safeHtmlToNode(safeHtml);984};985986987/**988* Converts HTML markup into a node. This is a safe version of989* {@code goog.dom.htmlToDocumentFragment} which is now deleted.990* @param {!goog.html.SafeHtml} html The HTML markup to convert.991* @return {!Node} The resulting node.992*/993goog.dom.safeHtmlToNode = function(html) {994return goog.dom.safeHtmlToNode_(document, html);995};996997998/**999* Helper for {@code safeHtmlToNode}.1000* @param {!Document} doc The document.1001* @param {!goog.html.SafeHtml} html The HTML markup to convert.1002* @return {!Node} The resulting node.1003* @private1004*/1005goog.dom.safeHtmlToNode_ = function(doc, html) {1006var tempDiv = goog.dom.createElement_(doc, goog.dom.TagName.DIV);1007if (goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT) {1008goog.dom.safe.setInnerHtml(1009tempDiv, goog.html.SafeHtml.concat(goog.html.SafeHtml.BR, html));1010tempDiv.removeChild(tempDiv.firstChild);1011} else {1012goog.dom.safe.setInnerHtml(tempDiv, html);1013}1014return goog.dom.childrenToNode_(doc, tempDiv);1015};101610171018/**1019* Helper for {@code safeHtmlToNode_}.1020* @param {!Document} doc The document.1021* @param {!Node} tempDiv The input node.1022* @return {!Node} The resulting node.1023* @private1024*/1025goog.dom.childrenToNode_ = function(doc, tempDiv) {1026if (tempDiv.childNodes.length == 1) {1027return tempDiv.removeChild(tempDiv.firstChild);1028} else {1029var fragment = doc.createDocumentFragment();1030while (tempDiv.firstChild) {1031fragment.appendChild(tempDiv.firstChild);1032}1033return fragment;1034}1035};103610371038/**1039* Returns true if the browser is in "CSS1-compatible" (standards-compliant)1040* mode, false otherwise.1041* @return {boolean} True if in CSS1-compatible mode.1042*/1043goog.dom.isCss1CompatMode = function() {1044return goog.dom.isCss1CompatMode_(document);1045};104610471048/**1049* Returns true if the browser is in "CSS1-compatible" (standards-compliant)1050* mode, false otherwise.1051* @param {!Document} doc The document to check.1052* @return {boolean} True if in CSS1-compatible mode.1053* @private1054*/1055goog.dom.isCss1CompatMode_ = function(doc) {1056if (goog.dom.COMPAT_MODE_KNOWN_) {1057return goog.dom.ASSUME_STANDARDS_MODE;1058}10591060return doc.compatMode == 'CSS1Compat';1061};106210631064/**1065* Determines if the given node can contain children, intended to be used for1066* HTML generation.1067*1068* IE natively supports node.canHaveChildren but has inconsistent behavior.1069* Prior to IE8 the base tag allows children and in IE9 all nodes return true1070* for canHaveChildren.1071*1072* In practice all non-IE browsers allow you to add children to any node, but1073* the behavior is inconsistent:1074*1075* <pre>1076* var a = goog.dom.createElement(goog.dom.TagName.BR);1077* a.appendChild(document.createTextNode('foo'));1078* a.appendChild(document.createTextNode('bar'));1079* console.log(a.childNodes.length); // 21080* console.log(a.innerHTML); // Chrome: "", IE9: "foobar", FF3.5: "foobar"1081* </pre>1082*1083* For more information, see:1084* http://dev.w3.org/html5/markup/syntax.html#syntax-elements1085*1086* TODO(user): Rename shouldAllowChildren() ?1087*1088* @param {Node} node The node to check.1089* @return {boolean} Whether the node can contain children.1090*/1091goog.dom.canHaveChildren = function(node) {1092if (node.nodeType != goog.dom.NodeType.ELEMENT) {1093return false;1094}1095switch (/** @type {!Element} */ (node).tagName) {1096case String(goog.dom.TagName.APPLET):1097case String(goog.dom.TagName.AREA):1098case String(goog.dom.TagName.BASE):1099case String(goog.dom.TagName.BR):1100case String(goog.dom.TagName.COL):1101case String(goog.dom.TagName.COMMAND):1102case String(goog.dom.TagName.EMBED):1103case String(goog.dom.TagName.FRAME):1104case String(goog.dom.TagName.HR):1105case String(goog.dom.TagName.IMG):1106case String(goog.dom.TagName.INPUT):1107case String(goog.dom.TagName.IFRAME):1108case String(goog.dom.TagName.ISINDEX):1109case String(goog.dom.TagName.KEYGEN):1110case String(goog.dom.TagName.LINK):1111case String(goog.dom.TagName.NOFRAMES):1112case String(goog.dom.TagName.NOSCRIPT):1113case String(goog.dom.TagName.META):1114case String(goog.dom.TagName.OBJECT):1115case String(goog.dom.TagName.PARAM):1116case String(goog.dom.TagName.SCRIPT):1117case String(goog.dom.TagName.SOURCE):1118case String(goog.dom.TagName.STYLE):1119case String(goog.dom.TagName.TRACK):1120case String(goog.dom.TagName.WBR):1121return false;1122}1123return true;1124};112511261127/**1128* Appends a child to a node.1129* @param {Node} parent Parent.1130* @param {Node} child Child.1131*/1132goog.dom.appendChild = function(parent, child) {1133parent.appendChild(child);1134};113511361137/**1138* Appends a node with text or other nodes.1139* @param {!Node} parent The node to append nodes to.1140* @param {...goog.dom.Appendable} var_args The things to append to the node.1141* If this is a Node it is appended as is.1142* If this is a string then a text node is appended.1143* If this is an array like object then fields 0 to length - 1 are appended.1144*/1145goog.dom.append = function(parent, var_args) {1146goog.dom.append_(goog.dom.getOwnerDocument(parent), parent, arguments, 1);1147};114811491150/**1151* Removes all the child nodes on a DOM node.1152* @param {Node} node Node to remove children from.1153*/1154goog.dom.removeChildren = function(node) {1155// Note: Iterations over live collections can be slow, this is the fastest1156// we could find. The double parenthesis are used to prevent JsCompiler and1157// strict warnings.1158var child;1159while ((child = node.firstChild)) {1160node.removeChild(child);1161}1162};116311641165/**1166* Inserts a new node before an existing reference node (i.e. as the previous1167* sibling). If the reference node has no parent, then does nothing.1168* @param {Node} newNode Node to insert.1169* @param {Node} refNode Reference node to insert before.1170*/1171goog.dom.insertSiblingBefore = function(newNode, refNode) {1172if (refNode.parentNode) {1173refNode.parentNode.insertBefore(newNode, refNode);1174}1175};117611771178/**1179* Inserts a new node after an existing reference node (i.e. as the next1180* sibling). If the reference node has no parent, then does nothing.1181* @param {Node} newNode Node to insert.1182* @param {Node} refNode Reference node to insert after.1183*/1184goog.dom.insertSiblingAfter = function(newNode, refNode) {1185if (refNode.parentNode) {1186refNode.parentNode.insertBefore(newNode, refNode.nextSibling);1187}1188};118911901191/**1192* Insert a child at a given index. If index is larger than the number of child1193* nodes that the parent currently has, the node is inserted as the last child1194* node.1195* @param {Element} parent The element into which to insert the child.1196* @param {Node} child The element to insert.1197* @param {number} index The index at which to insert the new child node. Must1198* not be negative.1199*/1200goog.dom.insertChildAt = function(parent, child, index) {1201// Note that if the second argument is null, insertBefore1202// will append the child at the end of the list of children.1203parent.insertBefore(child, parent.childNodes[index] || null);1204};120512061207/**1208* Removes a node from its parent.1209* @param {Node} node The node to remove.1210* @return {Node} The node removed if removed; else, null.1211*/1212goog.dom.removeNode = function(node) {1213return node && node.parentNode ? node.parentNode.removeChild(node) : null;1214};121512161217/**1218* Replaces a node in the DOM tree. Will do nothing if {@code oldNode} has no1219* parent.1220* @param {Node} newNode Node to insert.1221* @param {Node} oldNode Node to replace.1222*/1223goog.dom.replaceNode = function(newNode, oldNode) {1224var parent = oldNode.parentNode;1225if (parent) {1226parent.replaceChild(newNode, oldNode);1227}1228};122912301231/**1232* Flattens an element. That is, removes it and replace it with its children.1233* Does nothing if the element is not in the document.1234* @param {Element} element The element to flatten.1235* @return {Element|undefined} The original element, detached from the document1236* tree, sans children; or undefined, if the element was not in the document1237* to begin with.1238*/1239goog.dom.flattenElement = function(element) {1240var child, parent = element.parentNode;1241if (parent && parent.nodeType != goog.dom.NodeType.DOCUMENT_FRAGMENT) {1242// Use IE DOM method (supported by Opera too) if available1243if (element.removeNode) {1244return /** @type {Element} */ (element.removeNode(false));1245} else {1246// Move all children of the original node up one level.1247while ((child = element.firstChild)) {1248parent.insertBefore(child, element);1249}12501251// Detach the original element.1252return /** @type {Element} */ (goog.dom.removeNode(element));1253}1254}1255};125612571258/**1259* Returns an array containing just the element children of the given element.1260* @param {Element} element The element whose element children we want.1261* @return {!(Array<!Element>|NodeList<!Element>)} An array or array-like list1262* of just the element children of the given element.1263*/1264goog.dom.getChildren = function(element) {1265// We check if the children attribute is supported for child elements1266// since IE8 misuses the attribute by also including comments.1267if (goog.dom.BrowserFeature.CAN_USE_CHILDREN_ATTRIBUTE &&1268element.children != undefined) {1269return element.children;1270}1271// Fall back to manually filtering the element's child nodes.1272return goog.array.filter(element.childNodes, function(node) {1273return node.nodeType == goog.dom.NodeType.ELEMENT;1274});1275};127612771278/**1279* Returns the first child node that is an element.1280* @param {Node} node The node to get the first child element of.1281* @return {Element} The first child node of {@code node} that is an element.1282*/1283goog.dom.getFirstElementChild = function(node) {1284if (goog.isDef(node.firstElementChild)) {1285return /** @type {!Element} */ (node).firstElementChild;1286}1287return goog.dom.getNextElementNode_(node.firstChild, true);1288};128912901291/**1292* Returns the last child node that is an element.1293* @param {Node} node The node to get the last child element of.1294* @return {Element} The last child node of {@code node} that is an element.1295*/1296goog.dom.getLastElementChild = function(node) {1297if (goog.isDef(node.lastElementChild)) {1298return /** @type {!Element} */ (node).lastElementChild;1299}1300return goog.dom.getNextElementNode_(node.lastChild, false);1301};130213031304/**1305* Returns the first next sibling that is an element.1306* @param {Node} node The node to get the next sibling element of.1307* @return {Element} The next sibling of {@code node} that is an element.1308*/1309goog.dom.getNextElementSibling = function(node) {1310if (goog.isDef(node.nextElementSibling)) {1311return /** @type {!Element} */ (node).nextElementSibling;1312}1313return goog.dom.getNextElementNode_(node.nextSibling, true);1314};131513161317/**1318* Returns the first previous sibling that is an element.1319* @param {Node} node The node to get the previous sibling element of.1320* @return {Element} The first previous sibling of {@code node} that is1321* an element.1322*/1323goog.dom.getPreviousElementSibling = function(node) {1324if (goog.isDef(node.previousElementSibling)) {1325return /** @type {!Element} */ (node).previousElementSibling;1326}1327return goog.dom.getNextElementNode_(node.previousSibling, false);1328};132913301331/**1332* Returns the first node that is an element in the specified direction,1333* starting with {@code node}.1334* @param {Node} node The node to get the next element from.1335* @param {boolean} forward Whether to look forwards or backwards.1336* @return {Element} The first element.1337* @private1338*/1339goog.dom.getNextElementNode_ = function(node, forward) {1340while (node && node.nodeType != goog.dom.NodeType.ELEMENT) {1341node = forward ? node.nextSibling : node.previousSibling;1342}13431344return /** @type {Element} */ (node);1345};134613471348/**1349* Returns the next node in source order from the given node.1350* @param {Node} node The node.1351* @return {Node} The next node in the DOM tree, or null if this was the last1352* node.1353*/1354goog.dom.getNextNode = function(node) {1355if (!node) {1356return null;1357}13581359if (node.firstChild) {1360return node.firstChild;1361}13621363while (node && !node.nextSibling) {1364node = node.parentNode;1365}13661367return node ? node.nextSibling : null;1368};136913701371/**1372* Returns the previous node in source order from the given node.1373* @param {Node} node The node.1374* @return {Node} The previous node in the DOM tree, or null if this was the1375* first node.1376*/1377goog.dom.getPreviousNode = function(node) {1378if (!node) {1379return null;1380}13811382if (!node.previousSibling) {1383return node.parentNode;1384}13851386node = node.previousSibling;1387while (node && node.lastChild) {1388node = node.lastChild;1389}13901391return node;1392};139313941395/**1396* Whether the object looks like a DOM node.1397* @param {?} obj The object being tested for node likeness.1398* @return {boolean} Whether the object looks like a DOM node.1399*/1400goog.dom.isNodeLike = function(obj) {1401return goog.isObject(obj) && obj.nodeType > 0;1402};140314041405/**1406* Whether the object looks like an Element.1407* @param {?} obj The object being tested for Element likeness.1408* @return {boolean} Whether the object looks like an Element.1409*/1410goog.dom.isElement = function(obj) {1411return goog.isObject(obj) && obj.nodeType == goog.dom.NodeType.ELEMENT;1412};141314141415/**1416* Returns true if the specified value is a Window object. This includes the1417* global window for HTML pages, and iframe windows.1418* @param {?} obj Variable to test.1419* @return {boolean} Whether the variable is a window.1420*/1421goog.dom.isWindow = function(obj) {1422return goog.isObject(obj) && obj['window'] == obj;1423};142414251426/**1427* Returns an element's parent, if it's an Element.1428* @param {Element} element The DOM element.1429* @return {Element} The parent, or null if not an Element.1430*/1431goog.dom.getParentElement = function(element) {1432var parent;1433if (goog.dom.BrowserFeature.CAN_USE_PARENT_ELEMENT_PROPERTY) {1434var isIe9 = goog.userAgent.IE && goog.userAgent.isVersionOrHigher('9') &&1435!goog.userAgent.isVersionOrHigher('10');1436// SVG elements in IE9 can't use the parentElement property.1437// goog.global['SVGElement'] is not defined in IE9 quirks mode.1438if (!(isIe9 && goog.global['SVGElement'] &&1439element instanceof goog.global['SVGElement'])) {1440parent = element.parentElement;1441if (parent) {1442return parent;1443}1444}1445}1446parent = element.parentNode;1447return goog.dom.isElement(parent) ? /** @type {!Element} */ (parent) : null;1448};144914501451/**1452* Whether a node contains another node.1453* @param {?Node|undefined} parent The node that should contain the other node.1454* @param {?Node|undefined} descendant The node to test presence of.1455* @return {boolean} Whether the parent node contains the descendent node.1456*/1457goog.dom.contains = function(parent, descendant) {1458if (!parent || !descendant) {1459return false;1460}1461// We use browser specific methods for this if available since it is faster1462// that way.14631464// IE DOM1465if (parent.contains && descendant.nodeType == goog.dom.NodeType.ELEMENT) {1466return parent == descendant || parent.contains(descendant);1467}14681469// W3C DOM Level 31470if (typeof parent.compareDocumentPosition != 'undefined') {1471return parent == descendant ||1472Boolean(parent.compareDocumentPosition(descendant) & 16);1473}14741475// W3C DOM Level 11476while (descendant && parent != descendant) {1477descendant = descendant.parentNode;1478}1479return descendant == parent;1480};148114821483/**1484* Compares the document order of two nodes, returning 0 if they are the same1485* node, a negative number if node1 is before node2, and a positive number if1486* node2 is before node1. Note that we compare the order the tags appear in the1487* document so in the tree <b><i>text</i></b> the B node is considered to be1488* before the I node.1489*1490* @param {Node} node1 The first node to compare.1491* @param {Node} node2 The second node to compare.1492* @return {number} 0 if the nodes are the same node, a negative number if node11493* is before node2, and a positive number if node2 is before node1.1494*/1495goog.dom.compareNodeOrder = function(node1, node2) {1496// Fall out quickly for equality.1497if (node1 == node2) {1498return 0;1499}15001501// Use compareDocumentPosition where available1502if (node1.compareDocumentPosition) {1503// 4 is the bitmask for FOLLOWS.1504return node1.compareDocumentPosition(node2) & 2 ? 1 : -1;1505}15061507// Special case for document nodes on IE 7 and 8.1508if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {1509if (node1.nodeType == goog.dom.NodeType.DOCUMENT) {1510return -1;1511}1512if (node2.nodeType == goog.dom.NodeType.DOCUMENT) {1513return 1;1514}1515}15161517// Process in IE using sourceIndex - we check to see if the first node has1518// a source index or if its parent has one.1519if ('sourceIndex' in node1 ||1520(node1.parentNode && 'sourceIndex' in node1.parentNode)) {1521var isElement1 = node1.nodeType == goog.dom.NodeType.ELEMENT;1522var isElement2 = node2.nodeType == goog.dom.NodeType.ELEMENT;15231524if (isElement1 && isElement2) {1525return node1.sourceIndex - node2.sourceIndex;1526} else {1527var parent1 = node1.parentNode;1528var parent2 = node2.parentNode;15291530if (parent1 == parent2) {1531return goog.dom.compareSiblingOrder_(node1, node2);1532}15331534if (!isElement1 && goog.dom.contains(parent1, node2)) {1535return -1 * goog.dom.compareParentsDescendantNodeIe_(node1, node2);1536}153715381539if (!isElement2 && goog.dom.contains(parent2, node1)) {1540return goog.dom.compareParentsDescendantNodeIe_(node2, node1);1541}15421543return (isElement1 ? node1.sourceIndex : parent1.sourceIndex) -1544(isElement2 ? node2.sourceIndex : parent2.sourceIndex);1545}1546}15471548// For Safari, we compare ranges.1549var doc = goog.dom.getOwnerDocument(node1);15501551var range1, range2;1552range1 = doc.createRange();1553range1.selectNode(node1);1554range1.collapse(true);15551556range2 = doc.createRange();1557range2.selectNode(node2);1558range2.collapse(true);15591560return range1.compareBoundaryPoints(1561goog.global['Range'].START_TO_END, range2);1562};156315641565/**1566* Utility function to compare the position of two nodes, when1567* {@code textNode}'s parent is an ancestor of {@code node}. If this entry1568* condition is not met, this function will attempt to reference a null object.1569* @param {!Node} textNode The textNode to compare.1570* @param {Node} node The node to compare.1571* @return {number} -1 if node is before textNode, +1 otherwise.1572* @private1573*/1574goog.dom.compareParentsDescendantNodeIe_ = function(textNode, node) {1575var parent = textNode.parentNode;1576if (parent == node) {1577// If textNode is a child of node, then node comes first.1578return -1;1579}1580var sibling = node;1581while (sibling.parentNode != parent) {1582sibling = sibling.parentNode;1583}1584return goog.dom.compareSiblingOrder_(sibling, textNode);1585};158615871588/**1589* Utility function to compare the position of two nodes known to be non-equal1590* siblings.1591* @param {Node} node1 The first node to compare.1592* @param {!Node} node2 The second node to compare.1593* @return {number} -1 if node1 is before node2, +1 otherwise.1594* @private1595*/1596goog.dom.compareSiblingOrder_ = function(node1, node2) {1597var s = node2;1598while ((s = s.previousSibling)) {1599if (s == node1) {1600// We just found node1 before node2.1601return -1;1602}1603}16041605// Since we didn't find it, node1 must be after node2.1606return 1;1607};160816091610/**1611* Find the deepest common ancestor of the given nodes.1612* @param {...Node} var_args The nodes to find a common ancestor of.1613* @return {Node} The common ancestor of the nodes, or null if there is none.1614* null will only be returned if two or more of the nodes are from different1615* documents.1616*/1617goog.dom.findCommonAncestor = function(var_args) {1618var i, count = arguments.length;1619if (!count) {1620return null;1621} else if (count == 1) {1622return arguments[0];1623}16241625var paths = [];1626var minLength = Infinity;1627for (i = 0; i < count; i++) {1628// Compute the list of ancestors.1629var ancestors = [];1630var node = arguments[i];1631while (node) {1632ancestors.unshift(node);1633node = node.parentNode;1634}16351636// Save the list for comparison.1637paths.push(ancestors);1638minLength = Math.min(minLength, ancestors.length);1639}1640var output = null;1641for (i = 0; i < minLength; i++) {1642var first = paths[0][i];1643for (var j = 1; j < count; j++) {1644if (first != paths[j][i]) {1645return output;1646}1647}1648output = first;1649}1650return output;1651};165216531654/**1655* Returns the owner document for a node.1656* @param {Node|Window} node The node to get the document for.1657* @return {!Document} The document owning the node.1658*/1659goog.dom.getOwnerDocument = function(node) {1660// TODO(nnaze): Update param signature to be non-nullable.1661goog.asserts.assert(node, 'Node cannot be null or undefined.');1662return /** @type {!Document} */ (1663node.nodeType == goog.dom.NodeType.DOCUMENT ? node : node.ownerDocument ||1664node.document);1665};166616671668/**1669* Cross-browser function for getting the document element of a frame or iframe.1670* @param {Element} frame Frame element.1671* @return {!Document} The frame content document.1672*/1673goog.dom.getFrameContentDocument = function(frame) {1674return frame.contentDocument ||1675/** @type {!HTMLFrameElement} */ (frame).contentWindow.document;1676};167716781679/**1680* Cross-browser function for getting the window of a frame or iframe.1681* @param {Element} frame Frame element.1682* @return {Window} The window associated with the given frame, or null if none1683* exists.1684*/1685goog.dom.getFrameContentWindow = function(frame) {1686try {1687return frame.contentWindow ||1688(frame.contentDocument ? goog.dom.getWindow(frame.contentDocument) :1689null);1690} catch (e) {1691// NOTE(user): In IE8, checking the contentWindow or contentDocument1692// properties will throw a "Unspecified Error" exception if the iframe is1693// not inserted in the DOM. If we get this we can be sure that no window1694// exists, so return null.1695}1696return null;1697};169816991700/**1701* Sets the text content of a node, with cross-browser support.1702* @param {Node} node The node to change the text content of.1703* @param {string|number} text The value that should replace the node's content.1704*/1705goog.dom.setTextContent = function(node, text) {1706goog.asserts.assert(1707node != null,1708'goog.dom.setTextContent expects a non-null value for node');17091710if ('textContent' in node) {1711node.textContent = text;1712} else if (node.nodeType == goog.dom.NodeType.TEXT) {1713node.data = text;1714} else if (1715node.firstChild && node.firstChild.nodeType == goog.dom.NodeType.TEXT) {1716// If the first child is a text node we just change its data and remove the1717// rest of the children.1718while (node.lastChild != node.firstChild) {1719node.removeChild(node.lastChild);1720}1721node.firstChild.data = text;1722} else {1723goog.dom.removeChildren(node);1724var doc = goog.dom.getOwnerDocument(node);1725node.appendChild(doc.createTextNode(String(text)));1726}1727};172817291730/**1731* Gets the outerHTML of a node, which islike innerHTML, except that it1732* actually contains the HTML of the node itself.1733* @param {Element} element The element to get the HTML of.1734* @return {string} The outerHTML of the given element.1735*/1736goog.dom.getOuterHtml = function(element) {1737goog.asserts.assert(1738element !== null,1739'goog.dom.getOuterHtml expects a non-null value for element');1740// IE, Opera and WebKit all have outerHTML.1741if ('outerHTML' in element) {1742return element.outerHTML;1743} else {1744var doc = goog.dom.getOwnerDocument(element);1745var div = goog.dom.createElement_(doc, goog.dom.TagName.DIV);1746div.appendChild(element.cloneNode(true));1747return div.innerHTML;1748}1749};175017511752/**1753* Finds the first descendant node that matches the filter function, using1754* a depth first search. This function offers the most general purpose way1755* of finding a matching element. You may also wish to consider1756* {@code goog.dom.query} which can express many matching criteria using1757* CSS selector expressions. These expressions often result in a more1758* compact representation of the desired result.1759* @see goog.dom.query1760*1761* @param {Node} root The root of the tree to search.1762* @param {function(Node) : boolean} p The filter function.1763* @return {Node|undefined} The found node or undefined if none is found.1764*/1765goog.dom.findNode = function(root, p) {1766var rv = [];1767var found = goog.dom.findNodes_(root, p, rv, true);1768return found ? rv[0] : undefined;1769};177017711772/**1773* Finds all the descendant nodes that match the filter function, using a1774* a depth first search. This function offers the most general-purpose way1775* of finding a set of matching elements. You may also wish to consider1776* {@code goog.dom.query} which can express many matching criteria using1777* CSS selector expressions. These expressions often result in a more1778* compact representation of the desired result.17791780* @param {Node} root The root of the tree to search.1781* @param {function(Node) : boolean} p The filter function.1782* @return {!Array<!Node>} The found nodes or an empty array if none are found.1783*/1784goog.dom.findNodes = function(root, p) {1785var rv = [];1786goog.dom.findNodes_(root, p, rv, false);1787return rv;1788};178917901791/**1792* Finds the first or all the descendant nodes that match the filter function,1793* using a depth first search.1794* @param {Node} root The root of the tree to search.1795* @param {function(Node) : boolean} p The filter function.1796* @param {!Array<!Node>} rv The found nodes are added to this array.1797* @param {boolean} findOne If true we exit after the first found node.1798* @return {boolean} Whether the search is complete or not. True in case findOne1799* is true and the node is found. False otherwise.1800* @private1801*/1802goog.dom.findNodes_ = function(root, p, rv, findOne) {1803if (root != null) {1804var child = root.firstChild;1805while (child) {1806if (p(child)) {1807rv.push(child);1808if (findOne) {1809return true;1810}1811}1812if (goog.dom.findNodes_(child, p, rv, findOne)) {1813return true;1814}1815child = child.nextSibling;1816}1817}1818return false;1819};182018211822/**1823* Map of tags whose content to ignore when calculating text length.1824* @private {!Object<string, number>}1825* @const1826*/1827goog.dom.TAGS_TO_IGNORE_ = {1828'SCRIPT': 1,1829'STYLE': 1,1830'HEAD': 1,1831'IFRAME': 1,1832'OBJECT': 11833};183418351836/**1837* Map of tags which have predefined values with regard to whitespace.1838* @private {!Object<string, string>}1839* @const1840*/1841goog.dom.PREDEFINED_TAG_VALUES_ = {1842'IMG': ' ',1843'BR': '\n'1844};184518461847/**1848* Returns true if the element has a tab index that allows it to receive1849* keyboard focus (tabIndex >= 0), false otherwise. Note that some elements1850* natively support keyboard focus, even if they have no tab index.1851* @param {!Element} element Element to check.1852* @return {boolean} Whether the element has a tab index that allows keyboard1853* focus.1854*/1855goog.dom.isFocusableTabIndex = function(element) {1856return goog.dom.hasSpecifiedTabIndex_(element) &&1857goog.dom.isTabIndexFocusable_(element);1858};185918601861/**1862* Enables or disables keyboard focus support on the element via its tab index.1863* Only elements for which {@link goog.dom.isFocusableTabIndex} returns true1864* (or elements that natively support keyboard focus, like form elements) can1865* receive keyboard focus. See http://go/tabindex for more info.1866* @param {Element} element Element whose tab index is to be changed.1867* @param {boolean} enable Whether to set or remove a tab index on the element1868* that supports keyboard focus.1869*/1870goog.dom.setFocusableTabIndex = function(element, enable) {1871if (enable) {1872element.tabIndex = 0;1873} else {1874// Set tabIndex to -1 first, then remove it. This is a workaround for1875// Safari (confirmed in version 4 on Windows). When removing the attribute1876// without setting it to -1 first, the element remains keyboard focusable1877// despite not having a tabIndex attribute anymore.1878element.tabIndex = -1;1879element.removeAttribute('tabIndex'); // Must be camelCase!1880}1881};188218831884/**1885* Returns true if the element can be focused, i.e. it has a tab index that1886* allows it to receive keyboard focus (tabIndex >= 0), or it is an element1887* that natively supports keyboard focus.1888* @param {!Element} element Element to check.1889* @return {boolean} Whether the element allows keyboard focus.1890*/1891goog.dom.isFocusable = function(element) {1892var focusable;1893// Some elements can have unspecified tab index and still receive focus.1894if (goog.dom.nativelySupportsFocus_(element)) {1895// Make sure the element is not disabled ...1896focusable = !element.disabled &&1897// ... and if a tab index is specified, it allows focus.1898(!goog.dom.hasSpecifiedTabIndex_(element) ||1899goog.dom.isTabIndexFocusable_(element));1900} else {1901focusable = goog.dom.isFocusableTabIndex(element);1902}19031904// IE requires elements to be visible in order to focus them.1905return focusable && goog.userAgent.IE ?1906goog.dom.hasNonZeroBoundingRect_(/** @type {!HTMLElement} */ (element)) :1907focusable;1908};190919101911/**1912* Returns true if the element has a specified tab index.1913* @param {!Element} element Element to check.1914* @return {boolean} Whether the element has a specified tab index.1915* @private1916*/1917goog.dom.hasSpecifiedTabIndex_ = function(element) {1918// IE8 and below don't support hasAttribute(), instead check whether the1919// 'tabindex' attributeNode is specified. Otherwise check hasAttribute().1920if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')) {1921var attrNode = element.getAttributeNode('tabindex'); // Must be lowercase!1922return goog.isDefAndNotNull(attrNode) && attrNode.specified;1923} else {1924return element.hasAttribute('tabindex');1925}1926};192719281929/**1930* Returns true if the element's tab index allows the element to be focused.1931* @param {!Element} element Element to check.1932* @return {boolean} Whether the element's tab index allows focus.1933* @private1934*/1935goog.dom.isTabIndexFocusable_ = function(element) {1936var index = /** @type {!HTMLElement} */ (element).tabIndex;1937// NOTE: IE9 puts tabIndex in 16-bit int, e.g. -2 is 65534.1938return goog.isNumber(index) && index >= 0 && index < 32768;1939};194019411942/**1943* Returns true if the element is focusable even when tabIndex is not set.1944* @param {!Element} element Element to check.1945* @return {boolean} Whether the element natively supports focus.1946* @private1947*/1948goog.dom.nativelySupportsFocus_ = function(element) {1949return element.tagName == goog.dom.TagName.A ||1950element.tagName == goog.dom.TagName.INPUT ||1951element.tagName == goog.dom.TagName.TEXTAREA ||1952element.tagName == goog.dom.TagName.SELECT ||1953element.tagName == goog.dom.TagName.BUTTON;1954};195519561957/**1958* Returns true if the element has a bounding rectangle that would be visible1959* (i.e. its width and height are greater than zero).1960* @param {!HTMLElement} element Element to check.1961* @return {boolean} Whether the element has a non-zero bounding rectangle.1962* @private1963*/1964goog.dom.hasNonZeroBoundingRect_ = function(element) {1965var rect;1966if (!goog.isFunction(element['getBoundingClientRect']) ||1967// In IE, getBoundingClientRect throws on detached nodes.1968(goog.userAgent.IE && element.parentElement == null)) {1969rect = {'height': element.offsetHeight, 'width': element.offsetWidth};1970} else {1971rect = element.getBoundingClientRect();1972}1973return goog.isDefAndNotNull(rect) && rect.height > 0 && rect.width > 0;1974};197519761977/**1978* Returns the text content of the current node, without markup and invisible1979* symbols. New lines are stripped and whitespace is collapsed,1980* such that each character would be visible.1981*1982* In browsers that support it, innerText is used. Other browsers attempt to1983* simulate it via node traversal. Line breaks are canonicalized in IE.1984*1985* @param {Node} node The node from which we are getting content.1986* @return {string} The text content.1987*/1988goog.dom.getTextContent = function(node) {1989var textContent;1990// Note(arv): IE9, Opera, and Safari 3 support innerText but they include1991// text nodes in script tags. So we revert to use a user agent test here.1992if (goog.dom.BrowserFeature.CAN_USE_INNER_TEXT && node !== null &&1993('innerText' in node)) {1994textContent = goog.string.canonicalizeNewlines(node.innerText);1995// Unfortunately .innerText() returns text with ­ symbols1996// We need to filter it out and then remove duplicate whitespaces1997} else {1998var buf = [];1999goog.dom.getTextContent_(node, buf, true);2000textContent = buf.join('');2001}20022003// Strip ­ entities. goog.format.insertWordBreaks inserts them in Opera.2004textContent = textContent.replace(/ \xAD /g, ' ').replace(/\xAD/g, '');2005// Strip ​ entities. goog.format.insertWordBreaks inserts them in IE8.2006textContent = textContent.replace(/\u200B/g, '');20072008// Skip this replacement on old browsers with working innerText, which2009// automatically turns into ' ' and / +/ into ' ' when reading2010// innerText.2011if (!goog.dom.BrowserFeature.CAN_USE_INNER_TEXT) {2012textContent = textContent.replace(/ +/g, ' ');2013}2014if (textContent != ' ') {2015textContent = textContent.replace(/^\s*/, '');2016}20172018return textContent;2019};202020212022/**2023* Returns the text content of the current node, without markup.2024*2025* Unlike {@code getTextContent} this method does not collapse whitespaces2026* or normalize lines breaks.2027*2028* @param {Node} node The node from which we are getting content.2029* @return {string} The raw text content.2030*/2031goog.dom.getRawTextContent = function(node) {2032var buf = [];2033goog.dom.getTextContent_(node, buf, false);20342035return buf.join('');2036};203720382039/**2040* Recursive support function for text content retrieval.2041*2042* @param {Node} node The node from which we are getting content.2043* @param {Array<string>} buf string buffer.2044* @param {boolean} normalizeWhitespace Whether to normalize whitespace.2045* @private2046*/2047goog.dom.getTextContent_ = function(node, buf, normalizeWhitespace) {2048if (node.nodeName in goog.dom.TAGS_TO_IGNORE_) {2049// ignore certain tags2050} else if (node.nodeType == goog.dom.NodeType.TEXT) {2051if (normalizeWhitespace) {2052buf.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, ''));2053} else {2054buf.push(node.nodeValue);2055}2056} else if (node.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {2057buf.push(goog.dom.PREDEFINED_TAG_VALUES_[node.nodeName]);2058} else {2059var child = node.firstChild;2060while (child) {2061goog.dom.getTextContent_(child, buf, normalizeWhitespace);2062child = child.nextSibling;2063}2064}2065};206620672068/**2069* Returns the text length of the text contained in a node, without markup. This2070* is equivalent to the selection length if the node was selected, or the number2071* of cursor movements to traverse the node. Images & BRs take one space. New2072* lines are ignored.2073*2074* @param {Node} node The node whose text content length is being calculated.2075* @return {number} The length of {@code node}'s text content.2076*/2077goog.dom.getNodeTextLength = function(node) {2078return goog.dom.getTextContent(node).length;2079};208020812082/**2083* Returns the text offset of a node relative to one of its ancestors. The text2084* length is the same as the length calculated by goog.dom.getNodeTextLength.2085*2086* @param {Node} node The node whose offset is being calculated.2087* @param {Node=} opt_offsetParent The node relative to which the offset will2088* be calculated. Defaults to the node's owner document's body.2089* @return {number} The text offset.2090*/2091goog.dom.getNodeTextOffset = function(node, opt_offsetParent) {2092var root = opt_offsetParent || goog.dom.getOwnerDocument(node).body;2093var buf = [];2094while (node && node != root) {2095var cur = node;2096while ((cur = cur.previousSibling)) {2097buf.unshift(goog.dom.getTextContent(cur));2098}2099node = node.parentNode;2100}2101// Trim left to deal with FF cases when there might be line breaks and empty2102// nodes at the front of the text2103return goog.string.trimLeft(buf.join('')).replace(/ +/g, ' ').length;2104};210521062107/**2108* Returns the node at a given offset in a parent node. If an object is2109* provided for the optional third parameter, the node and the remainder of the2110* offset will stored as properties of this object.2111* @param {Node} parent The parent node.2112* @param {number} offset The offset into the parent node.2113* @param {Object=} opt_result Object to be used to store the return value. The2114* return value will be stored in the form {node: Node, remainder: number}2115* if this object is provided.2116* @return {Node} The node at the given offset.2117*/2118goog.dom.getNodeAtOffset = function(parent, offset, opt_result) {2119var stack = [parent], pos = 0, cur = null;2120while (stack.length > 0 && pos < offset) {2121cur = stack.pop();2122if (cur.nodeName in goog.dom.TAGS_TO_IGNORE_) {2123// ignore certain tags2124} else if (cur.nodeType == goog.dom.NodeType.TEXT) {2125var text = cur.nodeValue.replace(/(\r\n|\r|\n)/g, '').replace(/ +/g, ' ');2126pos += text.length;2127} else if (cur.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {2128pos += goog.dom.PREDEFINED_TAG_VALUES_[cur.nodeName].length;2129} else {2130for (var i = cur.childNodes.length - 1; i >= 0; i--) {2131stack.push(cur.childNodes[i]);2132}2133}2134}2135if (goog.isObject(opt_result)) {2136opt_result.remainder = cur ? cur.nodeValue.length + offset - pos - 1 : 0;2137opt_result.node = cur;2138}21392140return cur;2141};214221432144/**2145* Returns true if the object is a {@code NodeList}. To qualify as a NodeList,2146* the object must have a numeric length property and an item function (which2147* has type 'string' on IE for some reason).2148* @param {Object} val Object to test.2149* @return {boolean} Whether the object is a NodeList.2150*/2151goog.dom.isNodeList = function(val) {2152// TODO(attila): Now the isNodeList is part of goog.dom we can use2153// goog.userAgent to make this simpler.2154// A NodeList must have a length property of type 'number' on all platforms.2155if (val && typeof val.length == 'number') {2156// A NodeList is an object everywhere except Safari, where it's a function.2157if (goog.isObject(val)) {2158// A NodeList must have an item function (on non-IE platforms) or an item2159// property of type 'string' (on IE).2160return typeof val.item == 'function' || typeof val.item == 'string';2161} else if (goog.isFunction(val)) {2162// On Safari, a NodeList is a function with an item property that is also2163// a function.2164return typeof val.item == 'function';2165}2166}21672168// Not a NodeList.2169return false;2170};217121722173/**2174* Walks up the DOM hierarchy returning the first ancestor that has the passed2175* tag name and/or class name. If the passed element matches the specified2176* criteria, the element itself is returned.2177* @param {Node} element The DOM node to start with.2178* @param {?(goog.dom.TagName<T>|string)=} opt_tag The tag name to match (or2179* null/undefined to match only based on class name).2180* @param {?string=} opt_class The class name to match (or null/undefined to2181* match only based on tag name).2182* @param {number=} opt_maxSearchSteps Maximum number of levels to search up the2183* dom.2184* @return {?R} The first ancestor that matches the passed criteria, or2185* null if no match is found. The return type is {?Element} if opt_tag is2186* not a member of goog.dom.TagName or a more specific type if it is (e.g.2187* {?HTMLAnchorElement} for goog.dom.TagName.A).2188* @template T2189* @template R := cond(isUnknown(T), 'Element', T) =:2190*/2191goog.dom.getAncestorByTagNameAndClass = function(2192element, opt_tag, opt_class, opt_maxSearchSteps) {2193if (!opt_tag && !opt_class) {2194return null;2195}2196var tagName = opt_tag ? String(opt_tag).toUpperCase() : null;2197return /** @type {Element} */ (goog.dom.getAncestor(element, function(node) {2198return (!tagName || node.nodeName == tagName) &&2199(!opt_class ||2200goog.isString(node.className) &&2201goog.array.contains(node.className.split(/\s+/), opt_class));2202}, true, opt_maxSearchSteps));2203};220422052206/**2207* Walks up the DOM hierarchy returning the first ancestor that has the passed2208* class name. If the passed element matches the specified criteria, the2209* element itself is returned.2210* @param {Node} element The DOM node to start with.2211* @param {string} className The class name to match.2212* @param {number=} opt_maxSearchSteps Maximum number of levels to search up the2213* dom.2214* @return {Element} The first ancestor that matches the passed criteria, or2215* null if none match.2216*/2217goog.dom.getAncestorByClass = function(element, className, opt_maxSearchSteps) {2218return goog.dom.getAncestorByTagNameAndClass(2219element, null, className, opt_maxSearchSteps);2220};222122222223/**2224* Walks up the DOM hierarchy returning the first ancestor that passes the2225* matcher function.2226* @param {Node} element The DOM node to start with.2227* @param {function(Node) : boolean} matcher A function that returns true if the2228* passed node matches the desired criteria.2229* @param {boolean=} opt_includeNode If true, the node itself is included in2230* the search (the first call to the matcher will pass startElement as2231* the node to test).2232* @param {number=} opt_maxSearchSteps Maximum number of levels to search up the2233* dom.2234* @return {Node} DOM node that matched the matcher, or null if there was2235* no match.2236*/2237goog.dom.getAncestor = function(2238element, matcher, opt_includeNode, opt_maxSearchSteps) {2239if (element && !opt_includeNode) {2240element = element.parentNode;2241}2242var steps = 0;2243while (element &&2244(opt_maxSearchSteps == null || steps <= opt_maxSearchSteps)) {2245goog.asserts.assert(element.name != 'parentNode');2246if (matcher(element)) {2247return element;2248}2249element = element.parentNode;2250steps++;2251}2252// Reached the root of the DOM without a match2253return null;2254};225522562257/**2258* Determines the active element in the given document.2259* @param {Document} doc The document to look in.2260* @return {Element} The active element.2261*/2262goog.dom.getActiveElement = function(doc) {2263try {2264return doc && doc.activeElement;2265} catch (e) {2266// NOTE(nicksantos): Sometimes, evaluating document.activeElement in IE2267// throws an exception. I'm not 100% sure why, but I suspect it chokes2268// on document.activeElement if the activeElement has been recently2269// removed from the DOM by a JS operation.2270//2271// We assume that an exception here simply means2272// "there is no active element."2273}22742275return null;2276};227722782279/**2280* Gives the current devicePixelRatio.2281*2282* By default, this is the value of window.devicePixelRatio (which should be2283* preferred if present).2284*2285* If window.devicePixelRatio is not present, the ratio is calculated with2286* window.matchMedia, if present. Otherwise, gives 1.0.2287*2288* Some browsers (including Chrome) consider the browser zoom level in the pixel2289* ratio, so the value may change across multiple calls.2290*2291* @return {number} The number of actual pixels per virtual pixel.2292*/2293goog.dom.getPixelRatio = function() {2294var win = goog.dom.getWindow();2295if (goog.isDef(win.devicePixelRatio)) {2296return win.devicePixelRatio;2297} else if (win.matchMedia) {2298// Should be for IE10 and FF6-17 (this basically clamps to lower)2299// Note that the order of these statements is important2300return goog.dom.matchesPixelRatio_(3) || goog.dom.matchesPixelRatio_(2) ||2301goog.dom.matchesPixelRatio_(1.5) || goog.dom.matchesPixelRatio_(1) ||2302.75;2303}2304return 1;2305};230623072308/**2309* Calculates a mediaQuery to check if the current device supports the2310* given actual to virtual pixel ratio.2311* @param {number} pixelRatio The ratio of actual pixels to virtual pixels.2312* @return {number} pixelRatio if applicable, otherwise 0.2313* @private2314*/2315goog.dom.matchesPixelRatio_ = function(pixelRatio) {2316var win = goog.dom.getWindow();2317/**2318* Due to the 1:96 fixed ratio of CSS in to CSS px, 1dppx is equivalent to2319* 96dpi.2320* @const {number}2321*/2322var dpiPerDppx = 96;2323var query =2324// FF16-172325'(min-resolution: ' + pixelRatio + 'dppx),' +2326// FF6-152327'(min--moz-device-pixel-ratio: ' + pixelRatio + '),' +2328// IE10 (this works for the two browsers above too but I don't want to2329// trust the 1:96 fixed ratio magic)2330'(min-resolution: ' + (pixelRatio * dpiPerDppx) + 'dpi)';2331return win.matchMedia(query).matches ? pixelRatio : 0;2332};233323342335/**2336* Gets '2d' context of a canvas. Shortcut for canvas.getContext('2d') with a2337* type information.2338* @param {!HTMLCanvasElement} canvas2339* @return {!CanvasRenderingContext2D}2340*/2341goog.dom.getCanvasContext2D = function(canvas) {2342return /** @type {!CanvasRenderingContext2D} */ (canvas.getContext('2d'));2343};2344234523462347/**2348* Create an instance of a DOM helper with a new document object.2349* @param {Document=} opt_document Document object to associate with this2350* DOM helper.2351* @constructor2352*/2353goog.dom.DomHelper = function(opt_document) {2354/**2355* Reference to the document object to use2356* @type {!Document}2357* @private2358*/2359this.document_ = opt_document || goog.global.document || document;2360};236123622363/**2364* Gets the dom helper object for the document where the element resides.2365* @param {Node=} opt_node If present, gets the DomHelper for this node.2366* @return {!goog.dom.DomHelper} The DomHelper.2367*/2368goog.dom.DomHelper.prototype.getDomHelper = goog.dom.getDomHelper;236923702371/**2372* Sets the document object.2373* @param {!Document} document Document object.2374*/2375goog.dom.DomHelper.prototype.setDocument = function(document) {2376this.document_ = document;2377};237823792380/**2381* Gets the document object being used by the dom library.2382* @return {!Document} Document object.2383*/2384goog.dom.DomHelper.prototype.getDocument = function() {2385return this.document_;2386};238723882389/**2390* Alias for {@code getElementById}. If a DOM node is passed in then we just2391* return that.2392* @param {string|Element} element Element ID or a DOM node.2393* @return {Element} The element with the given ID, or the node passed in.2394*/2395goog.dom.DomHelper.prototype.getElement = function(element) {2396return goog.dom.getElementHelper_(this.document_, element);2397};239823992400/**2401* Gets an element by id, asserting that the element is found.2402*2403* This is used when an element is expected to exist, and should fail with2404* an assertion error if it does not (if assertions are enabled).2405*2406* @param {string} id Element ID.2407* @return {!Element} The element with the given ID, if it exists.2408*/2409goog.dom.DomHelper.prototype.getRequiredElement = function(id) {2410return goog.dom.getRequiredElementHelper_(this.document_, id);2411};241224132414/**2415* Alias for {@code getElement}.2416* @param {string|Element} element Element ID or a DOM node.2417* @return {Element} The element with the given ID, or the node passed in.2418* @deprecated Use {@link goog.dom.DomHelper.prototype.getElement} instead.2419*/2420goog.dom.DomHelper.prototype.$ = goog.dom.DomHelper.prototype.getElement;242124222423/**2424* Gets elements by tag name.2425* @param {!goog.dom.TagName<T>} tagName2426* @param {(!Document|!Element)=} opt_parent Parent element or document where to2427* look for elements. Defaults to document of this DomHelper.2428* @return {!NodeList<R>} List of elements. The members of the list are2429* {!Element} if tagName is not a member of goog.dom.TagName or more2430* specific types if it is (e.g. {!HTMLAnchorElement} for2431* goog.dom.TagName.A).2432* @template T2433* @template R := cond(isUnknown(T), 'Element', T) =:2434*/2435goog.dom.DomHelper.prototype.getElementsByTagName =2436function(tagName, opt_parent) {2437var parent = opt_parent || this.document_;2438return parent.getElementsByTagName(String(tagName));2439};244024412442/**2443* Looks up elements by both tag and class name, using browser native functions2444* ({@code querySelectorAll}, {@code getElementsByTagName} or2445* {@code getElementsByClassName}) where possible. The returned array is a live2446* NodeList or a static list depending on the code path taken.2447*2448* @see goog.dom.query2449*2450* @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name or * for all2451* tags.2452* @param {?string=} opt_class Optional class name.2453* @param {(Document|Element)=} opt_el Optional element to look in.2454* @return {!IArrayLike<R>} Array-like list of elements (only a length property2455* and numerical indices are guaranteed to exist). The members of the array2456* are {!Element} if opt_tag is not a member of goog.dom.TagName or more2457* specific types if it is (e.g. {!HTMLAnchorElement} for2458* goog.dom.TagName.A).2459* @template T2460* @template R := cond(isUnknown(T), 'Element', T) =:2461*/2462goog.dom.DomHelper.prototype.getElementsByTagNameAndClass = function(2463opt_tag, opt_class, opt_el) {2464return goog.dom.getElementsByTagNameAndClass_(2465this.document_, opt_tag, opt_class, opt_el);2466};246724682469/**2470* Returns an array of all the elements with the provided className.2471* @see {goog.dom.query}2472* @param {string} className the name of the class to look for.2473* @param {Element|Document=} opt_el Optional element to look in.2474* @return {!IArrayLike<!Element>} The items found with the class name provided.2475*/2476goog.dom.DomHelper.prototype.getElementsByClass = function(className, opt_el) {2477var doc = opt_el || this.document_;2478return goog.dom.getElementsByClass(className, doc);2479};248024812482/**2483* Returns the first element we find matching the provided class name.2484* @see {goog.dom.query}2485* @param {string} className the name of the class to look for.2486* @param {(Element|Document)=} opt_el Optional element to look in.2487* @return {Element} The first item found with the class name provided.2488*/2489goog.dom.DomHelper.prototype.getElementByClass = function(className, opt_el) {2490var doc = opt_el || this.document_;2491return goog.dom.getElementByClass(className, doc);2492};249324942495/**2496* Ensures an element with the given className exists, and then returns the2497* first element with the provided className.2498* @see {goog.dom.query}2499* @param {string} className the name of the class to look for.2500* @param {(!Element|!Document)=} opt_root Optional element or document to look2501* in.2502* @return {!Element} The first item found with the class name provided.2503* @throws {goog.asserts.AssertionError} Thrown if no element is found.2504*/2505goog.dom.DomHelper.prototype.getRequiredElementByClass = function(2506className, opt_root) {2507var root = opt_root || this.document_;2508return goog.dom.getRequiredElementByClass(className, root);2509};251025112512/**2513* Alias for {@code getElementsByTagNameAndClass}.2514* @deprecated Use DomHelper getElementsByTagNameAndClass.2515* @see goog.dom.query2516*2517* @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.2518* @param {?string=} opt_class Optional class name.2519* @param {Element=} opt_el Optional element to look in.2520* @return {!IArrayLike<R>} Array-like list of elements (only a length property2521* and numerical indices are guaranteed to exist). The members of the array2522* are {!Element} if opt_tag is a string or more specific types if it is2523* a member of goog.dom.TagName (e.g. {!HTMLAnchorElement} for2524* goog.dom.TagName.A).2525* @template T2526* @template R := cond(isUnknown(T), 'Element', T) =:2527*/2528goog.dom.DomHelper.prototype.$$ =2529goog.dom.DomHelper.prototype.getElementsByTagNameAndClass;253025312532/**2533* Sets a number of properties on a node.2534* @param {Element} element DOM node to set properties on.2535* @param {Object} properties Hash of property:value pairs.2536*/2537goog.dom.DomHelper.prototype.setProperties = goog.dom.setProperties;253825392540/**2541* Gets the dimensions of the viewport.2542* @param {Window=} opt_window Optional window element to test. Defaults to2543* the window of the Dom Helper.2544* @return {!goog.math.Size} Object with values 'width' and 'height'.2545*/2546goog.dom.DomHelper.prototype.getViewportSize = function(opt_window) {2547// TODO(arv): This should not take an argument. That breaks the rule of a2548// a DomHelper representing a single frame/window/document.2549return goog.dom.getViewportSize(opt_window || this.getWindow());2550};255125522553/**2554* Calculates the height of the document.2555*2556* @return {number} The height of the document.2557*/2558goog.dom.DomHelper.prototype.getDocumentHeight = function() {2559return goog.dom.getDocumentHeight_(this.getWindow());2560};256125622563/**2564* Typedef for use with goog.dom.createDom and goog.dom.append.2565* @typedef {Object|string|Array|NodeList}2566*/2567goog.dom.Appendable;256825692570/**2571* Returns a dom node with a set of attributes. This function accepts varargs2572* for subsequent nodes to be added. Subsequent nodes will be added to the2573* first node as childNodes.2574*2575* So:2576* <code>createDom(goog.dom.TagName.DIV, null, createDom(goog.dom.TagName.P), createDom(goog.dom.TagName.P));</code>2577* would return a div with two child paragraphs2578*2579* An easy way to move all child nodes of an existing element to a new parent2580* element is:2581* <code>createDom(goog.dom.TagName.DIV, null, oldElement.childNodes);</code>2582* which will remove all child nodes from the old element and add them as2583* child nodes of the new DIV.2584*2585* @param {string|!goog.dom.TagName<T>} tagName Tag to create.2586* @param {Object|string=} opt_attributes If object, then a map of name-value2587* pairs for attributes. If a string, then this is the className of the new2588* element.2589* @param {...goog.dom.Appendable} var_args Further DOM nodes or2590* strings for text nodes. If one of the var_args is an array or2591* NodeList, its elements will be added as childNodes instead.2592* @return {R} Reference to a DOM node. The return type is {!Element} if tagName2593* is a string or a more specific type if it is a member of2594* goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).2595* @template T2596* @template R := cond(isUnknown(T), 'Element', T) =:2597*/2598goog.dom.DomHelper.prototype.createDom = function(2599tagName, opt_attributes, var_args) {2600return goog.dom.createDom_(this.document_, arguments);2601};260226032604/**2605* Alias for {@code createDom}.2606* @param {string|!goog.dom.TagName<T>} tagName Tag to create.2607* @param {(Object|string)=} opt_attributes If object, then a map of name-value2608* pairs for attributes. If a string, then this is the className of the new2609* element.2610* @param {...goog.dom.Appendable} var_args Further DOM nodes or strings for2611* text nodes. If one of the var_args is an array, its children will be2612* added as childNodes instead.2613* @return {R} Reference to a DOM node. The return type is {!Element} if tagName2614* is a string or a more specific type if it is a member of2615* goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).2616* @template T2617* @template R := cond(isUnknown(T), 'Element', T) =:2618* @deprecated Use {@link goog.dom.DomHelper.prototype.createDom} instead.2619*/2620goog.dom.DomHelper.prototype.$dom = goog.dom.DomHelper.prototype.createDom;262126222623/**2624* Creates a new element.2625* @param {string|!goog.dom.TagName<T>} name Tag to create.2626* @return {R} The new element. The return type is {!Element} if name is2627* a string or a more specific type if it is a member of goog.dom.TagName2628* (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).2629* @template T2630* @template R := cond(isUnknown(T), 'Element', T) =:2631*/2632goog.dom.DomHelper.prototype.createElement = function(name) {2633return goog.dom.createElement_(this.document_, name);2634};263526362637/**2638* Creates a new text node.2639* @param {number|string} content Content.2640* @return {!Text} The new text node.2641*/2642goog.dom.DomHelper.prototype.createTextNode = function(content) {2643return this.document_.createTextNode(String(content));2644};264526462647/**2648* Create a table.2649* @param {number} rows The number of rows in the table. Must be >= 1.2650* @param {number} columns The number of columns in the table. Must be >= 1.2651* @param {boolean=} opt_fillWithNbsp If true, fills table entries with2652* {@code goog.string.Unicode.NBSP} characters.2653* @return {!HTMLElement} The created table.2654*/2655goog.dom.DomHelper.prototype.createTable = function(2656rows, columns, opt_fillWithNbsp) {2657return goog.dom.createTable_(2658this.document_, rows, columns, !!opt_fillWithNbsp);2659};266026612662/**2663* Converts an HTML into a node or a document fragment. A single Node is used if2664* {@code html} only generates a single node. If {@code html} generates multiple2665* nodes then these are put inside a {@code DocumentFragment}. This is a safe2666* version of {@code goog.dom.DomHelper#htmlToDocumentFragment} which is now2667* deleted.2668* @param {!goog.html.SafeHtml} html The HTML markup to convert.2669* @return {!Node} The resulting node.2670*/2671goog.dom.DomHelper.prototype.safeHtmlToNode = function(html) {2672return goog.dom.safeHtmlToNode_(this.document_, html);2673};267426752676/**2677* Returns true if the browser is in "CSS1-compatible" (standards-compliant)2678* mode, false otherwise.2679* @return {boolean} True if in CSS1-compatible mode.2680*/2681goog.dom.DomHelper.prototype.isCss1CompatMode = function() {2682return goog.dom.isCss1CompatMode_(this.document_);2683};268426852686/**2687* Gets the window object associated with the document.2688* @return {!Window} The window associated with the given document.2689*/2690goog.dom.DomHelper.prototype.getWindow = function() {2691return goog.dom.getWindow_(this.document_);2692};269326942695/**2696* Gets the document scroll element.2697* @return {!Element} Scrolling element.2698*/2699goog.dom.DomHelper.prototype.getDocumentScrollElement = function() {2700return goog.dom.getDocumentScrollElement_(this.document_);2701};270227032704/**2705* Gets the document scroll distance as a coordinate object.2706* @return {!goog.math.Coordinate} Object with properties 'x' and 'y'.2707*/2708goog.dom.DomHelper.prototype.getDocumentScroll = function() {2709return goog.dom.getDocumentScroll_(this.document_);2710};271127122713/**2714* Determines the active element in the given document.2715* @param {Document=} opt_doc The document to look in.2716* @return {Element} The active element.2717*/2718goog.dom.DomHelper.prototype.getActiveElement = function(opt_doc) {2719return goog.dom.getActiveElement(opt_doc || this.document_);2720};272127222723/**2724* Appends a child to a node.2725* @param {Node} parent Parent.2726* @param {Node} child Child.2727*/2728goog.dom.DomHelper.prototype.appendChild = goog.dom.appendChild;272927302731/**2732* Appends a node with text or other nodes.2733* @param {!Node} parent The node to append nodes to.2734* @param {...goog.dom.Appendable} var_args The things to append to the node.2735* If this is a Node it is appended as is.2736* If this is a string then a text node is appended.2737* If this is an array like object then fields 0 to length - 1 are appended.2738*/2739goog.dom.DomHelper.prototype.append = goog.dom.append;274027412742/**2743* Determines if the given node can contain children, intended to be used for2744* HTML generation.2745*2746* @param {Node} node The node to check.2747* @return {boolean} Whether the node can contain children.2748*/2749goog.dom.DomHelper.prototype.canHaveChildren = goog.dom.canHaveChildren;275027512752/**2753* Removes all the child nodes on a DOM node.2754* @param {Node} node Node to remove children from.2755*/2756goog.dom.DomHelper.prototype.removeChildren = goog.dom.removeChildren;275727582759/**2760* Inserts a new node before an existing reference node (i.e., as the previous2761* sibling). If the reference node has no parent, then does nothing.2762* @param {Node} newNode Node to insert.2763* @param {Node} refNode Reference node to insert before.2764*/2765goog.dom.DomHelper.prototype.insertSiblingBefore = goog.dom.insertSiblingBefore;276627672768/**2769* Inserts a new node after an existing reference node (i.e., as the next2770* sibling). If the reference node has no parent, then does nothing.2771* @param {Node} newNode Node to insert.2772* @param {Node} refNode Reference node to insert after.2773*/2774goog.dom.DomHelper.prototype.insertSiblingAfter = goog.dom.insertSiblingAfter;277527762777/**2778* Insert a child at a given index. If index is larger than the number of child2779* nodes that the parent currently has, the node is inserted as the last child2780* node.2781* @param {Element} parent The element into which to insert the child.2782* @param {Node} child The element to insert.2783* @param {number} index The index at which to insert the new child node. Must2784* not be negative.2785*/2786goog.dom.DomHelper.prototype.insertChildAt = goog.dom.insertChildAt;278727882789/**2790* Removes a node from its parent.2791* @param {Node} node The node to remove.2792* @return {Node} The node removed if removed; else, null.2793*/2794goog.dom.DomHelper.prototype.removeNode = goog.dom.removeNode;279527962797/**2798* Replaces a node in the DOM tree. Will do nothing if {@code oldNode} has no2799* parent.2800* @param {Node} newNode Node to insert.2801* @param {Node} oldNode Node to replace.2802*/2803goog.dom.DomHelper.prototype.replaceNode = goog.dom.replaceNode;280428052806/**2807* Flattens an element. That is, removes it and replace it with its children.2808* @param {Element} element The element to flatten.2809* @return {Element|undefined} The original element, detached from the document2810* tree, sans children, or undefined if the element was already not in the2811* document.2812*/2813goog.dom.DomHelper.prototype.flattenElement = goog.dom.flattenElement;281428152816/**2817* Returns an array containing just the element children of the given element.2818* @param {Element} element The element whose element children we want.2819* @return {!(Array<!Element>|NodeList<!Element>)} An array or array-like list2820* of just the element children of the given element.2821*/2822goog.dom.DomHelper.prototype.getChildren = goog.dom.getChildren;282328242825/**2826* Returns the first child node that is an element.2827* @param {Node} node The node to get the first child element of.2828* @return {Element} The first child node of {@code node} that is an element.2829*/2830goog.dom.DomHelper.prototype.getFirstElementChild =2831goog.dom.getFirstElementChild;283228332834/**2835* Returns the last child node that is an element.2836* @param {Node} node The node to get the last child element of.2837* @return {Element} The last child node of {@code node} that is an element.2838*/2839goog.dom.DomHelper.prototype.getLastElementChild = goog.dom.getLastElementChild;284028412842/**2843* Returns the first next sibling that is an element.2844* @param {Node} node The node to get the next sibling element of.2845* @return {Element} The next sibling of {@code node} that is an element.2846*/2847goog.dom.DomHelper.prototype.getNextElementSibling =2848goog.dom.getNextElementSibling;284928502851/**2852* Returns the first previous sibling that is an element.2853* @param {Node} node The node to get the previous sibling element of.2854* @return {Element} The first previous sibling of {@code node} that is2855* an element.2856*/2857goog.dom.DomHelper.prototype.getPreviousElementSibling =2858goog.dom.getPreviousElementSibling;285928602861/**2862* Returns the next node in source order from the given node.2863* @param {Node} node The node.2864* @return {Node} The next node in the DOM tree, or null if this was the last2865* node.2866*/2867goog.dom.DomHelper.prototype.getNextNode = goog.dom.getNextNode;286828692870/**2871* Returns the previous node in source order from the given node.2872* @param {Node} node The node.2873* @return {Node} The previous node in the DOM tree, or null if this was the2874* first node.2875*/2876goog.dom.DomHelper.prototype.getPreviousNode = goog.dom.getPreviousNode;287728782879/**2880* Whether the object looks like a DOM node.2881* @param {?} obj The object being tested for node likeness.2882* @return {boolean} Whether the object looks like a DOM node.2883*/2884goog.dom.DomHelper.prototype.isNodeLike = goog.dom.isNodeLike;288528862887/**2888* Whether the object looks like an Element.2889* @param {?} obj The object being tested for Element likeness.2890* @return {boolean} Whether the object looks like an Element.2891*/2892goog.dom.DomHelper.prototype.isElement = goog.dom.isElement;289328942895/**2896* Returns true if the specified value is a Window object. This includes the2897* global window for HTML pages, and iframe windows.2898* @param {?} obj Variable to test.2899* @return {boolean} Whether the variable is a window.2900*/2901goog.dom.DomHelper.prototype.isWindow = goog.dom.isWindow;290229032904/**2905* Returns an element's parent, if it's an Element.2906* @param {Element} element The DOM element.2907* @return {Element} The parent, or null if not an Element.2908*/2909goog.dom.DomHelper.prototype.getParentElement = goog.dom.getParentElement;291029112912/**2913* Whether a node contains another node.2914* @param {Node} parent The node that should contain the other node.2915* @param {Node} descendant The node to test presence of.2916* @return {boolean} Whether the parent node contains the descendent node.2917*/2918goog.dom.DomHelper.prototype.contains = goog.dom.contains;291929202921/**2922* Compares the document order of two nodes, returning 0 if they are the same2923* node, a negative number if node1 is before node2, and a positive number if2924* node2 is before node1. Note that we compare the order the tags appear in the2925* document so in the tree <b><i>text</i></b> the B node is considered to be2926* before the I node.2927*2928* @param {Node} node1 The first node to compare.2929* @param {Node} node2 The second node to compare.2930* @return {number} 0 if the nodes are the same node, a negative number if node12931* is before node2, and a positive number if node2 is before node1.2932*/2933goog.dom.DomHelper.prototype.compareNodeOrder = goog.dom.compareNodeOrder;293429352936/**2937* Find the deepest common ancestor of the given nodes.2938* @param {...Node} var_args The nodes to find a common ancestor of.2939* @return {Node} The common ancestor of the nodes, or null if there is none.2940* null will only be returned if two or more of the nodes are from different2941* documents.2942*/2943goog.dom.DomHelper.prototype.findCommonAncestor = goog.dom.findCommonAncestor;294429452946/**2947* Returns the owner document for a node.2948* @param {Node} node The node to get the document for.2949* @return {!Document} The document owning the node.2950*/2951goog.dom.DomHelper.prototype.getOwnerDocument = goog.dom.getOwnerDocument;295229532954/**2955* Cross browser function for getting the document element of an iframe.2956* @param {Element} iframe Iframe element.2957* @return {!Document} The frame content document.2958*/2959goog.dom.DomHelper.prototype.getFrameContentDocument =2960goog.dom.getFrameContentDocument;296129622963/**2964* Cross browser function for getting the window of a frame or iframe.2965* @param {Element} frame Frame element.2966* @return {Window} The window associated with the given frame.2967*/2968goog.dom.DomHelper.prototype.getFrameContentWindow =2969goog.dom.getFrameContentWindow;297029712972/**2973* Sets the text content of a node, with cross-browser support.2974* @param {Node} node The node to change the text content of.2975* @param {string|number} text The value that should replace the node's content.2976*/2977goog.dom.DomHelper.prototype.setTextContent = goog.dom.setTextContent;297829792980/**2981* Gets the outerHTML of a node, which islike innerHTML, except that it2982* actually contains the HTML of the node itself.2983* @param {Element} element The element to get the HTML of.2984* @return {string} The outerHTML of the given element.2985*/2986goog.dom.DomHelper.prototype.getOuterHtml = goog.dom.getOuterHtml;298729882989/**2990* Finds the first descendant node that matches the filter function. This does2991* a depth first search.2992* @param {Node} root The root of the tree to search.2993* @param {function(Node) : boolean} p The filter function.2994* @return {Node|undefined} The found node or undefined if none is found.2995*/2996goog.dom.DomHelper.prototype.findNode = goog.dom.findNode;299729982999/**3000* Finds all the descendant nodes that matches the filter function. This does a3001* depth first search.3002* @param {Node} root The root of the tree to search.3003* @param {function(Node) : boolean} p The filter function.3004* @return {Array<Node>} The found nodes or an empty array if none are found.3005*/3006goog.dom.DomHelper.prototype.findNodes = goog.dom.findNodes;300730083009/**3010* Returns true if the element has a tab index that allows it to receive3011* keyboard focus (tabIndex >= 0), false otherwise. Note that some elements3012* natively support keyboard focus, even if they have no tab index.3013* @param {!Element} element Element to check.3014* @return {boolean} Whether the element has a tab index that allows keyboard3015* focus.3016*/3017goog.dom.DomHelper.prototype.isFocusableTabIndex = goog.dom.isFocusableTabIndex;301830193020/**3021* Enables or disables keyboard focus support on the element via its tab index.3022* Only elements for which {@link goog.dom.isFocusableTabIndex} returns true3023* (or elements that natively support keyboard focus, like form elements) can3024* receive keyboard focus. See http://go/tabindex for more info.3025* @param {Element} element Element whose tab index is to be changed.3026* @param {boolean} enable Whether to set or remove a tab index on the element3027* that supports keyboard focus.3028*/3029goog.dom.DomHelper.prototype.setFocusableTabIndex =3030goog.dom.setFocusableTabIndex;303130323033/**3034* Returns true if the element can be focused, i.e. it has a tab index that3035* allows it to receive keyboard focus (tabIndex >= 0), or it is an element3036* that natively supports keyboard focus.3037* @param {!Element} element Element to check.3038* @return {boolean} Whether the element allows keyboard focus.3039*/3040goog.dom.DomHelper.prototype.isFocusable = goog.dom.isFocusable;304130423043/**3044* Returns the text contents of the current node, without markup. New lines are3045* stripped and whitespace is collapsed, such that each character would be3046* visible.3047*3048* In browsers that support it, innerText is used. Other browsers attempt to3049* simulate it via node traversal. Line breaks are canonicalized in IE.3050*3051* @param {Node} node The node from which we are getting content.3052* @return {string} The text content.3053*/3054goog.dom.DomHelper.prototype.getTextContent = goog.dom.getTextContent;305530563057/**3058* Returns the text length of the text contained in a node, without markup. This3059* is equivalent to the selection length if the node was selected, or the number3060* of cursor movements to traverse the node. Images & BRs take one space. New3061* lines are ignored.3062*3063* @param {Node} node The node whose text content length is being calculated.3064* @return {number} The length of {@code node}'s text content.3065*/3066goog.dom.DomHelper.prototype.getNodeTextLength = goog.dom.getNodeTextLength;306730683069/**3070* Returns the text offset of a node relative to one of its ancestors. The text3071* length is the same as the length calculated by3072* {@code goog.dom.getNodeTextLength}.3073*3074* @param {Node} node The node whose offset is being calculated.3075* @param {Node=} opt_offsetParent Defaults to the node's owner document's body.3076* @return {number} The text offset.3077*/3078goog.dom.DomHelper.prototype.getNodeTextOffset = goog.dom.getNodeTextOffset;307930803081/**3082* Returns the node at a given offset in a parent node. If an object is3083* provided for the optional third parameter, the node and the remainder of the3084* offset will stored as properties of this object.3085* @param {Node} parent The parent node.3086* @param {number} offset The offset into the parent node.3087* @param {Object=} opt_result Object to be used to store the return value. The3088* return value will be stored in the form {node: Node, remainder: number}3089* if this object is provided.3090* @return {Node} The node at the given offset.3091*/3092goog.dom.DomHelper.prototype.getNodeAtOffset = goog.dom.getNodeAtOffset;309330943095/**3096* Returns true if the object is a {@code NodeList}. To qualify as a NodeList,3097* the object must have a numeric length property and an item function (which3098* has type 'string' on IE for some reason).3099* @param {Object} val Object to test.3100* @return {boolean} Whether the object is a NodeList.3101*/3102goog.dom.DomHelper.prototype.isNodeList = goog.dom.isNodeList;310331043105/**3106* Walks up the DOM hierarchy returning the first ancestor that has the passed3107* tag name and/or class name. If the passed element matches the specified3108* criteria, the element itself is returned.3109* @param {Node} element The DOM node to start with.3110* @param {?(goog.dom.TagName<T>|string)=} opt_tag The tag name to match (or3111* null/undefined to match only based on class name).3112* @param {?string=} opt_class The class name to match (or null/undefined to3113* match only based on tag name).3114* @param {number=} opt_maxSearchSteps Maximum number of levels to search up the3115* dom.3116* @return {?R} The first ancestor that matches the passed criteria, or3117* null if no match is found. The return type is {?Element} if opt_tag is3118* not a member of goog.dom.TagName or a more specific type if it is (e.g.3119* {?HTMLAnchorElement} for goog.dom.TagName.A).3120* @template T3121* @template R := cond(isUnknown(T), 'Element', T) =:3122*/3123goog.dom.DomHelper.prototype.getAncestorByTagNameAndClass =3124goog.dom.getAncestorByTagNameAndClass;312531263127/**3128* Walks up the DOM hierarchy returning the first ancestor that has the passed3129* class name. If the passed element matches the specified criteria, the3130* element itself is returned.3131* @param {Node} element The DOM node to start with.3132* @param {string} class The class name to match.3133* @param {number=} opt_maxSearchSteps Maximum number of levels to search up the3134* dom.3135* @return {Element} The first ancestor that matches the passed criteria, or3136* null if none match.3137*/3138goog.dom.DomHelper.prototype.getAncestorByClass = goog.dom.getAncestorByClass;313931403141/**3142* Walks up the DOM hierarchy returning the first ancestor that passes the3143* matcher function.3144* @param {Node} element The DOM node to start with.3145* @param {function(Node) : boolean} matcher A function that returns true if the3146* passed node matches the desired criteria.3147* @param {boolean=} opt_includeNode If true, the node itself is included in3148* the search (the first call to the matcher will pass startElement as3149* the node to test).3150* @param {number=} opt_maxSearchSteps Maximum number of levels to search up the3151* dom.3152* @return {Node} DOM node that matched the matcher, or null if there was3153* no match.3154*/3155goog.dom.DomHelper.prototype.getAncestor = goog.dom.getAncestor;315631573158/**3159* Gets '2d' context of a canvas. Shortcut for canvas.getContext('2d') with a3160* type information.3161* @param {!HTMLCanvasElement} canvas3162* @return {!CanvasRenderingContext2D}3163*/3164goog.dom.DomHelper.prototype.getCanvasContext2D = goog.dom.getCanvasContext2D;316531663167