| OLD | NEW |
| 1 // Copyright 2005-2006 Google Inc. All Rights Reserved. | 1 // Copyright 2006 Google Inc. |
| 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at |
| 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| 12 // implied. See the License for the specific language governing |
| 13 // permissions and limitations under the License. |
| 2 /** | 14 /** |
| 3 * @fileoverview This file contains javascript utility functions that | 15 * @fileoverview Miscellaneous constants and functions referenced in |
| 4 * do not depend on anything defined elsewhere. | 16 * the main source files. |
| 5 * | |
| 6 */ | 17 */ |
| 7 | 18 |
| 19 function log(msg) {} |
| 20 |
| 21 // String literals defined globally and not to be inlined. (IE6 perf) |
| 22 /** @const */ var STRING_empty = ''; |
| 23 |
| 24 /** @const */ var CSS_display = 'display'; |
| 25 /** @const */ var CSS_position = 'position'; |
| 26 |
| 27 // Constants for possible values of the typeof operator. |
| 28 var TYPE_boolean = 'boolean'; |
| 29 var TYPE_number = 'number'; |
| 30 var TYPE_object = 'object'; |
| 31 var TYPE_string = 'string'; |
| 32 var TYPE_function = 'function'; |
| 33 var TYPE_undefined = 'undefined'; |
| 34 |
| 35 |
| 8 /** | 36 /** |
| 9 * Returns the value of the length property of the given object. Used | 37 * Wrapper for the eval() builtin function to evaluate expressions and |
| 10 * to reduce compiled code size. | 38 * obtain their value. It wraps the expression in parentheses such |
| 39 * that object literals are really evaluated to objects. Without the |
| 40 * wrapping, they are evaluated as block, and create syntax |
| 41 * errors. Also protects against other syntax errors in the eval()ed |
| 42 * code and returns null if the eval throws an exception. |
| 11 * | 43 * |
| 12 * @param {Array | String} a The string or array to interrogate. | 44 * @param {string} expr |
| 13 * @return {Number} The value of the length property. | 45 * @return {Object|null} |
| 14 */ | 46 */ |
| 15 function jsLength(a) { | 47 function jsEval(expr) { |
| 16 return a.length; | 48 try { |
| 49 // NOTE(mesch): An alternative idiom would be: |
| 50 // |
| 51 // eval('(' + expr + ')'); |
| 52 // |
| 53 // Note that using the square brackets as below, "" evals to undefined. |
| 54 // The alternative of using parentheses does not work when evaluating |
| 55 // function literals in IE. |
| 56 // e.g. eval("(function() {})") returns undefined, and not a function |
| 57 // object, in IE. |
| 58 return eval('[' + expr + '][0]'); |
| 59 } catch (e) { |
| 60 log('EVAL FAILED ' + expr + ': ' + e); |
| 61 return null; |
| 62 } |
| 17 } | 63 } |
| 18 | 64 |
| 19 // Wrappers for Math functions | 65 function jsLength(obj) { |
| 20 var min = Math.min; | 66 return obj.length; |
| 21 var max = Math.max; | 67 } |
| 22 var ceil = Math.ceil; | 68 |
| 23 var floor = Math.floor; | 69 function assert(obj) {} |
| 24 var round = Math.round; | |
| 25 var abs = Math.abs; | |
| 26 | 70 |
| 27 /** | 71 /** |
| 28 * Copies all properties from second object to the first. Modifies to. | 72 * Copies all properties from second object to the first. Modifies to. |
| 29 * | 73 * |
| 30 * @param {Object} to The target object. | 74 * @param {Object} to The target object. |
| 31 * @param {Object} from The source object. | 75 * @param {Object} from The source object. |
| 32 */ | 76 */ |
| 33 function copyProperties(to, from) { | 77 function copyProperties(to, from) { |
| 34 foreachin(from, function(p) { | 78 for (var p in from) { |
| 35 to[p] = from[p]; | 79 to[p] = from[p]; |
| 36 }); | 80 } |
| 37 } | 81 } |
| 38 | 82 |
| 39 /** | 83 |
| 40 * Iterates over the array, calling the given function for each | 84 /** |
| 41 * element. | 85 * @param {Object|null|undefined} value The possible value to use. |
| 42 * | 86 * @param {Object} defaultValue The default if the value is not set. |
| 43 * @param {Array} array | 87 * @return {Object} The value, if it is |
| 44 * @param {Function} fn | 88 * defined and not null; otherwise the default |
| 45 */ | 89 */ |
| 46 function foreach(array, fn) { | 90 function getDefaultObject(value, defaultValue) { |
| 47 var I = jsLength(array); | 91 if (typeof value != TYPE_undefined && value != null) { |
| 48 for (var i = 0; i < I; ++i) { | 92 return /** @type Object */(value); |
| 49 fn(array[i], i); | 93 } else { |
| 50 } | 94 return defaultValue; |
| 51 } | 95 } |
| 52 | 96 } |
| 53 /** | 97 |
| 54 * Safely iterates over all properties of the given object, calling | 98 /** |
| 55 * the given function for each property. If opt_all isn't true, uses | 99 * Detect if an object looks like an Array. |
| 56 * hasOwnProperty() to assure the property is on the object, not on | 100 * Note that instanceof Array is not robust; for example an Array |
| 57 * its prototype. | 101 * created in another iframe fails instanceof Array. |
| 58 * | 102 * @param {Object|null} value Object to interrogate |
| 59 * @param {Object} object | 103 * @return {boolean} Is the object an array? |
| 60 * @param {Function} fn | 104 */ |
| 61 * @param {Boolean} opt_all If true, also iterates over inherited properties. | 105 function isArray(value) { |
| 62 */ | 106 return value != null && |
| 63 function foreachin(object, fn, opt_all) { | 107 typeof value == TYPE_object && |
| 64 for (var i in object) { | 108 typeof value.length == TYPE_number; |
| 65 // NOTE: Safari/1.3 doesn't have hasOwnProperty(). In that | 109 } |
| 66 // case, we iterate over all properties as a very lame workaround. | 110 |
| 67 if (opt_all || !object.hasOwnProperty || object.hasOwnProperty(i)) { | 111 |
| 68 fn(i, object[i]); | 112 /** |
| 113 * Finds a slice of an array. |
| 114 * |
| 115 * @param {Array} array Array to be sliced. |
| 116 * @param {number} start The start of the slice. |
| 117 * @param {number} opt_end The end of the slice (optional). |
| 118 * @return {Array} array The slice of the array from start to end. |
| 119 */ |
| 120 function arraySlice(array, start, opt_end) { |
| 121 // Use |
| 122 // return Function.prototype.call.apply(Array.prototype.slice, arguments); |
| 123 // instead of the simpler |
| 124 // return Array.prototype.slice.call(array, start, opt_end); |
| 125 // here because of a bug in the FF and IE implementations of |
| 126 // Array.prototype.slice which causes this function to return an empty list |
| 127 // if opt_end is not provided. |
| 128 return Function.prototype.call.apply(Array.prototype.slice, arguments); |
| 129 } |
| 130 |
| 131 |
| 132 /** |
| 133 * Jscompiler wrapper for parseInt() with base 10. |
| 134 * |
| 135 * @param {string} s string repersentation of a number. |
| 136 * |
| 137 * @return {number} The integer contained in s, converted on base 10. |
| 138 */ |
| 139 function parseInt10(s) { |
| 140 return parseInt(s, 10); |
| 141 } |
| 142 |
| 143 |
| 144 /** |
| 145 * Clears the array by setting the length property to 0. This usually |
| 146 * works, and if it should turn out not to work everywhere, here would |
| 147 * be the place to implement the browser specific workaround. |
| 148 * |
| 149 * @param {Array} array Array to be cleared. |
| 150 */ |
| 151 function arrayClear(array) { |
| 152 array.length = 0; |
| 153 } |
| 154 |
| 155 |
| 156 /** |
| 157 * Prebinds "this" within the given method to an object, but ignores all |
| 158 * arguments passed to the resulting function. |
| 159 * I.e. var_args are all the arguments that method is invoked with when |
| 160 * invoking the bound function. |
| 161 * |
| 162 * @param {Object|null} object The object that the method call targets. |
| 163 * @param {Function} method The target method. |
| 164 * @return {Function} Method with the target object bound to it and curried by |
| 165 * the provided arguments. |
| 166 */ |
| 167 function bindFully(object, method, var_args) { |
| 168 var args = arraySlice(arguments, 2); |
| 169 return function() { |
| 170 return method.apply(object, args); |
| 171 } |
| 172 } |
| 173 |
| 174 // Based on <http://www.w3.org/TR/2000/ REC-DOM-Level-2-Core-20001113/ |
| 175 // core.html#ID-1950641247>. |
| 176 var DOM_ELEMENT_NODE = 1; |
| 177 var DOM_ATTRIBUTE_NODE = 2; |
| 178 var DOM_TEXT_NODE = 3; |
| 179 var DOM_CDATA_SECTION_NODE = 4; |
| 180 var DOM_ENTITY_REFERENCE_NODE = 5; |
| 181 var DOM_ENTITY_NODE = 6; |
| 182 var DOM_PROCESSING_INSTRUCTION_NODE = 7; |
| 183 var DOM_COMMENT_NODE = 8; |
| 184 var DOM_DOCUMENT_NODE = 9; |
| 185 var DOM_DOCUMENT_TYPE_NODE = 10; |
| 186 var DOM_DOCUMENT_FRAGMENT_NODE = 11; |
| 187 var DOM_NOTATION_NODE = 12; |
| 188 |
| 189 |
| 190 |
| 191 function domGetElementById(document, id) { |
| 192 return document.getElementById(id); |
| 193 } |
| 194 |
| 195 /** |
| 196 * Creates a new node in the given document |
| 197 * |
| 198 * @param {Document} doc Target document. |
| 199 * @param {string} name Name of new element (i.e. the tag name).. |
| 200 * @return {Element} Newly constructed element. |
| 201 */ |
| 202 function domCreateElement(doc, name) { |
| 203 return doc.createElement(name); |
| 204 } |
| 205 |
| 206 /** |
| 207 * Traverses the element nodes in the DOM section underneath the given |
| 208 * node and invokes the given callback as a method on every element |
| 209 * node encountered. |
| 210 * |
| 211 * @param {Element} node Parent element of the subtree to traverse. |
| 212 * @param {Function} callback Called on each node in the traversal. |
| 213 */ |
| 214 function domTraverseElements(node, callback) { |
| 215 var traverser = new DomTraverser(callback); |
| 216 traverser.run(node); |
| 217 } |
| 218 |
| 219 /** |
| 220 * A class to hold state for a dom traversal. |
| 221 * @param {Function} callback Called on each node in the traversal. |
| 222 * @constructor |
| 223 * @class |
| 224 */ |
| 225 function DomTraverser(callback) { |
| 226 this.callback_ = callback; |
| 227 } |
| 228 |
| 229 /** |
| 230 * Processes the dom tree in breadth-first order. |
| 231 * @param {Element} root The root node of the traversal. |
| 232 */ |
| 233 DomTraverser.prototype.run = function(root) { |
| 234 var me = this; |
| 235 me.queue_ = [ root ]; |
| 236 while (jsLength(me.queue_)) { |
| 237 me.process_(me.queue_.shift()); |
| 238 } |
| 239 } |
| 240 |
| 241 /** |
| 242 * Processes a single node. |
| 243 * @param {Element} node The current node of the traversal. |
| 244 */ |
| 245 DomTraverser.prototype.process_ = function(node) { |
| 246 var me = this; |
| 247 |
| 248 me.callback_(node); |
| 249 |
| 250 for (var c = node.firstChild; c; c = c.nextSibling) { |
| 251 if (c.nodeType == DOM_ELEMENT_NODE) { |
| 252 me.queue_.push(c); |
| 69 } | 253 } |
| 70 } | 254 } |
| 71 } | 255 } |
| 72 | 256 |
| 73 /** | 257 /** |
| 74 * Appends the second array to the first, copying its elements. | 258 * Get an attribute from the DOM. Simple redirect, exists to compress code. |
| 75 * Optionally only a slice of the second array is copied. | 259 * |
| 76 * | 260 * @param {Element} node Element to interrogate. |
| 77 * @param {Array} a1 Target array (modified). | 261 * @param {string} name Name of parameter to extract. |
| 78 * @param {Array} a2 Source array. | 262 * @return {string|null} Resulting attribute. |
| 79 * @param {Number} opt_begin Begin of slice of second array (optional). | 263 */ |
| 80 * @param {Number} opt_end End (exclusive) of slice of second array (optional). | 264 function domGetAttribute(node, name) { |
| 81 */ | 265 return node.getAttribute(name); |
| 82 function arrayAppend(a1, a2, opt_begin, opt_end) { | 266 // NOTE(mesch): Neither in IE nor in Firefox, HTML DOM attributes |
| 83 var i0 = opt_begin || 0; | 267 // implement namespaces. All items in the attribute collection have |
| 84 var i1 = opt_end || jsLength(a2); | 268 // null localName and namespaceURI attribute values. In IE, we even |
| 85 for (var i = i0; i < i1; ++i) { | 269 // encounter DIV elements that don't implement the method |
| 86 a1.push(a2[i]); | 270 // getAttributeNS(). |
| 87 } | 271 } |
| 88 } | 272 |
| 273 |
| 274 /** |
| 275 * Set an attribute in the DOM. Simple redirect to compress code. |
| 276 * |
| 277 * @param {Element} node Element to interrogate. |
| 278 * @param {string} name Name of parameter to set. |
| 279 * @param {string|number} value Set attribute to this value. |
| 280 */ |
| 281 function domSetAttribute(node, name, value) { |
| 282 node.setAttribute(name, value); |
| 283 } |
| 284 |
| 285 /** |
| 286 * Remove an attribute from the DOM. Simple redirect to compress code. |
| 287 * |
| 288 * @param {Element} node Element to interrogate. |
| 289 * @param {string} name Name of parameter to remove. |
| 290 */ |
| 291 function domRemoveAttribute(node, name) { |
| 292 node.removeAttribute(name); |
| 293 } |
| 294 |
| 295 /** |
| 296 * Clone a node in the DOM. |
| 297 * |
| 298 * @param {Node} node Node to clone. |
| 299 * @return {Node} Cloned node. |
| 300 */ |
| 301 function domCloneNode(node) { |
| 302 return node.cloneNode(true); |
| 303 // NOTE(mesch): we never so far wanted to use cloneNode(false), |
| 304 // hence the default. |
| 305 } |
| 306 |
| 307 /** |
| 308 * Clone a element in the DOM. |
| 309 * |
| 310 * @param {Element} element Element to clone. |
| 311 * @return {Element} Cloned element. |
| 312 */ |
| 313 function domCloneElement(element) { |
| 314 return /** @type {Element} */(domCloneNode(element)); |
| 315 } |
| 316 |
| 317 /** |
| 318 * Returns the document owner of the given element. In particular, |
| 319 * returns window.document if node is null or the browser does not |
| 320 * support ownerDocument. If the node is a document itself, returns |
| 321 * itself. |
| 322 * |
| 323 * @param {Node|null|undefined} node The node whose ownerDocument is required. |
| 324 * @returns {Document} The owner document or window.document if unsupported. |
| 325 */ |
| 326 function ownerDocument(node) { |
| 327 if (!node) { |
| 328 return document; |
| 329 } else if (node.nodeType == DOM_DOCUMENT_NODE) { |
| 330 return /** @type Document */(node); |
| 331 } else { |
| 332 return node.ownerDocument || document; |
| 333 } |
| 334 } |
| 335 |
| 336 /** |
| 337 * Creates a new text node in the given document. |
| 338 * |
| 339 * @param {Document} doc Target document. |
| 340 * @param {string} text Text composing new text node. |
| 341 * @return {Text} Newly constructed text node. |
| 342 */ |
| 343 function domCreateTextNode(doc, text) { |
| 344 return doc.createTextNode(text); |
| 345 } |
| 346 |
| 347 /** |
| 348 * Appends a new child to the specified (parent) node. |
| 349 * |
| 350 * @param {Element} node Parent element. |
| 351 * @param {Node} child Child node to append. |
| 352 * @return {Node} Newly appended node. |
| 353 */ |
| 354 function domAppendChild(node, child) { |
| 355 return node.appendChild(child); |
| 356 } |
| 357 |
| 358 /** |
| 359 * Sets display to default. |
| 360 * |
| 361 * @param {Element} node The dom element to manipulate. |
| 362 */ |
| 363 function displayDefault(node) { |
| 364 node.style[CSS_display] = ''; |
| 365 } |
| 366 |
| 367 /** |
| 368 * Sets display to none. Doing this as a function saves a few bytes for |
| 369 * the 'style.display' property and the 'none' literal. |
| 370 * |
| 371 * @param {Element} node The dom element to manipulate. |
| 372 */ |
| 373 function displayNone(node) { |
| 374 node.style[CSS_display] = 'none'; |
| 375 } |
| 376 |
| 377 |
| 378 /** |
| 379 * Sets position style attribute to absolute. |
| 380 * |
| 381 * @param {Element} node The dom element to manipulate. |
| 382 */ |
| 383 function positionAbsolute(node) { |
| 384 node.style[CSS_position] = 'absolute'; |
| 385 } |
| 386 |
| 387 |
| 388 /** |
| 389 * Inserts a new child before a given sibling. |
| 390 * |
| 391 * @param {Node} newChild Node to insert. |
| 392 * @param {Node} oldChild Sibling node. |
| 393 * @return {Node} Reference to new child. |
| 394 */ |
| 395 function domInsertBefore(newChild, oldChild) { |
| 396 return oldChild.parentNode.insertBefore(newChild, oldChild); |
| 397 } |
| 398 |
| 399 /** |
| 400 * Replaces an old child node with a new child node. |
| 401 * |
| 402 * @param {Node} newChild New child to append. |
| 403 * @param {Node} oldChild Old child to remove. |
| 404 * @return {Node} Replaced node. |
| 405 */ |
| 406 function domReplaceChild(newChild, oldChild) { |
| 407 return oldChild.parentNode.replaceChild(newChild, oldChild); |
| 408 } |
| 409 |
| 410 /** |
| 411 * Removes a node from the DOM. |
| 412 * |
| 413 * @param {Node} node The node to remove. |
| 414 * @return {Node} The removed node. |
| 415 */ |
| 416 function domRemoveNode(node) { |
| 417 return domRemoveChild(node.parentNode, node); |
| 418 } |
| 419 |
| 420 /** |
| 421 * Remove a child from the specified (parent) node. |
| 422 * |
| 423 * @param {Element} node Parent element. |
| 424 * @param {Node} child Child node to remove. |
| 425 * @return {Node} Removed node. |
| 426 */ |
| 427 function domRemoveChild(node, child) { |
| 428 return node.removeChild(child); |
| 429 } |
| 430 |
| 89 | 431 |
| 90 /** | 432 /** |
| 91 * Trim whitespace from begin and end of string. | 433 * Trim whitespace from begin and end of string. |
| 92 * | 434 * |
| 93 * @see testStringTrim(); | 435 * @see testStringTrim(); |
| 94 * | 436 * |
| 95 * @param {String} str Input string. | 437 * @param {string} str Input string. |
| 96 * @return {String} Trimmed string. | 438 * @return {string} Trimmed string. |
| 97 */ | 439 */ |
| 98 function stringTrim(str) { | 440 function stringTrim(str) { |
| 99 return stringTrimRight(stringTrimLeft(str)); | 441 return stringTrimRight(stringTrimLeft(str)); |
| 100 } | 442 } |
| 101 | 443 |
| 102 /** | 444 /** |
| 103 * Trim whitespace from beginning of string. | 445 * Trim whitespace from beginning of string. |
| 104 * | 446 * |
| 105 * @see testStringTrimLeft(); | 447 * @see testStringTrimLeft(); |
| 106 * | 448 * |
| 107 * @param {String} str Input string. | 449 * @param {string} str Input string. |
| 108 * @return {String} Trimmed string. | 450 * @return {string} Trimmed string. |
| 109 */ | 451 */ |
| 110 function stringTrimLeft(str) { | 452 function stringTrimLeft(str) { |
| 111 return str.replace(/^\s+/, ""); | 453 return str.replace(/^\s+/, ""); |
| 112 } | 454 } |
| 113 | 455 |
| 114 /** | 456 /** |
| 115 * Trim whitespace from end of string. | 457 * Trim whitespace from end of string. |
| 116 * | 458 * |
| 117 * @see testStringTrimRight(); | 459 * @see testStringTrimRight(); |
| 118 * | 460 * |
| 119 * @param {String} str Input string. | 461 * @param {string} str Input string. |
| 120 * @return {String} Trimmed string. | 462 * @return {string} Trimmed string. |
| 121 */ | 463 */ |
| 122 function stringTrimRight(str) { | 464 function stringTrimRight(str) { |
| 123 return str.replace(/\s+$/, ""); | 465 return str.replace(/\s+$/, ""); |
| 124 } | 466 } |
| 125 | |
| 126 /** | |
| 127 * Jscompiler wrapper for parseInt() with base 10. | |
| 128 * | |
| 129 * @param {String} s String repersentation of a number. | |
| 130 * | |
| 131 * @return {Number} The integer contained in s, converted on base 10. | |
| 132 */ | |
| 133 function parseInt10(s) { | |
| 134 return parseInt(s, 10); | |
| 135 } | |
| OLD | NEW |