18ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Copyright 2006 Google Inc.
28ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen//
38ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Licensed under the Apache License, Version 2.0 (the "License");
48ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// you may not use this file except in compliance with the License.
58ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// You may obtain a copy of the License at
68ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen//
78ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// http://www.apache.org/licenses/LICENSE-2.0
88ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen//
98ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Unless required by applicable law or agreed to in writing, software
108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// distributed under the License is distributed on an "AS IS" BASIS,
118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// implied. See the License for the specific language governing
138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// permissions and limitations under the License.
148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @fileoverview Miscellaneous constants and functions referenced in
168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * the main source files.
178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @author Steffen Meschkat (mesch@google.com)
198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar MAPS_DEBUG = false;
228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction log(msg) {}
248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// String literals defined globally and not to be inlined. (IE6 perf)
268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** @const */ var STRING_empty = '';
278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** @const */ var CSS_display = 'display';
298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** @const */ var CSS_position = 'position';
308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Constants for possible values of the typeof operator.
328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar TYPE_boolean = 'boolean';
338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar TYPE_number = 'number';
348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar TYPE_object = 'object';
358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar TYPE_string = 'string';
368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar TYPE_function = 'function';
378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar TYPE_undefined = 'undefined';
388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Wrapper for the eval() builtin function to evaluate expressions and
428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * obtain their value. It wraps the expression in parentheses such
438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * that object literals are really evaluated to objects. Without the
448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * wrapping, they are evaluated as block, and create syntax
458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * errors. Also protects against other syntax errors in the eval()ed
468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * code and returns null if the eval throws an exception.
478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} expr
498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Object|null}
508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction jsEval(expr) {
528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  try {
538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // NOTE(mesch): An alternative idiom would be:
548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    //
558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    //   eval('(' + expr + ')');
568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    //
578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // Note that using the square brackets as below, "" evals to undefined.
588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // The alternative of using parentheses does not work when evaluating
598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // function literals in IE.
608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // e.g. eval("(function() {})") returns undefined, and not a function
618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    // object, in IE.
628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return eval('[' + expr + '][0]');
638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  } catch (e) {
648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    log('EVAL FAILED ' + expr + ': ' + e);
658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return null;
668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction jsLength(obj) {
708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return obj.length;
718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction assert(obj) {}
748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Copies all properties from second object to the first.  Modifies to.
778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object} to  The target object.
798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object} from  The source object.
808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction copyProperties(to, from) {
828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  for (var p in from) {
838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    to[p] = from[p];
848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object|null|undefined} value The possible value to use.
908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object} defaultValue The default if the value is not set.
918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Object} The value, if it is
928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * defined and not null; otherwise the default
938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction getDefaultObject(value, defaultValue) {
958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (typeof value != TYPE_undefined && value != null) {
968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return /** @type Object */(value);
978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  } else {
988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return defaultValue;
998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
1018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
1038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Detect if an object looks like an Array.
1048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Note that instanceof Array is not robust; for example an Array
1058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * created in another iframe fails instanceof Array.
1068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object|null} value Object to interrogate
1078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {boolean} Is the object an array?
1088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
1098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction isArray(value) {
1108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return value != null &&
1118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      typeof value == TYPE_object &&
1128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      typeof value.length == TYPE_number;
1138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
1148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
1178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Finds a slice of an array.
1188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
1198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Array} array  Array to be sliced.
1208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {number} start  The start of the slice.
1218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {number} opt_end  The end of the slice (optional).
1228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Array} array  The slice of the array from start to end.
1238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
1248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction arraySlice(array, start, opt_end) {
1258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Use
1268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  //   return Function.prototype.call.apply(Array.prototype.slice, arguments);
1278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // instead of the simpler
1288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  //   return Array.prototype.slice.call(array, start, opt_end);
1298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // here because of a bug in the FF and IE implementations of
1308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // Array.prototype.slice which causes this function to return an empty list
1318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // if opt_end is not provided.
1328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return Function.prototype.call.apply(Array.prototype.slice, arguments);
1338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
1348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
1378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Jscompiler wrapper for parseInt() with base 10.
1388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
1398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} s string repersentation of a number.
1408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
1418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {number} The integer contained in s, converted on base 10.
1428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
1438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction parseInt10(s) {
1448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return parseInt(s, 10);
1458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
1468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
1498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Clears the array by setting the length property to 0. This usually
1508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * works, and if it should turn out not to work everywhere, here would
1518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * be the place to implement the browser specific workaround.
1528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
1538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Array} array  Array to be cleared.
1548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
1558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction arrayClear(array) {
1568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  array.length = 0;
1578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
1588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
1618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Prebinds "this" within the given method to an object, but ignores all
1628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * arguments passed to the resulting function.
1638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * I.e. var_args are all the arguments that method is invoked with when
1648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * invoking the bound function.
1658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
1668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object|null} object  The object that the method call targets.
1678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Function} method  The target method.
1688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Function}  Method with the target object bound to it and curried by
1698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *                     the provided arguments.
1708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
1718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction bindFully(object, method, var_args) {
1728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  var args = arraySlice(arguments, 2);
1738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return function() {
1748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return method.apply(object, args);
1758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
1768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
1778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Based on <http://www.w3.org/TR/2000/ REC-DOM-Level-2-Core-20001113/
1798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// core.html#ID-1950641247>.
1808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar DOM_ELEMENT_NODE = 1;
1818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar DOM_ATTRIBUTE_NODE = 2;
1828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar DOM_TEXT_NODE = 3;
1838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar DOM_CDATA_SECTION_NODE = 4;
1848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar DOM_ENTITY_REFERENCE_NODE = 5;
1858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar DOM_ENTITY_NODE = 6;
1868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar DOM_PROCESSING_INSTRUCTION_NODE = 7;
1878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar DOM_COMMENT_NODE = 8;
1888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar DOM_DOCUMENT_NODE = 9;
1898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar DOM_DOCUMENT_TYPE_NODE = 10;
1908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar DOM_DOCUMENT_FRAGMENT_NODE = 11;
1918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar DOM_NOTATION_NODE = 12;
1928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domGetElementById(document, id) {
1968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return document.getElementById(id);
1978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
1988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
1998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
2008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Creates a new node in the given document
2018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
2028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Document} doc  Target document.
2038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} name  Name of new element (i.e. the tag name)..
2048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Element}  Newly constructed element.
2058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
2068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domCreateElement(doc, name) {
2078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return doc.createElement(name);
2088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
2098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
2118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Traverses the element nodes in the DOM section underneath the given
2128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * node and invokes the given callback as a method on every element
2138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * node encountered.
2148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
2158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} node  Parent element of the subtree to traverse.
2168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Function} callback  Called on each node in the traversal.
2178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
2188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domTraverseElements(node, callback) {
2198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  var traverser = new DomTraverser(callback);
2208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  traverser.run(node);
2218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
2228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
2248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * A class to hold state for a dom traversal.
2258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Function} callback  Called on each node in the traversal.
2268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @constructor
2278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @class
2288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
2298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction DomTraverser(callback) {
2308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  this.callback_ = callback;
2318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
2328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
2348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Processes the dom tree in breadth-first order.
2358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} root  The root node of the traversal.
2368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
2378ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenDomTraverser.prototype.run = function(root) {
2388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  var me = this;
2398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  me.queue_ = [ root ];
2408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  while (jsLength(me.queue_)) {
2418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    me.process_(me.queue_.shift());
2428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
2448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
2468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Processes a single node.
2478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} node  The current node of the traversal.
2488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
2498ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenDomTraverser.prototype.process_ = function(node) {
2508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  var me = this;
2518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  me.callback_(node);
2538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  for (var c = node.firstChild; c; c = c.nextSibling) {
2558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    if (c.nodeType == DOM_ELEMENT_NODE) {
2568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen      me.queue_.push(c);
2578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    }
2588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
2598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
2608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
2628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Get an attribute from the DOM.  Simple redirect, exists to compress code.
2638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
2648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} node  Element to interrogate.
2658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} name  Name of parameter to extract.
2668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {string|null}  Resulting attribute.
2678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
2688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domGetAttribute(node, name) {
2698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return node.getAttribute(name);
2708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // NOTE(mesch): Neither in IE nor in Firefox, HTML DOM attributes
2718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // implement namespaces. All items in the attribute collection have
2728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // null localName and namespaceURI attribute values. In IE, we even
2738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // encounter DIV elements that don't implement the method
2748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // getAttributeNS().
2758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
2768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
2798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Set an attribute in the DOM.  Simple redirect to compress code.
2808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
2818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} node  Element to interrogate.
2828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} name  Name of parameter to set.
2838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string|number} value  Set attribute to this value.
2848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
2858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domSetAttribute(node, name, value) {
2868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  node.setAttribute(name, value);
2878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
2888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
2908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Remove an attribute from the DOM.  Simple redirect to compress code.
2918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
2928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} node  Element to interrogate.
2938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} name  Name of parameter to remove.
2948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
2958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domRemoveAttribute(node, name) {
2968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  node.removeAttribute(name);
2978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
2988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
2998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
3008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Clone a node in the DOM.
3018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
3028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Node} node  Node to clone.
3038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Node}  Cloned node.
3048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
3058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domCloneNode(node) {
3068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return node.cloneNode(true);
3078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // NOTE(mesch): we never so far wanted to use cloneNode(false),
3088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  // hence the default.
3098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
3108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
3128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Clone a element in the DOM.
3138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
3148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} element  Element to clone.
3158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Element}  Cloned element.
3168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
3178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domCloneElement(element) {
3188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return /** @type {Element} */(domCloneNode(element));
3198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
3208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
3228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Returns the document owner of the given element. In particular,
3238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * returns window.document if node is null or the browser does not
3248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * support ownerDocument.  If the node is a document itself, returns
3258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * itself.
3268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
3278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Node|null|undefined} node  The node whose ownerDocument is required.
3288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @returns {Document}  The owner document or window.document if unsupported.
3298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
3308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction ownerDocument(node) {
3318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  if (!node) {
3328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return document;
3338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  } else if (node.nodeType == DOM_DOCUMENT_NODE) {
3348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return /** @type Document */(node);
3358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  } else {
3368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen    return node.ownerDocument || document;
3378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  }
3388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
3398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
3418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Creates a new text node in the given document.
3428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
3438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Document} doc  Target document.
3448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} text  Text composing new text node.
3458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Text}  Newly constructed text node.
3468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
3478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domCreateTextNode(doc, text) {
3488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return doc.createTextNode(text);
3498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
3508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
3528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Appends a new child to the specified (parent) node.
3538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
3548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} node  Parent element.
3558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Node} child  Child node to append.
3568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Node}  Newly appended node.
3578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
3588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domAppendChild(node, child) {
3598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return node.appendChild(child);
3608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
3618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
3638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Sets display to default.
3648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
3658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} node  The dom element to manipulate.
3668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
3678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction displayDefault(node) {
3688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  node.style[CSS_display] = '';
3698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
3708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
3728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Sets display to none. Doing this as a function saves a few bytes for
3738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * the 'style.display' property and the 'none' literal.
3748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
3758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} node  The dom element to manipulate.
3768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
3778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction displayNone(node) {
3788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  node.style[CSS_display] = 'none';
3798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
3808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
3838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Sets position style attribute to absolute.
3848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
3858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} node  The dom element to manipulate.
3868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
3878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction positionAbsolute(node) {
3888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  node.style[CSS_position] = 'absolute';
3898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
3908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
3928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
3938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Inserts a new child before a given sibling.
3948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
3958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Node} newChild  Node to insert.
3968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Node} oldChild  Sibling node.
3978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Node}  Reference to new child.
3988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
3998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domInsertBefore(newChild, oldChild) {
4008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return oldChild.parentNode.insertBefore(newChild, oldChild);
4018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
4028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
4048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Replaces an old child node with a new child node.
4058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
4068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Node} newChild  New child to append.
4078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Node} oldChild  Old child to remove.
4088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Node}  Replaced node.
4098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
4108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domReplaceChild(newChild, oldChild) {
4118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return oldChild.parentNode.replaceChild(newChild, oldChild);
4128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
4138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
4158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Removes a node from the DOM.
4168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
4178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Node} node  The node to remove.
4188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Node}  The removed node.
4198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
4208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domRemoveNode(node) {
4218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return domRemoveChild(node.parentNode, node);
4228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
4238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
4258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Remove a child from the specified (parent) node.
4268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
4278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} node  Parent element.
4288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Node} child  Child node to remove.
4298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Node}  Removed node.
4308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
4318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction domRemoveChild(node, child) {
4328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return node.removeChild(child);
4338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
4348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
4378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Trim whitespace from begin and end of string.
4388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
4398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @see testStringTrim();
4408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
4418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} str  Input string.
4428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {string}  Trimmed string.
4438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
4448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction stringTrim(str) {
4458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return stringTrimRight(stringTrimLeft(str));
4468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
4478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
4498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Trim whitespace from beginning of string.
4508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
4518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @see testStringTrimLeft();
4528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
4538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} str  Input string.
4548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {string}  Trimmed string.
4558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */
4568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction stringTrimLeft(str) {
4578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return str.replace(/^\s+/, "");
4588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
4598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
4608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/**
4618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Trim whitespace from end of string.
4628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
4638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @see testStringTrimRight();
4648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen *
4658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} str  Input string.
4668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {string}  Trimmed string.
4678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  */
4688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction stringTrimRight(str) {
4698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen  return str.replace(/\s+$/, "");
4708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}
4718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen
472