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