Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(287)

Side by Side Diff: chrome/test/data/extensions/samples/tabs/jstemplate_compiled.js

Issue 99201: Sample sprucing part 2 (Closed)
Patch Set: Merge to HEAD Created 11 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /**
2 * @fileoverview This file contains miscellaneous basic functionality.
3 *
4 */
5
6 /**
7 * Creates a DOM element with the given tag name in the document of the
8 * owner element.
9 *
10 * @param {String} tagName The name of the tag to create.
11 * @param {Element} owner The intended owner (i.e., parent element) of
12 * the created element.
13 * @param {Point} opt_position The top-left corner of the created element.
14 * @param {Size} opt_size The size of the created element.
15 * @param {Boolean} opt_noAppend Do not append the new element to the owner.
16 * @return {Element} The newly created element node.
17 */
18 function createElement(tagName, owner, opt_position, opt_size, opt_noAppend) {
19 var element = ownerDocument(owner).createElement(tagName);
20 if (opt_position) {
21 setPosition(element, opt_position);
22 }
23 if (opt_size) {
24 setSize(element, opt_size);
25 }
26 if (owner && !opt_noAppend) {
27 appendChild(owner, element);
28 }
29
30 return element;
31 }
32
33 /**
34 * Creates a text node with the given value.
35 *
36 * @param {String} value The text to place in the new node.
37 * @param {Element} owner The owner (i.e., parent element) of the new
38 * text node.
39 * @return {Text} The newly created text node.
40 */
41 function createTextNode(value, owner) {
42 var element = ownerDocument(owner).createTextNode(value);
43 if (owner) {
44 appendChild(owner, element);
45 }
46 return element;
47 }
48
49 /**
50 * Returns the document owner of the given element. In particular,
51 * returns window.document if node is null or the browser does not
52 * support ownerDocument.
53 *
54 * @param {Node} node The node whose ownerDocument is required.
55 * @returns {Document|Null} The owner document or null if unsupported.
56 */
57 function ownerDocument(node) {
58 return (node ? node.ownerDocument : null) || document;
59 }
60
61 /**
62 * Wrapper function to create CSS units (pixels) string
63 *
64 * @param {Number} numPixels Number of pixels, may be floating point.
65 * @returns {String} Corresponding CSS units string.
66 */
67 function px(numPixels) {
68 return round(numPixels) + "px";
69 }
70
71 /**
72 * Sets the left and top of the given element to the given point.
73 *
74 * @param {Element} element The dom element to manipulate.
75 * @param {Point} point The desired position.
76 */
77 function setPosition(element, point) {
78 var style = element.style;
79 style.position = "absolute";
80 style.left = px(point.x);
81 style.top = px(point.y);
82 }
83
84 /**
85 * Sets the width and height style attributes to the given size.
86 *
87 * @param {Element} element The dom element to manipulate.
88 * @param {Size} size The desired size.
89 */
90 function setSize(element, size) {
91 var style = element.style;
92 style.width = px(size.width);
93 style.height = px(size.height);
94 }
95
96 /**
97 * Sets display to none. Doing this as a function saves a few bytes for
98 * the 'style.display' property and the 'none' literal.
99 *
100 * @param {Element} node The dom element to manipulate.
101 */
102 function displayNone(node) {
103 node.style.display = 'none';
104 }
105
106 /**
107 * Sets display to default.
108 *
109 * @param {Element} node The dom element to manipulate.
110 */
111 function displayDefault(node) {
112 node.style.display = '';
113 }
114
115 /**
116 * Appends the given child to the given parent in the DOM
117 *
118 * @param {Element} parent The parent dom element.
119 * @param {Node} child The new child dom node.
120 */
121 function appendChild(parent, child) {
122 parent.appendChild(child);
123 }
124
125
126 /**
127 * Wrapper for the eval() builtin function to evaluate expressions and
128 * obtain their value. It wraps the expression in parentheses such
129 * that object literals are really evaluated to objects. Without the
130 * wrapping, they are evaluated as block, and create syntax
131 * errors. Also protects against other syntax errors in the eval()ed
132 * code and returns null if the eval throws an exception.
133 *
134 * @param {String} expr
135 * @return {Object|Null}
136 */
137 function jsEval(expr) {
138 try {
139 return eval('[' + expr + '][0]');
140 } catch (e) {
141 return null;
142 }
143 }
144
145
146 /**
147 * Wrapper for the eval() builtin function to execute statements. This
148 * guards against exceptions thrown, but doesn't return a
149 * value. Still, mostly for testability, it returns a boolean to
150 * indicate whether execution was successful. NOTE:
151 * javascript's eval semantics is murky in that it confounds
152 * expression evaluation and statement execution into a single
153 * construct. Cf. jsEval().
154 *
155 * @param {String} stmt
156 * @return {Boolean}
157 */
158 function jsExec(stmt) {
159 try {
160 eval(stmt);
161 return true;
162 } catch (e) {
163 return false;
164 }
165 }
166
167
168 /**
169 * Wrapper for eval with a context. NOTE: The style guide
170 * deprecates eval, so this is the exception that proves the
171 * rule. Notice also that since the value of the expression is
172 * returned rather than assigned to a local variable, one major
173 * objection aganist the use of the with() statement, namely that
174 * properties of the with() target override local variables of the
175 * same name, is void here.
176 *
177 * @param {String} expr
178 * @param {Object} context
179 * @return {Object|Null}
180 */
181 function jsEvalWith(expr, context) {
182 try {
183 with (context) {
184 return eval('[' + expr + '][0]');
185 }
186 } catch (e) {
187 return null;
188 }
189 }
190
191
192 var DOM_ELEMENT_NODE = 1;
193 var DOM_ATTRIBUTE_NODE = 2;
194 var DOM_TEXT_NODE = 3;
195 var DOM_CDATA_SECTION_NODE = 4;
196 var DOM_ENTITY_REFERENCE_NODE = 5;
197 var DOM_ENTITY_NODE = 6;
198 var DOM_PROCESSING_INSTRUCTION_NODE = 7;
199 var DOM_COMMENT_NODE = 8;
200 var DOM_DOCUMENT_NODE = 9;
201 var DOM_DOCUMENT_TYPE_NODE = 10;
202 var DOM_DOCUMENT_FRAGMENT_NODE = 11;
203 var DOM_NOTATION_NODE = 12;
204
205 /**
206 * Traverses the element nodes in the DOM tree underneath the given
207 * node and finds the first node with elemId, or null if there is no such
208 * element. Traversal is in depth-first order.
209 *
210 * NOTE: The reason this is not combined with the elem() function is
211 * that the implementations are different.
212 * elem() is a wrapper for the built-in document.getElementById() function,
213 * whereas this function performs the traversal itself.
214 * Modifying elem() to take an optional root node is a possibility,
215 * but the in-built function would perform better than using our own traversal.
216 *
217 * @param {Element} node Root element of subtree to traverse.
218 * @param {String} elemId The id of the element to search for.
219 * @return {Element|Null} The corresponding element, or null if not found.
220 */
221 function nodeGetElementById(node, elemId) {
222 for (var c = node.firstChild; c; c = c.nextSibling) {
223 if (c.id == elemId) {
224 return c;
225 }
226 if (c.nodeType == DOM_ELEMENT_NODE) {
227 var n = arguments.callee.call(this, c, elemId);
228 if (n) {
229 return n;
230 }
231 }
232 }
233 return null;
234 }
235
236
237 /**
238 * Get an attribute from the DOM. Simple redirect, exists to compress code.
239 *
240 * @param {Element} node Element to interrogate.
241 * @param {String} name Name of parameter to extract.
242 * @return {String} Resulting attribute.
243 */
244 function domGetAttribute(node, name) {
245 return node.getAttribute(name);
246 }
247
248 /**
249 * Set an attribute in the DOM. Simple redirect to compress code.
250 *
251 * @param {Element} node Element to interrogate.
252 * @param {String} name Name of parameter to set.
253 * @param {String} value Set attribute to this value.
254 */
255 function domSetAttribute(node, name, value) {
256 node.setAttribute(name, value);
257 }
258
259 /**
260 * Remove an attribute from the DOM. Simple redirect to compress code.
261 *
262 * @param {Element} node Element to interrogate.
263 * @param {String} name Name of parameter to remove.
264 */
265 function domRemoveAttribute(node, name) {
266 node.removeAttribute(name);
267 }
268
269 /**
270 * Clone a node in the DOM.
271 *
272 * @param {Node} node Node to clone.
273 * @return {Node} Cloned node.
274 */
275 function domCloneNode(node) {
276 return node.cloneNode(true);
277 }
278
279
280 /**
281 * Return a safe string for the className of a node.
282 * If className is not a string, returns "".
283 *
284 * @param {Element} node DOM element to query.
285 * @return {String}
286 */
287 function domClassName(node) {
288 return node.className ? "" + node.className : "";
289 }
290
291 /**
292 * Adds a class name to the class attribute of the given node.
293 *
294 * @param {Element} node DOM element to modify.
295 * @param {String} className Class name to add.
296 */
297 function domAddClass(node, className) {
298 var name = domClassName(node);
299 if (name) {
300 var cn = name.split(/\s+/);
301 var found = false;
302 for (var i = 0; i < jsLength(cn); ++i) {
303 if (cn[i] == className) {
304 found = true;
305 break;
306 }
307 }
308
309 if (!found) {
310 cn.push(className);
311 }
312
313 node.className = cn.join(' ');
314 } else {
315 node.className = className;
316 }
317 }
318
319 /**
320 * Removes a class name from the class attribute of the given node.
321 *
322 * @param {Element} node DOM element to modify.
323 * @param {String} className Class name to remove.
324 */
325 function domRemoveClass(node, className) {
326 var c = domClassName(node);
327 if (!c || c.indexOf(className) == -1) {
328 return;
329 }
330 var cn = c.split(/\s+/);
331 for (var i = 0; i < jsLength(cn); ++i) {
332 if (cn[i] == className) {
333 cn.splice(i--, 1);
334 }
335 }
336 node.className = cn.join(' ');
337 }
338
339 /**
340 * Checks if a node belongs to a style class.
341 *
342 * @param {Element} node DOM element to test.
343 * @param {String} className Class name to check for.
344 * @return {Boolean} Node belongs to style class.
345 */
346 function domTestClass(node, className) {
347 var cn = domClassName(node).split(/\s+/);
348 for (var i = 0; i < jsLength(cn); ++i) {
349 if (cn[i] == className) {
350 return true;
351 }
352 }
353 return false;
354 }
355
356 /**
357 * Inserts a new child before a given sibling.
358 *
359 * @param {Node} newChild Node to insert.
360 * @param {Node} oldChild Sibling node.
361 * @return {Node} Reference to new child.
362 */
363 function domInsertBefore(newChild, oldChild) {
364 return oldChild.parentNode.insertBefore(newChild, oldChild);
365 }
366
367 /**
368 * Appends a new child to the specified (parent) node.
369 *
370 * @param {Element} node Parent element.
371 * @param {Node} child Child node to append.
372 * @return {Node} Newly appended node.
373 */
374 function domAppendChild(node, child) {
375 return node.appendChild(child);
376 }
377
378 /**
379 * Remove a new child from the specified (parent) node.
380 *
381 * @param {Element} node Parent element.
382 * @param {Node} child Child node to remove.
383 * @return {Node} Removed node.
384 */
385 function domRemoveChild(node, child) {
386 return node.removeChild(child);
387 }
388
389 /**
390 * Replaces an old child node with a new child node.
391 *
392 * @param {Node} newChild New child to append.
393 * @param {Node} oldChild Old child to remove.
394 * @return {Node} Replaced node.
395 */
396 function domReplaceChild(newChild, oldChild) {
397 return oldChild.parentNode.replaceChild(newChild, oldChild);
398 }
399
400 /**
401 * Removes a node from the DOM.
402 *
403 * @param {Node} node The node to remove.
404 * @return {Node} The removed node.
405 */
406 function domRemoveNode(node) {
407 return domRemoveChild(node.parentNode, node);
408 }
409
410 /**
411 * Creates a new text node in the given document.
412 *
413 * @param {Document} doc Target document.
414 * @param {String} text Text composing new text node.
415 * @return {Text} Newly constructed text node.
416 */
417 function domCreateTextNode(doc, text) {
418 return doc.createTextNode(text);
419 }
420
421 /**
422 * Creates a new node in the given document
423 *
424 * @param {Document} doc Target document.
425 * @param {String} name Name of new element (i.e. the tag name)..
426 * @return {Element} Newly constructed element.
427 */
428 function domCreateElement(doc, name) {
429 return doc.createElement(name);
430 }
431
432 /**
433 * Creates a new attribute in the given document.
434 *
435 * @param {Document} doc Target document.
436 * @param {String} name Name of new attribute.
437 * @return {Attr} Newly constructed attribute.
438 */
439 function domCreateAttribute(doc, name) {
440 return doc.createAttribute(name);
441 }
442
443 /**
444 * Creates a new comment in the given document.
445 *
446 * @param {Document} doc Target document.
447 * @param {String} text Comment text.
448 * @return {Comment} Newly constructed comment.
449 */
450 function domCreateComment(doc, text) {
451 return doc.createComment(text);
452 }
453
454 /**
455 * Creates a document fragment.
456 *
457 * @param {Document} doc Target document.
458 * @return {DocumentFragment} Resulting document fragment node.
459 */
460 function domCreateDocumentFragment(doc) {
461 return doc.createDocumentFragment();
462 }
463
464 /**
465 * Redirect to document.getElementById
466 *
467 * @param {Document} doc Target document.
468 * @param {String} id Id of requested node.
469 * @return {Element|Null} Resulting element.
470 */
471 function domGetElementById(doc, id) {
472 return doc.getElementById(id);
473 }
474
475 /**
476 * Redirect to window.setInterval
477 *
478 * @param {Window} win Target window.
479 * @param {Function} fun Callback function.
480 * @param {Number} time Time in milliseconds.
481 * @return {Object} Contract id.
482 */
483 function windowSetInterval(win, fun, time) {
484 return win.setInterval(fun, time);
485 }
486
487 /**
488 * Redirect to window.clearInterval
489 *
490 * @param {Window} win Target window.
491 * @param {object} id Contract id.
492 * @return {any} NOTE: Return type unknown?
493 */
494 function windowClearInterval(win, id) {
495 return win.clearInterval(id);
496 }
497
498 /**
499 * Determines whether one node is recursively contained in another.
500 * @param parent The parent node.
501 * @param child The node to look for in parent.
502 * @return parent recursively contains child
503 */
504 function containsNode(parent, child) {
505 while (parent != child && child.parentNode) {
506 child = child.parentNode;
507 }
508 return parent == child;
509 };
510 /**
511 * @fileoverview This file contains javascript utility functions that
512 * do not depend on anything defined elsewhere.
513 *
514 */
515
516 /**
517 * Returns the value of the length property of the given object. Used
518 * to reduce compiled code size.
519 *
520 * @param {Array | String} a The string or array to interrogate.
521 * @return {Number} The value of the length property.
522 */
523 function jsLength(a) {
524 return a.length;
525 }
526
527 var min = Math.min;
528 var max = Math.max;
529 var ceil = Math.ceil;
530 var floor = Math.floor;
531 var round = Math.round;
532 var abs = Math.abs;
533
534 /**
535 * Copies all properties from second object to the first. Modifies to.
536 *
537 * @param {Object} to The target object.
538 * @param {Object} from The source object.
539 */
540 function copyProperties(to, from) {
541 foreachin(from, function(p) {
542 to[p] = from[p];
543 });
544 }
545
546 /**
547 * Iterates over the array, calling the given function for each
548 * element.
549 *
550 * @param {Array} array
551 * @param {Function} fn
552 */
553 function foreach(array, fn) {
554 var I = jsLength(array);
555 for (var i = 0; i < I; ++i) {
556 fn(array[i], i);
557 }
558 }
559
560 /**
561 * Safely iterates over all properties of the given object, calling
562 * the given function for each property. If opt_all isn't true, uses
563 * hasOwnProperty() to assure the property is on the object, not on
564 * its prototype.
565 *
566 * @param {Object} object
567 * @param {Function} fn
568 * @param {Boolean} opt_all If true, also iterates over inherited properties.
569 */
570 function foreachin(object, fn, opt_all) {
571 for (var i in object) {
572 if (opt_all || !object.hasOwnProperty || object.hasOwnProperty(i)) {
573 fn(i, object[i]);
574 }
575 }
576 }
577
578 /**
579 * Appends the second array to the first, copying its elements.
580 * Optionally only a slice of the second array is copied.
581 *
582 * @param {Array} a1 Target array (modified).
583 * @param {Array} a2 Source array.
584 * @param {Number} opt_begin Begin of slice of second array (optional).
585 * @param {Number} opt_end End (exclusive) of slice of second array (optional).
586 */
587 function arrayAppend(a1, a2, opt_begin, opt_end) {
588 var i0 = opt_begin || 0;
589 var i1 = opt_end || jsLength(a2);
590 for (var i = i0; i < i1; ++i) {
591 a1.push(a2[i]);
592 }
593 }
594
595 /**
596 * Trim whitespace from begin and end of string.
597 *
598 * @see testStringTrim();
599 *
600 * @param {String} str Input string.
601 * @return {String} Trimmed string.
602 */
603 function stringTrim(str) {
604 return stringTrimRight(stringTrimLeft(str));
605 }
606
607 /**
608 * Trim whitespace from beginning of string.
609 *
610 * @see testStringTrimLeft();
611 *
612 * @param {String} str Input string.
613 * @return {String} Trimmed string.
614 */
615 function stringTrimLeft(str) {
616 return str.replace(/^\s+/, "");
617 }
618
619 /**
620 * Trim whitespace from end of string.
621 *
622 * @see testStringTrimRight();
623 *
624 * @param {String} str Input string.
625 * @return {String} Trimmed string.
626 */
627 function stringTrimRight(str) {
628 return str.replace(/\s+$/, "");
629 }
630
631 /**
632 * Jscompiler wrapper for parseInt() with base 10.
633 *
634 * @param {String} s String repersentation of a number.
635 *
636 * @return {Number} The integer contained in s, converted on base 10.
637 */
638 function parseInt10(s) {
639 return parseInt(s, 10);
640 }
641 /**
642 * @fileoverview A simple formatter to project JavaScript data into
643 * HTML templates. The template is edited in place. I.e. in order to
644 * instantiate a template, clone it from the DOM first, and then
645 * process the cloned template. This allows for updating of templates:
646 * If the templates is processed again, changed values are merely
647 * updated.
648 *
649 * NOTE: IE DOM doesn't have importNode().
650 *
651 * NOTE: The property name "length" must not be used in input
652 * data, see comment in jstSelect_().
653 */
654
655
656 /**
657 * Names of jstemplate attributes. These attributes are attached to
658 * normal HTML elements and bind expression context data to the HTML
659 * fragment that is used as template.
660 */
661 var ATT_select = 'jsselect';
662 var ATT_instance = 'jsinstance';
663 var ATT_display = 'jsdisplay';
664 var ATT_values = 'jsvalues';
665 var ATT_eval = 'jseval';
666 var ATT_transclude = 'transclude';
667 var ATT_content = 'jscontent';
668
669
670 /**
671 * Names of special variables defined by the jstemplate evaluation
672 * context. These can be used in js expression in jstemplate
673 * attributes.
674 */
675 var VAR_index = '$index';
676 var VAR_this = '$this';
677
678
679 /**
680 * Context for processing a jstemplate. The context contains a context
681 * object, whose properties can be referred to in jstemplate
682 * expressions, and it holds the locally defined variables.
683 *
684 * @param {Object} opt_data The context object. Null if no context.
685 *
686 * @param {Object} opt_parent The parent context, from which local
687 * variables are inherited. Normally the context object of the parent
688 * context is the object whose property the parent object is. Null for the
689 * context of the root object.
690 *
691 * @constructor
692 */
693 function JsExprContext(opt_data, opt_parent) {
694 var me = this;
695
696 /**
697 * The local context of the input data in which the jstemplate
698 * expressions are evaluated. Notice that this is usually an Object,
699 * but it can also be a scalar value (and then still the expression
700 * $this can be used to refer to it). Notice this can be a scalar
701 * value, including undefined.
702 *
703 * @type {Object}
704 */
705 me.data_ = opt_data;
706
707 /**
708 * The context for variable definitions in which the jstemplate
709 * expressions are evaluated. Other than for the local context,
710 * which replaces the parent context, variable definitions of the
711 * parent are inherited. The special variable $this points to data_.
712 *
713 * @type {Object}
714 */
715 me.vars_ = {};
716 if (opt_parent) {
717 copyProperties(me.vars_, opt_parent.vars_);
718 }
719 this.vars_[VAR_this] = me.data_;
720 }
721
722
723 /**
724 * Evaluates the given expression in the context of the current
725 * context object and the current local variables.
726 *
727 * @param {String} expr A javascript expression.
728 *
729 * @param {Element} template DOM node of the template.
730 *
731 * @return The value of that expression.
732 */
733 JsExprContext.prototype.jseval = function(expr, template) {
734 with (this.vars_) {
735 with (this.data_) {
736 try {
737 return (function() {
738 return eval('[' + expr + '][0]');
739 }).call(template);
740 } catch (e) {
741 return null;
742 }
743 }
744 }
745 }
746
747
748 /**
749 * Clones the current context for a new context object. The cloned
750 * context has the data object as its context object and the current
751 * context as its parent context. It also sets the $index variable to
752 * the given value. This value usually is the position of the data
753 * object in a list for which a template is instantiated multiply.
754 *
755 * @param {Object} data The new context object.
756 *
757 * @param {Number} index Position of the new context when multiply
758 * instantiated. (See implementation of jstSelect().)
759 *
760 * @return {JsExprContext}
761 */
762 JsExprContext.prototype.clone = function(data, index) {
763 var ret = new JsExprContext(data, this);
764 ret.setVariable(VAR_index, index);
765 if (this.resolver_) {
766 ret.setSubTemplateResolver(this.resolver_);
767 }
768 return ret;
769 }
770
771
772 /**
773 * Binds a local variable to the given value. If set from jstemplate
774 * jsvalue expressions, variable names must start with $, but in the
775 * API they only have to be valid javascript identifier.
776 *
777 * @param {String} name
778 *
779 * @param {Object} value
780 */
781 JsExprContext.prototype.setVariable = function(name, value) {
782 this.vars_[name] = value;
783 }
784
785
786 /**
787 * Sets the function used to resolve the values of the transclude
788 * attribute into DOM nodes. By default, this is jstGetTemplate(). The
789 * value set here is inherited by clones of this context.
790 *
791 * @param {Function} resolver The function used to resolve transclude
792 * ids into a DOM node of a subtemplate. The DOM node returned by this
793 * function will be inserted into the template instance being
794 * processed. Thus, the resolver function must instantiate the
795 * subtemplate as necessary.
796 */
797 JsExprContext.prototype.setSubTemplateResolver = function(resolver) {
798 this.resolver_ = resolver;
799 }
800
801
802 /**
803 * Resolves a sub template from an id. Used to process the transclude
804 * attribute. If a resolver function was set using
805 * setSubTemplateResolver(), it will be used, otherwise
806 * jstGetTemplate().
807 *
808 * @param {String} id The id of the sub template.
809 *
810 * @return {Node} The root DOM node of the sub template, for direct
811 * insertion into the currently processed template instance.
812 */
813 JsExprContext.prototype.getSubTemplate = function(id) {
814 return (this.resolver_ || jstGetTemplate).call(this, id);
815 }
816
817
818 /**
819 * HTML template processor. Data values are bound to HTML templates
820 * using the attributes transclude, jsselect, jsdisplay, jscontent,
821 * jsvalues. The template is modifed in place. The values of those
822 * attributes are JavaScript expressions that are evaluated in the
823 * context of the data object fragment.
824 *
825 * @param {JsExprContext} context Context created from the input data
826 * object.
827 *
828 * @param {Element} template DOM node of the template. This will be
829 * processed in place. After processing, it will still be a valid
830 * template that, if processed again with the same data, will remain
831 * unchanged.
832 */
833 function jstProcess(context, template) {
834 var processor = new JstProcessor();
835 processor.run_([ processor, processor.jstProcess_, context, template ]);
836 }
837
838
839 /**
840 * Internal class used by jstemplates to maintain context.
841 * NOTE: This is necessary to process deep templates in Safari
842 * which has a relatively shallow stack.
843 * @class
844 */
845 function JstProcessor() {
846 }
847
848
849 /**
850 * Runs the state machine, beginning with function "start".
851 *
852 * @param {Array} start The first function to run, in the form
853 * [object, method, args ...]
854 */
855 JstProcessor.prototype.run_ = function(start) {
856 var me = this;
857
858 me.queue_ = [ start ];
859 while (jsLength(me.queue_)) {
860 var f = me.queue_.shift();
861 f[1].apply(f[0], f.slice(2));
862 }
863 }
864
865
866 /**
867 * Appends a function to be called later.
868 * Analogous to calling that function on a subsequent line, or a subsequent
869 * iteration of a loop.
870 *
871 * @param {Array} f A function in the form [object, method, args ...]
872 */
873 JstProcessor.prototype.enqueue_ = function(f) {
874 this.queue_.push(f);
875 }
876
877
878 /**
879 * Implements internals of jstProcess.
880 *
881 * @param {JsExprContext} context
882 *
883 * @param {Element} template
884 */
885 JstProcessor.prototype.jstProcess_ = function(context, template) {
886 var me = this;
887
888 var transclude = domGetAttribute(template, ATT_transclude);
889 if (transclude) {
890 var tr = context.getSubTemplate(transclude);
891 if (tr) {
892 domReplaceChild(tr, template);
893 me.enqueue_([ me, me.jstProcess_, context, tr ]);
894 } else {
895 domRemoveNode(template);
896 }
897 return;
898 }
899
900 var select = domGetAttribute(template, ATT_select);
901 if (select) {
902 me.jstSelect_(context, template, select);
903 return;
904 }
905
906 var display = domGetAttribute(template, ATT_display);
907 if (display) {
908 if (!context.jseval(display, template)) {
909 displayNone(template);
910 return;
911 }
912
913 displayDefault(template);
914 }
915
916
917 var values = domGetAttribute(template, ATT_values);
918 if (values) {
919 me.jstValues_(context, template, values);
920 }
921
922 var expressions = domGetAttribute(template, ATT_eval);
923 if (expressions) {
924 foreach(expressions.split(/\s*;\s*/), function(expression) {
925 expression = stringTrim(expression);
926 if (jsLength(expression)) {
927 context.jseval(expression, template);
928 }
929 });
930 }
931
932 var content = domGetAttribute(template, ATT_content);
933 if (content) {
934 me.jstContent_(context, template, content);
935
936 } else {
937 var childnodes = [];
938 for (var i = 0; i < jsLength(template.childNodes); ++i) {
939 if (template.childNodes[i].nodeType == DOM_ELEMENT_NODE) {
940 me.enqueue_(
941 [ me, me.jstProcess_, context, template.childNodes[i] ]);
942 }
943 }
944 }
945 }
946
947
948 /**
949 * Implements the jsselect attribute: evalutes the value of the
950 * jsselect attribute in the current context, with the current
951 * variable bindings (see JsExprContext.jseval()). If the value is an
952 * array, the current template node is multiplied once for every
953 * element in the array, with the array element being the context
954 * object. If the array is empty, or the value is undefined, then the
955 * current template node is dropped. If the value is not an array,
956 * then it is just made the context object.
957 *
958 * @param {JsExprContext} context The current evaluation context.
959 *
960 * @param {Element} template The currently processed node of the template.
961 *
962 * @param {String} select The javascript expression to evaluate.
963 *
964 * @param {Function} process The function to continue processing with.
965 */
966 JstProcessor.prototype.jstSelect_ = function(context, template, select) {
967 var me = this;
968
969 var value = context.jseval(select, template);
970 domRemoveAttribute(template, ATT_select);
971
972 var instance = domGetAttribute(template, ATT_instance);
973 var instance_last = false;
974 if (instance) {
975 if (instance.charAt(0) == '*') {
976 instance = parseInt10(instance.substr(1));
977 instance_last = true;
978 } else {
979 instance = parseInt10(instance);
980 }
981 }
982
983 var multiple = (value !== null &&
984 typeof value == 'object' &&
985 typeof value.length == 'number');
986 var multiple_empty = (multiple && value.length == 0);
987
988 if (multiple) {
989 if (multiple_empty) {
990 if (!instance) {
991 domSetAttribute(template, ATT_select, select);
992 domSetAttribute(template, ATT_instance, '*0');
993 displayNone(template);
994 } else {
995 domRemoveNode(template);
996 }
997
998 } else {
999 displayDefault(template);
1000 if (instance === null || instance === "" || instance === undefined ||
1001 (instance_last && instance < jsLength(value) - 1)) {
1002 var templatenodes = [];
1003 var instances_start = instance || 0;
1004 for (var i = instances_start + 1; i < jsLength(value); ++i) {
1005 var node = domCloneNode(template);
1006 templatenodes.push(node);
1007 domInsertBefore(node, template);
1008 }
1009 templatenodes.push(template);
1010
1011 for (var i = 0; i < jsLength(templatenodes); ++i) {
1012 var ii = i + instances_start;
1013 var v = value[ii];
1014 var t = templatenodes[i];
1015
1016 me.enqueue_([ me, me.jstProcess_, context.clone(v, ii), t ]);
1017 var instanceStr = (ii == jsLength(value) - 1 ? '*' : '') + ii;
1018 me.enqueue_(
1019 [ null, postProcessMultiple_, t, select, instanceStr ]);
1020 }
1021
1022 } else if (instance < jsLength(value)) {
1023 var v = value[instance];
1024
1025 me.enqueue_(
1026 [me, me.jstProcess_, context.clone(v, instance), template]);
1027 var instanceStr = (instance == jsLength(value) - 1 ? '*' : '')
1028 + instance;
1029 me.enqueue_(
1030 [ null, postProcessMultiple_, template, select, instanceStr ]);
1031 } else {
1032 domRemoveNode(template);
1033 }
1034 }
1035 } else {
1036 if (value == null) {
1037 domSetAttribute(template, ATT_select, select);
1038 displayNone(template);
1039 } else {
1040 me.enqueue_(
1041 [ me, me.jstProcess_, context.clone(value, 0), template ]);
1042 me.enqueue_(
1043 [ null, postProcessSingle_, template, select ]);
1044 }
1045 }
1046 }
1047
1048
1049 /**
1050 * Sets ATT_select and ATT_instance following recursion to jstProcess.
1051 *
1052 * @param {Element} template The template
1053 *
1054 * @param {String} select The jsselect string
1055 *
1056 * @param {String} instanceStr The new value for the jsinstance attribute
1057 */
1058 function postProcessMultiple_(template, select, instanceStr) {
1059 domSetAttribute(template, ATT_select, select);
1060 domSetAttribute(template, ATT_instance, instanceStr);
1061 }
1062
1063
1064 /**
1065 * Sets ATT_select and makes the element visible following recursion to
1066 * jstProcess.
1067 *
1068 * @param {Element} template The template
1069 *
1070 * @param {String} select The jsselect string
1071 */
1072 function postProcessSingle_(template, select) {
1073 domSetAttribute(template, ATT_select, select);
1074 displayDefault(template);
1075 }
1076
1077
1078 /**
1079 * Implements the jsvalues attribute: evaluates each of the values and
1080 * assigns them to variables in the current context (if the name
1081 * starts with '$', javascript properties of the current template node
1082 * (if the name starts with '.'), or DOM attributes of the current
1083 * template node (otherwise). Since DOM attribute values are always
1084 * strings, the value is coerced to string in the latter case,
1085 * otherwise it's the uncoerced javascript value.
1086 *
1087 * @param {JsExprContext} context Current evaluation context.
1088 *
1089 * @param {Element} template Currently processed template node.
1090 *
1091 * @param {String} valuesStr Value of the jsvalues attribute to be
1092 * processed.
1093 */
1094 JstProcessor.prototype.jstValues_ = function(context, template, valuesStr) {
1095 var values = valuesStr.split(/\s*;\s*/);
1096 for (var i = 0; i < jsLength(values); ++i) {
1097 var colon = values[i].indexOf(':');
1098 if (colon < 0) {
1099 continue;
1100 }
1101 var label = stringTrim(values[i].substr(0, colon));
1102 var value = context.jseval(values[i].substr(colon + 1), template);
1103
1104 if (label.charAt(0) == '$') {
1105 context.setVariable(label, value);
1106
1107 } else if (label.charAt(0) == '.') {
1108 var nameSpaceLabel = label.substr(1).split('.');
1109 var nameSpaceObject = template;
1110 var nameSpaceDepth = jsLength(nameSpaceLabel);
1111 for (var j = 0, J = nameSpaceDepth - 1; j < J; ++j) {
1112 var jLabel = nameSpaceLabel[j];
1113 if (!nameSpaceObject[jLabel]) {
1114 nameSpaceObject[jLabel] = {};
1115 }
1116 nameSpaceObject = nameSpaceObject[jLabel];
1117 }
1118 nameSpaceObject[nameSpaceLabel[nameSpaceDepth - 1]] = value;
1119 } else if (label) {
1120 if (typeof value == 'boolean') {
1121 if (value) {
1122 domSetAttribute(template, label, label);
1123 } else {
1124 domRemoveAttribute(template, label);
1125 }
1126 } else {
1127 domSetAttribute(template, label, '' + value);
1128 }
1129 }
1130 }
1131 }
1132
1133
1134 /**
1135 * Implements the jscontent attribute. Evalutes the expression in
1136 * jscontent in the current context and with the current variables,
1137 * and assigns its string value to the content of the current template
1138 * node.
1139 *
1140 * @param {JsExprContext} context Current evaluation context.
1141 *
1142 * @param {Element} template Currently processed template node.
1143 *
1144 * @param {String} content Value of the jscontent attribute to be
1145 * processed.
1146 */
1147 JstProcessor.prototype.jstContent_ = function(context, template, content) {
1148 var value = '' + context.jseval(content, template);
1149 if (template.innerHTML == value) {
1150 return;
1151 }
1152 while (template.firstChild) {
1153 domRemoveNode(template.firstChild);
1154 }
1155 var t = domCreateTextNode(ownerDocument(template), value);
1156 domAppendChild(template, t);
1157 }
1158
1159
1160 /**
1161 * Helps to implement the transclude attribute, and is the initial
1162 * call to get hold of a template from its ID.
1163 *
1164 * @param {String} name The ID of the HTML element used as template.
1165 *
1166 * @returns {Element} The DOM node of the template. (Only element
1167 * nodes can be found by ID, hence it's a Element.)
1168 */
1169 function jstGetTemplate(name) {
1170 var section = domGetElementById(document, name);
1171 if (section) {
1172 var ret = domCloneNode(section);
1173 domRemoveAttribute(ret, 'id');
1174 return ret;
1175 } else {
1176 return null;
1177 }
1178 }
1179
1180 window['jstGetTemplate'] = jstGetTemplate;
1181 window['jstProcess'] = jstProcess;
1182 window['JsExprContext'] = JsExprContext;
OLDNEW
« no previous file with comments | « chrome/test/data/extensions/samples/buildbot/manifest.json ('k') | chrome/test/data/extensions/samples/tabs/manifest.json » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698