1 // Copyright 2006 Google Inc.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 * @fileoverview Miscellaneous constants and functions referenced in
16 * the main source files.
21 // String literals defined globally and not to be inlined. (IE6 perf)
22 /** @const */ var STRING_empty
= '';
24 /** @const */ var CSS_display
= 'display';
25 /** @const */ var CSS_position
= 'position';
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';
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.
44 * @param {string} expr
45 * @return {Object|null}
47 function jsEval(expr
) {
49 // NOTE(mesch): An alternative idiom would be:
51 // eval('(' + expr + ')');
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
58 var result
= eval('[' + expr
+ '][0]');
59 if (typeof result
!= 'object') {
60 throw new Error('expression of type Object expected, ' +
61 typeof result
+ ' found');
63 return /** @type {Object} */(result
);
65 log('EVAL FAILED ' + expr
+ ': ' + e
);
70 function jsLength(obj
) {
75 * Copies all properties from second object to the first. Modifies to.
77 * @param {Object} to The target object.
78 * @param {Object} from The source object.
80 function copyProperties(to
, from) {
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.
92 function getDefaultObject(value
, defaultValue
) {
93 if (typeof value
!= TYPE_undefined
&& value
!= null) {
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?
107 function isArray(value
) {
108 return value
!= null &&
109 typeof value
== TYPE_object
&&
110 typeof value
.length
== TYPE_number
;
115 * Finds a slice of an array.
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.
122 function arraySlice(array
, start
, opt_end
) {
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
));
136 * Jscompiler wrapper for parseInt() with base 10.
138 * @param {string} s string repersentation of a number.
140 * @return {number} The integer contained in s, converted on base 10.
142 function parseInt10(s
) {
143 return parseInt(s
, 10);
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.
152 * @param {Array} array Array to be cleared.
154 function arrayClear(array
) {
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.
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.
171 function bindFully(object
, method
, var_args
) {
172 var args
= arraySlice(arguments
, 2);
174 return method
.apply(object
, args
);
178 // Based on <http://www.w3.org/TR/2000/ REC-DOM-Level-2-Core-20001113/
179 // core.html#ID-1950641247>.
180 var DOM_ELEMENT_NODE
= 1;
181 var DOM_ATTRIBUTE_NODE
= 2;
182 var DOM_TEXT_NODE
= 3;
183 var DOM_CDATA_SECTION_NODE
= 4;
184 var DOM_ENTITY_REFERENCE_NODE
= 5;
185 var DOM_ENTITY_NODE
= 6;
186 var DOM_PROCESSING_INSTRUCTION_NODE
= 7;
187 var DOM_COMMENT_NODE
= 8;
188 var DOM_DOCUMENT_NODE
= 9;
189 var DOM_DOCUMENT_TYPE_NODE
= 10;
190 var DOM_DOCUMENT_FRAGMENT_NODE
= 11;
191 var DOM_NOTATION_NODE
= 12;
195 function domGetElementById(document
, id
) {
196 return document
.getElementById(id
);
200 * Creates a new node in the given document
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.
206 function domCreateElement(doc
, name
) {
207 return doc
.createElement(name
);
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
215 * @param {Element} node Parent element of the subtree to traverse.
216 * @param {Function} callback Called on each node in the traversal.
218 function domTraverseElements(node
, callback
) {
219 var traverser
= new DomTraverser(callback
);
224 * A class to hold state for a dom traversal.
225 * @param {Function} callback Called on each node in the traversal.
229 function DomTraverser(callback
) {
230 this.callback_
= callback
;
234 * Processes the dom tree in breadth-first order.
235 * @param {Element} root The root node of the traversal.
237 DomTraverser
.prototype.run = function(root
) {
239 me
.queue_
= [ root
];
240 while (jsLength(me
.queue_
)) {
241 me
.process_(me
.queue_
.shift());
246 * Processes a single node.
247 * @param {Element} node The current node of the traversal.
249 DomTraverser
.prototype.process_ = function(node
) {
254 for (var c
= node
.firstChild
; c
; c
= c
.nextSibling
) {
255 if (c
.nodeType
== DOM_ELEMENT_NODE
) {
262 * Get an attribute from the DOM. Simple redirect, exists to compress code.
264 * @param {Element} node Element to interrogate.
265 * @param {string} name Name of parameter to extract.
266 * @return {string|null} Resulting attribute.
268 function 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
279 * Set an attribute in the DOM. Simple redirect to compress code.
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.
285 function domSetAttribute(node
, name
, value
) {
286 node
.setAttribute(name
, value
);
290 * Remove an attribute from the DOM. Simple redirect to compress code.
292 * @param {Element} node Element to interrogate.
293 * @param {string} name Name of parameter to remove.
295 function domRemoveAttribute(node
, name
) {
296 node
.removeAttribute(name
);
300 * Clone a node in the DOM.
302 * @param {Node} node Node to clone.
303 * @return {Node} Cloned node.
305 function domCloneNode(node
) {
306 return node
.cloneNode(true);
307 // NOTE(mesch): we never so far wanted to use cloneNode(false),
308 // hence the default.
312 * Clone a element in the DOM.
314 * @param {Element} element Element to clone.
315 * @return {Element} Cloned element.
317 function domCloneElement(element
) {
318 return /** @type {Element} */(domCloneNode(element
));
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
327 * @param {Node|null|undefined} node The node whose ownerDocument is required.
328 * @returns {Document} The owner document or window.document if unsupported.
330 function ownerDocument(node
) {
333 } else if (node
.nodeType
== DOM_DOCUMENT_NODE
) {
334 return /** @type Document */(node
);
336 return node
.ownerDocument
|| document
;
341 * Creates a new text node in the given document.
343 * @param {Document} doc Target document.
344 * @param {string} text Text composing new text node.
345 * @return {Text} Newly constructed text node.
347 function domCreateTextNode(doc
, text
) {
348 return doc
.createTextNode(text
);
352 * Appends a new child to the specified (parent) node.
354 * @param {Element} node Parent element.
355 * @param {Node} child Child node to append.
356 * @return {Node} Newly appended node.
358 function domAppendChild(node
, child
) {
359 return node
.appendChild(child
);
363 * Sets display to default.
365 * @param {Element} node The dom element to manipulate.
367 function displayDefault(node
) {
368 node
.style
[CSS_display
] = '';
372 * Sets display to none. Doing this as a function saves a few bytes for
373 * the 'style.display' property and the 'none' literal.
375 * @param {Element} node The dom element to manipulate.
377 function displayNone(node
) {
378 node
.style
[CSS_display
] = 'none';
383 * Sets position style attribute to absolute.
385 * @param {Element} node The dom element to manipulate.
387 function positionAbsolute(node
) {
388 node
.style
[CSS_position
] = 'absolute';
393 * Inserts a new child before a given sibling.
395 * @param {Node} newChild Node to insert.
396 * @param {Node} oldChild Sibling node.
397 * @return {Node} Reference to new child.
399 function domInsertBefore(newChild
, oldChild
) {
400 return oldChild
.parentNode
.insertBefore(newChild
, oldChild
);
404 * Replaces an old child node with a new child node.
406 * @param {Node} newChild New child to append.
407 * @param {Node} oldChild Old child to remove.
408 * @return {Node} Replaced node.
410 function domReplaceChild(newChild
, oldChild
) {
411 return oldChild
.parentNode
.replaceChild(newChild
, oldChild
);
415 * Removes a node from the DOM.
417 * @param {Node} node The node to remove.
418 * @return {Node} The removed node.
420 function domRemoveNode(node
) {
421 return domRemoveChild(node
.parentNode
, node
);
425 * Remove a child from the specified (parent) node.
427 * @param {Node} node Parent element.
428 * @param {Node} child Child node to remove.
429 * @return {Node} Removed node.
431 function domRemoveChild(node
, child
) {
432 return node
.removeChild(child
);
437 * Trim whitespace from begin and end of string.
439 * @see testStringTrim();
441 * @param {string} str Input string.
442 * @return {string} Trimmed string.
444 function stringTrim(str
) {
445 return stringTrimRight(stringTrimLeft(str
));
449 * Trim whitespace from beginning of string.
451 * @see testStringTrimLeft();
453 * @param {string} str Input string.
454 * @return {string} Trimmed string.
456 function stringTrimLeft(str
) {
457 return str
.replace(/^\s+/, "");
461 * Trim whitespace from end of string.
463 * @see testStringTrimRight();
465 * @param {string} str Input string.
466 * @return {string} Trimmed string.
468 function stringTrimRight(str
) {
469 return str
.replace(/\s+$/, "");