OLD | NEW |
| (Empty) |
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 | |
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 | |
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 */ | |
47 function 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 return eval('[' + expr + '][0]'); | |
59 } catch (e) { | |
60 log('EVAL FAILED ' + expr + ': ' + e); | |
61 return null; | |
62 } | |
63 } | |
64 | |
65 function jsLength(obj) { | |
66 return obj.length; | |
67 } | |
68 | |
69 function assert(obj) {} | |
70 | |
71 /** | |
72 * Copies all properties from second object to the first. Modifies to. | |
73 * | |
74 * @param {Object} to The target object. | |
75 * @param {Object} from The source object. | |
76 */ | |
77 function copyProperties(to, from) { | |
78 for (var p in from) { | |
79 to[p] = from[p]; | |
80 } | |
81 } | |
82 | |
83 | |
84 /** | |
85 * @param {Object|null|undefined} value The possible value to use. | |
86 * @param {Object} defaultValue The default if the value is not set. | |
87 * @return {Object} The value, if it is | |
88 * defined and not null; otherwise the default | |
89 */ | |
90 function getDefaultObject(value, defaultValue) { | |
91 if (typeof value != TYPE_undefined && value != null) { | |
92 return /** @type Object */(value); | |
93 } else { | |
94 return defaultValue; | |
95 } | |
96 } | |
97 | |
98 /** | |
99 * Detect if an object looks like an Array. | |
100 * Note that instanceof Array is not robust; for example an Array | |
101 * created in another iframe fails instanceof Array. | |
102 * @param {Object|null} value Object to interrogate | |
103 * @return {boolean} Is the object an array? | |
104 */ | |
105 function isArray(value) { | |
106 return value != null && | |
107 typeof value == TYPE_object && | |
108 typeof value.length == TYPE_number; | |
109 } | |
110 | |
111 | |
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); | |
253 } | |
254 } | |
255 } | |
256 | |
257 /** | |
258 * Get an attribute from the DOM. Simple redirect, exists to compress code. | |
259 * | |
260 * @param {Element} node Element to interrogate. | |
261 * @param {string} name Name of parameter to extract. | |
262 * @return {string|null} Resulting attribute. | |
263 */ | |
264 function domGetAttribute(node, name) { | |
265 return node.getAttribute(name); | |
266 // NOTE(mesch): Neither in IE nor in Firefox, HTML DOM attributes | |
267 // implement namespaces. All items in the attribute collection have | |
268 // null localName and namespaceURI attribute values. In IE, we even | |
269 // encounter DIV elements that don't implement the method | |
270 // getAttributeNS(). | |
271 } | |
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 | |
431 | |
432 /** | |
433 * Trim whitespace from begin and end of string. | |
434 * | |
435 * @see testStringTrim(); | |
436 * | |
437 * @param {string} str Input string. | |
438 * @return {string} Trimmed string. | |
439 */ | |
440 function stringTrim(str) { | |
441 return stringTrimRight(stringTrimLeft(str)); | |
442 } | |
443 | |
444 /** | |
445 * Trim whitespace from beginning of string. | |
446 * | |
447 * @see testStringTrimLeft(); | |
448 * | |
449 * @param {string} str Input string. | |
450 * @return {string} Trimmed string. | |
451 */ | |
452 function stringTrimLeft(str) { | |
453 return str.replace(/^\s+/, ""); | |
454 } | |
455 | |
456 /** | |
457 * Trim whitespace from end of string. | |
458 * | |
459 * @see testStringTrimRight(); | |
460 * | |
461 * @param {string} str Input string. | |
462 * @return {string} Trimmed string. | |
463 */ | |
464 function stringTrimRight(str) { | |
465 return str.replace(/\s+$/, ""); | |
466 } | |
OLD | NEW |