1 /* 2 * Copyright (C) 2009 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 // Template Test 32 // This test uses a simple JS templating system for injecting JSON data into 33 // an HTML page. It is designed to use the kind of DOM manipulation common 34 // in templating systems. 35 // 36 // Code from http://code.google.com/p/google-jstemplate/source/browse/trunk/jstemplate.js 37 38 // This is the HTML code to use in the template test 39 var content ="\ 40 <style type=\"text/css\"> \ 41 body {\ 42 border-top: 10px solid #3B85E3;\ 43 color: #333;\ 44 font-family: Verdana,Arial,Helvetica,sans-serif;\ 45 }\ 46 body, td {\ 47 font-size: 11px;\ 48 }\ 49 a:link, a:visited {\ 50 color: #2C3EBA;\ 51 text-decoration: none;\ 52 }\ 53 a:hover {\ 54 color: red;\ 55 text-decoration: underline;\ 56 }\ 57 h1 {\ 58 border-left: 10px solid #FFF;\ 59 font-size: 16px;\ 60 font-weight: bold;\ 61 margin: 0;\ 62 padding: 0.2em;\ 63 color: #3B85E3;\ 64 }\ 65 h2 {\ 66 border-left: 10px solid #FFF;\ 67 font-size: 11px;\ 68 font-weight: normal;\ 69 margin: 0;\ 70 padding: 0 6em 0.2em 0.2em;\ 71 }\ 72 .details {\ 73 margin: 0.4em 1.9em 0 1.2em;\ 74 padding: 0 0.4em 0.3em 0;\ 75 white-space: nowrap;\ 76 }\ 77 .details .outer {\ 78 padding-right: 0;\ 79 vertical-align: top;\ 80 }\ 81 .details .top {\ 82 border-top: 2px solid #333;\ 83 font-weight: bold;\ 84 margin-top: 0.4em;\ 85 }\ 86 .details .header2 {\ 87 font-weight: bold;\ 88 padding-left: 0.9em;\ 89 }\ 90 .details .key {\ 91 padding-left: 1.1em;\ 92 vertical-align: top;\ 93 }\ 94 .details .value {\ 95 text-align: right;\ 96 color: #333;\ 97 font-weight: bold;\ 98 }\ 99 .details .zebra {\ 100 background: #EEE;\ 101 }\ 102 .lower {\ 103 text-transform: lowercase;\ 104 }\ 105 </style> \ 106 <h1 class=\"lower\">About Stats</h1> \ 107 <table class=\"details\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\"> \ 108 <tbody> \ 109 <tr> \ 110 <td class=\"outer\"> \ 111 <table cellspacing=\"0\" cellpadding=\"0\" border=\"0\"> \ 112 <tbody> \ 113 <tr> \ 114 <td class=\"top\" width=\"100\">Counters</td> \ 115 <td class=\"top value\" colspan=2></td> \ 116 </tr> \ 117 <tr> \ 118 <td class=\"header2 lower\" width=\"200\">name</td> \ 119 <td class=\"header2 lower\">value</td> \ 120 <td class=\"header2 lower\">delta</td> \ 121 </tr> \ 122 <tr jsselect=\"counters\" name=\"counter\"> \ 123 <td class=\"key\" width=\"200\" jscontent=\"name\"></td> \ 124 <td class=\"value\" jscontent=\"value\"></td> \ 125 <td class=\"value\" jscontent=\"delta\"></td> \ 126 </tr> \ 127 </tbody> \ 128 </table> \ 129 </td> \ 130 <td width=\"15\"/> \ 131 <td class=\"outer\"> \ 132 <table cellspacing=\"0\" cellpadding=\"0\" border=\"0\"> \ 133 <tbody> \ 134 <tr> \ 135 <td class=\"top\" width=\"100\">Timers</td> \ 136 <td class=\"top value\"></td> \ 137 <td class=\"top value\" colspan=3></td> \ 138 </tr> \ 139 <tr> \ 140 <td class=\"header2 lower\" width=\"200\">name</td> \ 141 <td class=\"header2 lower\">count</td> \ 142 <td class=\"header2 lower\">time (ms)</td> \ 143 <td class=\"header2 lower\">avg time (ms)</td> \ 144 </tr> \ 145 <tr jsselect=\"timers\" name=\"timer\"> \ 146 <td class=\"key\" width=\"200\" jscontent=\"name\"></td> \ 147 <td class=\"value\" jscontent=\"value\"></td> \ 148 <td class=\"value\" jscontent=\"time\"></td> \ 149 <td class=\"value\"></td> \ 150 </tr> \ 151 </tbody> \ 152 </table> \ 153 </td> \ 154 </tr> \ 155 </tbody> \ 156 </table><br/> \ 157 </body> \ 158 </html> \ 159 "; 160 161 // Generic Template Library 162 163 /** 164 * @fileoverview This file contains miscellaneous basic functionality. 165 * 166 */ 167 168 /** 169 * Returns the document owner of the given element. In particular, 170 * returns window.document if node is null or the browser does not 171 * support ownerDocument. 172 * 173 * @param {Node} node The node whose ownerDocument is required. 174 * @returns {Document|Null} The owner document or null if unsupported. 175 */ 176 function Template_ownerDocument(node) { 177 return (node ? node.ownerDocument : null) || document; 178 } 179 180 /** 181 * Wrapper function to create CSS units (pixels) string 182 * 183 * @param {Number} numPixels Number of pixels, may be floating point. 184 * @returns {String} Corresponding CSS units string. 185 */ 186 function Template_px(numPixels) { 187 return round(numPixels) + "px"; 188 } 189 190 /** 191 * Sets display to none. Doing this as a function saves a few bytes for 192 * the 'style.display' property and the 'none' literal. 193 * 194 * @param {Element} node The dom element to manipulate. 195 */ 196 function Template_displayNone(node) { 197 node.style.display = 'none'; 198 } 199 200 /** 201 * Sets display to default. 202 * 203 * @param {Element} node The dom element to manipulate. 204 */ 205 function Template_displayDefault(node) { 206 node.style.display = ''; 207 } 208 209 var DOM_ELEMENT_NODE = 1; 210 var DOM_ATTRIBUTE_NODE = 2; 211 var DOM_TEXT_NODE = 3; 212 var DOM_CDATA_SECTION_NODE = 4; 213 var DOM_ENTITY_REFERENCE_NODE = 5; 214 var DOM_ENTITY_NODE = 6; 215 var DOM_PROCESSING_INSTRUCTION_NODE = 7; 216 var DOM_COMMENT_NODE = 8; 217 var DOM_DOCUMENT_NODE = 9; 218 var DOM_DOCUMENT_TYPE_NODE = 10; 219 var DOM_DOCUMENT_FRAGMENT_NODE = 11; 220 var DOM_NOTATION_NODE = 12; 221 222 /** 223 * Get an attribute from the DOM. Simple redirect, exists to compress code. 224 * 225 * @param {Element} node Element to interrogate. 226 * @param {String} name Name of parameter to extract. 227 * @return {String} Resulting attribute. 228 */ 229 function Template_domGetAttribute(node, name) { 230 return node.getAttribute(name); 231 } 232 233 /** 234 * Set an attribute in the DOM. Simple redirect to compress code. 235 * 236 * @param {Element} node Element to interrogate. 237 * @param {String} name Name of parameter to set. 238 * @param {String} value Set attribute to this value. 239 */ 240 function Template_domSetAttribute(node, name, value) { 241 node.setAttribute(name, value); 242 } 243 244 /** 245 * Remove an attribute from the DOM. Simple redirect to compress code. 246 * 247 * @param {Element} node Element to interrogate. 248 * @param {String} name Name of parameter to remove. 249 */ 250 function Template_domRemoveAttribute(node, name) { 251 node.removeAttribute(name); 252 } 253 254 /** 255 * Clone a node in the DOM. 256 * 257 * @param {Node} node Node to clone. 258 * @return {Node} Cloned node. 259 */ 260 function Template_domCloneNode(node) { 261 return node.cloneNode(true); 262 } 263 264 /** 265 * Return a safe string for the className of a node. 266 * If className is not a string, returns "". 267 * 268 * @param {Element} node DOM element to query. 269 * @return {String} 270 */ 271 function Template_domClassName(node) { 272 return node.className ? "" + node.className : ""; 273 } 274 275 /** 276 * Inserts a new child before a given sibling. 277 * 278 * @param {Node} newChild Node to insert. 279 * @param {Node} oldChild Sibling node. 280 * @return {Node} Reference to new child. 281 */ 282 function Template_domInsertBefore(newChild, oldChild) { 283 return oldChild.parentNode.insertBefore(newChild, oldChild); 284 } 285 286 /** 287 * Appends a new child to the specified (parent) node. 288 * 289 * @param {Element} node Parent element. 290 * @param {Node} child Child node to append. 291 * @return {Node} Newly appended node. 292 */ 293 function Template_domAppendChild(node, child) { 294 return node.appendChild(child); 295 } 296 297 /** 298 * Remove a new child from the specified (parent) node. 299 * 300 * @param {Element} node Parent element. 301 * @param {Node} child Child node to remove. 302 * @return {Node} Removed node. 303 */ 304 function Template_domRemoveChild(node, child) { 305 return node.removeChild(child); 306 } 307 308 /** 309 * Replaces an old child node with a new child node. 310 * 311 * @param {Node} newChild New child to append. 312 * @param {Node} oldChild Old child to remove. 313 * @return {Node} Replaced node. 314 */ 315 function Template_domReplaceChild(newChild, oldChild) { 316 return oldChild.parentNode.replaceChild(newChild, oldChild); 317 } 318 319 /** 320 * Removes a node from the DOM. 321 * 322 * @param {Node} node The node to remove. 323 * @return {Node} The removed node. 324 */ 325 function Template_domRemoveNode(node) { 326 return Template_domRemoveChild(node.parentNode, node); 327 } 328 329 /** 330 * Creates a new text node in the given document. 331 * 332 * @param {Document} doc Target document. 333 * @param {String} text Text composing new text node. 334 * @return {Text} Newly constructed text node. 335 */ 336 function Template_domCreateTextNode(doc, text) { 337 return doc.createTextNode(text); 338 } 339 340 /** 341 * Redirect to document.getElementById 342 * 343 * @param {Document} doc Target document. 344 * @param {String} id Id of requested node. 345 * @return {Element|Null} Resulting element. 346 */ 347 function Template_domGetElementById(doc, id) { 348 return doc.getElementById(id); 349 } 350 351 /** 352 * @fileoverview This file contains javascript utility functions that 353 * do not depend on anything defined elsewhere. 354 * 355 */ 356 357 /** 358 * Returns the value of the length property of the given object. Used 359 * to reduce compiled code size. 360 * 361 * @param {Array | String} a The string or array to interrogate. 362 * @return {Number} The value of the length property. 363 */ 364 function Template_jsLength(a) { 365 return a.length; 366 } 367 368 var min = Math.min; 369 var max = Math.max; 370 var ceil = Math.ceil; 371 var floor = Math.floor; 372 var round = Math.round; 373 var abs = Math.abs; 374 375 /** 376 * Copies all properties from second object to the first. Modifies to. 377 * 378 * @param {Object} to The target object. 379 * @param {Object} from The source object. 380 */ 381 function Template_copyProperties(to, from) { 382 foreachin(from, function(p) { to[p] = from[p]; }); 383 } 384 385 /** 386 * Iterates over the array, calling the given function for each 387 * element. 388 * 389 * @param {Array} array 390 * @param {Function} fn 391 */ 392 function foreach(array, fn) { 393 var I = Template_jsLength(array); 394 for (var i = 0; i < I; ++i) 395 fn(array[i], i); 396 } 397 398 /** 399 * Safely iterates over all properties of the given object, calling 400 * the given function for each property. If opt_all isn't true, uses 401 * hasOwnProperty() to assure the property is on the object, not on 402 * its prototype. 403 * 404 * @param {Object} object 405 * @param {Function} fn 406 * @param {Boolean} opt_all If true, also iterates over inherited properties. 407 */ 408 function foreachin(object, fn, opt_all) { 409 for (var i in object) { 410 if (opt_all || !object.hasOwnProperty || object.hasOwnProperty(i)) 411 fn(i, object[i]); 412 } 413 } 414 415 /** 416 * Trim whitespace from begin and end of string. 417 * 418 * @see testStringTrim(); 419 * 420 * @param {String} str Input string. 421 * @return {String} Trimmed string. 422 */ 423 function Template_stringTrim(str) { 424 return Template_stringTrimRight(stringTrimLeft(str)); 425 } 426 427 /** 428 * Trim whitespace from beginning of string. 429 * 430 * @see testStringTrimLeft(); 431 * 432 * @param {String} str Input string. 433 * @return {String} Trimmed string. 434 */ 435 function Template_stringTrimLeft(str) { 436 return str.replace(/^\s+/, ""); 437 } 438 439 /** 440 * Trim whitespace from end of string. 441 * 442 * @see testStringTrimRight(); 443 * 444 * @param {String} str Input string. 445 * @return {String} Trimmed string. 446 */ 447 function Template_stringTrimRight(str) { 448 return str.replace(/\s+$/, ""); 449 } 450 451 /** 452 * Jscompiler wrapper for parseInt() with base 10. 453 * 454 * @param {String} s String repersentation of a number. 455 * 456 * @return {Number} The integer contained in s, converted on base 10. 457 */ 458 function Template_parseInt10(s) { 459 return parseInt(s, 10); 460 } 461 /** 462 * @fileoverview A simple formatter to project JavaScript data into 463 * HTML templates. The template is edited in place. I.e. in order to 464 * instantiate a template, clone it from the DOM first, and then 465 * process the cloned template. This allows for updating of templates: 466 * If the templates is processed again, changed values are merely 467 * updated. 468 * 469 * NOTE: IE DOM doesn't have importNode(). 470 * 471 * NOTE: The property name "length" must not be used in input 472 * data, see comment in jstSelect_(). 473 */ 474 475 /** 476 * Names of jstemplate attributes. These attributes are attached to 477 * normal HTML elements and bind expression context data to the HTML 478 * fragment that is used as template. 479 */ 480 var ATT_select = 'jsselect'; 481 var ATT_instance = 'jsinstance'; 482 var ATT_display = 'jsdisplay'; 483 var ATT_values = 'jsvalues'; 484 var ATT_eval = 'jseval'; 485 var ATT_transclude = 'transclude'; 486 var ATT_content = 'jscontent'; 487 488 /** 489 * Names of special variables defined by the jstemplate evaluation 490 * context. These can be used in js expression in jstemplate 491 * attributes. 492 */ 493 var VAR_index = '$index'; 494 var VAR_this = '$this'; 495 496 /** 497 * Context for processing a jstemplate. The context contains a context 498 * object, whose properties can be referred to in jstemplate 499 * expressions, and it holds the locally defined variables. 500 * 501 * @param {Object} opt_data The context object. Null if no context. 502 * 503 * @param {Object} opt_parent The parent context, from which local 504 * variables are inherited. Normally the context object of the parent 505 * context is the object whose property the parent object is. Null for the 506 * context of the root object. 507 * 508 * @constructor 509 */ 510 function JsExprContext(opt_data, opt_parent) { 511 var me = this; 512 513 /** 514 * The local context of the input data in which the jstemplate 515 * expressions are evaluated. Notice that this is usually an Object, 516 * but it can also be a scalar value (and then still the expression 517 * $this can be used to refer to it). Notice this can be a scalar 518 * value, including undefined. 519 * 520 * @type {Object} 521 */ 522 me.data_ = opt_data; 523 524 /** 525 * The context for variable definitions in which the jstemplate 526 * expressions are evaluated. Other than for the local context, 527 * which replaces the parent context, variable definitions of the 528 * parent are inherited. The special variable $this points to data_. 529 * 530 * @type {Object} 531 */ 532 me.vars_ = {}; 533 if (opt_parent) 534 Template_copyProperties(me.vars_, opt_parent.vars_); 535 this.vars_[VAR_this] = me.data_; 536 } 537 538 /** 539 * Evaluates the given expression in the context of the current 540 * context object and the current local variables. 541 * 542 * @param {String} expr A javascript expression. 543 * 544 * @param {Element} template DOM node of the template. 545 * 546 * @return The value of that expression. 547 */ 548 JsExprContext.prototype.jseval = function(expr, template) { 549 with (this.vars_) { 550 with (this.data_) { 551 try { 552 return (function() { return eval('[' + expr + '][0]'); }).call(template); 553 } catch (e) { 554 return null; 555 } 556 } 557 } 558 }; 559 560 /** 561 * Clones the current context for a new context object. The cloned 562 * context has the data object as its context object and the current 563 * context as its parent context. It also sets the $index variable to 564 * the given value. This value usually is the position of the data 565 * object in a list for which a template is instantiated multiply. 566 * 567 * @param {Object} data The new context object. 568 * 569 * @param {Number} index Position of the new context when multiply 570 * instantiated. (See implementation of jstSelect().) 571 * 572 * @return {JsExprContext} 573 */ 574 JsExprContext.prototype.clone = function(data, index) { 575 var ret = new JsExprContext(data, this); 576 ret.setVariable(VAR_index, index); 577 if (this.resolver_) 578 ret.setSubTemplateResolver(this.resolver_); 579 return ret; 580 }; 581 582 /** 583 * Binds a local variable to the given value. If set from jstemplate 584 * jsvalue expressions, variable names must start with $, but in the 585 * API they only have to be valid javascript identifier. 586 * 587 * @param {String} name 588 * 589 * @param {Object} value 590 */ 591 JsExprContext.prototype.setVariable = function(name, value) { 592 this.vars_[name] = value; 593 }; 594 595 /** 596 * Sets the function used to resolve the values of the transclude 597 * attribute into DOM nodes. By default, this is jstGetTemplate(). The 598 * value set here is inherited by clones of this context. 599 * 600 * @param {Function} resolver The function used to resolve transclude 601 * ids into a DOM node of a subtemplate. The DOM node returned by this 602 * function will be inserted into the template instance being 603 * processed. Thus, the resolver function must instantiate the 604 * subtemplate as necessary. 605 */ 606 JsExprContext.prototype.setSubTemplateResolver = function(resolver) { 607 this.resolver_ = resolver; 608 }; 609 610 /** 611 * Resolves a sub template from an id. Used to process the transclude 612 * attribute. If a resolver function was set using 613 * setSubTemplateResolver(), it will be used, otherwise 614 * jstGetTemplate(). 615 * 616 * @param {String} id The id of the sub template. 617 * 618 * @return {Node} The root DOM node of the sub template, for direct 619 * insertion into the currently processed template instance. 620 */ 621 JsExprContext.prototype.getSubTemplate = function(id) { 622 return (this.resolver_ || jstGetTemplate).call(this, id); 623 }; 624 625 /** 626 * HTML template processor. Data values are bound to HTML templates 627 * using the attributes transclude, jsselect, jsdisplay, jscontent, 628 * jsvalues. The template is modifed in place. The values of those 629 * attributes are JavaScript expressions that are evaluated in the 630 * context of the data object fragment. 631 * 632 * @param {JsExprContext} context Context created from the input data 633 * object. 634 * 635 * @param {Element} template DOM node of the template. This will be 636 * processed in place. After processing, it will still be a valid 637 * template that, if processed again with the same data, will remain 638 * unchanged. 639 */ 640 function jstProcess(context, template) { 641 var processor = new JstProcessor(); 642 processor.run_([ processor, processor.jstProcess_, context, template ]); 643 } 644 645 /** 646 * Internal class used by jstemplates to maintain context. 647 * NOTE: This is necessary to process deep templates in Safari 648 * which has a relatively shallow stack. 649 * @class 650 */ 651 function JstProcessor() { 652 } 653 654 /** 655 * Runs the state machine, beginning with function "start". 656 * 657 * @param {Array} start The first function to run, in the form 658 * [object, method, args ...] 659 */ 660 JstProcessor.prototype.run_ = function(start) { 661 var me = this; 662 663 me.queue_ = [ start ]; 664 while (Template_jsLength(me.queue_)) { 665 var f = me.queue_.shift(); 666 f[1].apply(f[0], f.slice(2)); 667 } 668 }; 669 670 /** 671 * Appends a function to be called later. 672 * Analogous to calling that function on a subsequent line, or a subsequent 673 * iteration of a loop. 674 * 675 * @param {Array} f A function in the form [object, method, args ...] 676 */ 677 JstProcessor.prototype.enqueue_ = function(f) { 678 this.queue_.push(f); 679 }; 680 681 /** 682 * Implements internals of jstProcess. 683 * 684 * @param {JsExprContext} context 685 * 686 * @param {Element} template 687 */ 688 JstProcessor.prototype.jstProcess_ = function(context, template) { 689 var me = this; 690 691 var transclude = Template_domGetAttribute(template, ATT_transclude); 692 if (transclude) { 693 var tr = context.getSubTemplate(transclude); 694 if (tr) { 695 Template_domReplaceChild(tr, template); 696 me.enqueue_([ me, me.jstProcess_, context, tr ]); 697 } else 698 Template_domRemoveNode(template); 699 return; 700 } 701 702 var select = Template_domGetAttribute(template, ATT_select); 703 if (select) { 704 me.jstSelect_(context, template, select); 705 return; 706 } 707 708 var display = Template_domGetAttribute(template, ATT_display); 709 if (display) { 710 if (!context.jseval(display, template)) { 711 Template_displayNone(template); 712 return; 713 } 714 Template_displayDefault(template); 715 } 716 717 var values = Template_domGetAttribute(template, ATT_values); 718 if (values) 719 me.jstValues_(context, template, values); 720 721 var expressions = Template_domGetAttribute(template, ATT_eval); 722 if (expressions) { 723 foreach(expressions.split(/\s*;\s*/), function(expression) { 724 expression = Template_stringTrim(expression); 725 if (Template_jsLength(expression)) 726 context.jseval(expression, template); 727 }); 728 } 729 730 var content = Template_domGetAttribute(template, ATT_content); 731 if (content) 732 me.jstContent_(context, template, content); 733 else { 734 var childnodes = []; 735 for (var i = 0; i < Template_jsLength(template.childNodes); ++i) { 736 if (template.childNodes[i].nodeType == DOM_ELEMENT_NODE) 737 me.enqueue_([me, me.jstProcess_, context, template.childNodes[i]]); 738 } 739 } 740 }; 741 742 /** 743 * Implements the jsselect attribute: evalutes the value of the 744 * jsselect attribute in the current context, with the current 745 * variable bindings (see JsExprContext.jseval()). If the value is an 746 * array, the current template node is multiplied once for every 747 * element in the array, with the array element being the context 748 * object. If the array is empty, or the value is undefined, then the 749 * current template node is dropped. If the value is not an array, 750 * then it is just made the context object. 751 * 752 * @param {JsExprContext} context The current evaluation context. 753 * 754 * @param {Element} template The currently processed node of the template. 755 * 756 * @param {String} select The javascript expression to evaluate. 757 * 758 * @param {Function} process The function to continue processing with. 759 */ 760 JstProcessor.prototype.jstSelect_ = function(context, template, select) { 761 var me = this; 762 763 var value = context.jseval(select, template); 764 Template_domRemoveAttribute(template, ATT_select); 765 766 var instance = Template_domGetAttribute(template, ATT_instance); 767 var instance_last = false; 768 if (instance) { 769 if (instance.charAt(0) == '*') { 770 instance = Template_parseInt10(instance.substr(1)); 771 instance_last = true; 772 } else 773 instance = Template_parseInt10(instance); 774 } 775 776 var multiple = (value !== null && typeof value == 'object' && typeof value.length == 'number'); 777 var multiple_empty = (multiple && value.length == 0); 778 779 if (multiple) { 780 if (multiple_empty) { 781 if (!instance) { 782 Template_domSetAttribute(template, ATT_select, select); 783 Template_domSetAttribute(template, ATT_instance, '*0'); 784 Template_displayNone(template); 785 } else 786 Template_domRemoveNode(template); 787 } else { 788 Template_displayDefault(template); 789 if (instance === null || instance === "" || instance === undefined || 790 (instance_last && instance < Template_jsLength(value) - 1)) { 791 var templatenodes = []; 792 var instances_start = instance || 0; 793 for (var i = instances_start + 1; i < Template_jsLength(value); ++i) { 794 var node = Template_domCloneNode(template); 795 templatenodes.push(node); 796 Template_domInsertBefore(node, template); 797 } 798 templatenodes.push(template); 799 800 for (var i = 0; i < Template_jsLength(templatenodes); ++i) { 801 var ii = i + instances_start; 802 var v = value[ii]; 803 var t = templatenodes[i]; 804 805 me.enqueue_([me, me.jstProcess_, context.clone(v, ii), t]); 806 var instanceStr = (ii == Template_jsLength(value) - 1 ? '*' : '') + ii; 807 me.enqueue_([null, postProcessMultiple_, t, select, instanceStr]); 808 } 809 } else if (instance < Template_jsLength(value)) { 810 var v = value[instance]; 811 812 me.enqueue_([me, me.jstProcess_, context.clone(v, instance), template]); 813 var instanceStr = (instance == Template_jsLength(value) - 1 ? '*' : '') + instance; 814 me.enqueue_([null, postProcessMultiple_, template, select, instanceStr]); 815 } else 816 Template_domRemoveNode(template); 817 } 818 } else { 819 if (value == null) { 820 Template_domSetAttribute(template, ATT_select, select); 821 Template_displayNone(template); 822 } else { 823 me.enqueue_([me, me.jstProcess_, context.clone(value, 0), template]); 824 me.enqueue_([null, postProcessSingle_, template, select]); 825 } 826 } 827 }; 828 829 /** 830 * Sets ATT_select and ATT_instance following recursion to jstProcess. 831 * 832 * @param {Element} template The template 833 * 834 * @param {String} select The jsselect string 835 * 836 * @param {String} instanceStr The new value for the jsinstance attribute 837 */ 838 function postProcessMultiple_(template, select, instanceStr) { 839 Template_domSetAttribute(template, ATT_select, select); 840 Template_domSetAttribute(template, ATT_instance, instanceStr); 841 } 842 843 /** 844 * Sets ATT_select and makes the element visible following recursion to 845 * jstProcess. 846 * 847 * @param {Element} template The template 848 * 849 * @param {String} select The jsselect string 850 */ 851 function postProcessSingle_(template, select) { 852 Template_domSetAttribute(template, ATT_select, select); 853 Template_displayDefault(template); 854 } 855 856 /** 857 * Implements the jsvalues attribute: evaluates each of the values and 858 * assigns them to variables in the current context (if the name 859 * starts with '$', javascript properties of the current template node 860 * (if the name starts with '.'), or DOM attributes of the current 861 * template node (otherwise). Since DOM attribute values are always 862 * strings, the value is coerced to string in the latter case, 863 * otherwise it's the uncoerced javascript value. 864 * 865 * @param {JsExprContext} context Current evaluation context. 866 * 867 * @param {Element} template Currently processed template node. 868 * 869 * @param {String} valuesStr Value of the jsvalues attribute to be 870 * processed. 871 */ 872 JstProcessor.prototype.jstValues_ = function(context, template, valuesStr) { 873 var values = valuesStr.split(/\s*;\s*/); 874 for (var i = 0; i < Template_jsLength(values); ++i) { 875 var colon = values[i].indexOf(':'); 876 if (colon < 0) 877 continue; 878 var label = Template_stringTrim(values[i].substr(0, colon)); 879 var value = context.jseval(values[i].substr(colon + 1), template); 880 881 if (label.charAt(0) == '$') 882 context.setVariable(label, value); 883 else if (label.charAt(0) == '.') 884 template[label.substr(1)] = value; 885 else if (label) { 886 if (typeof value == 'boolean') { 887 if (value) 888 Template_domSetAttribute(template, label, label); 889 else 890 Template_domRemoveAttribute(template, label); 891 } else 892 Template_domSetAttribute(template, label, '' + value); 893 } 894 } 895 }; 896 897 /** 898 * Implements the jscontent attribute. Evalutes the expression in 899 * jscontent in the current context and with the current variables, 900 * and assigns its string value to the content of the current template 901 * node. 902 * 903 * @param {JsExprContext} context Current evaluation context. 904 * 905 * @param {Element} template Currently processed template node. 906 * 907 * @param {String} content Value of the jscontent attribute to be 908 * processed. 909 */ 910 JstProcessor.prototype.jstContent_ = function(context, template, content) { 911 var value = '' + context.jseval(content, template); 912 if (template.innerHTML == value) 913 return; 914 while (template.firstChild) 915 Template_domRemoveNode(template.firstChild); 916 var t = Template_domCreateTextNode(Template_ownerDocument(template), value); 917 Template_domAppendChild(template, t); 918 }; 919 920 /** 921 * Helps to implement the transclude attribute, and is the initial 922 * call to get hold of a template from its ID. 923 * 924 * @param {String} name The ID of the HTML element used as template. 925 * 926 * @returns {Element} The DOM node of the template. (Only element 927 * nodes can be found by ID, hence it's a Element.) 928 */ 929 function jstGetTemplate(name) { 930 var section = Template_domGetElementById(document, name); 931 if (section) { 932 var ret = Template_domCloneNode(section); 933 Template_domRemoveAttribute(ret, 'id'); 934 return ret; 935 } else 936 return null; 937 } 938 939 window['jstGetTemplate'] = jstGetTemplate; 940 window['jstProcess'] = jstProcess; 941 window['JsExprContext'] = JsExprContext; 942 943 function TemplateTest() { 944 // Find the location to insert the content 945 var tp = document.getElementById('benchmark_content'); 946 947 // Inject the content 948 tp.innerHTML = content; 949 950 // Run the template 951 var cx = new JsExprContext( 952 {"counters": [ 953 {"name":"Chrome:Init","time":5}, 954 {"delta":0,"name":"Shutdown:window_close:time","time":111,"value":1}, 955 {"delta":0,"name":" Shutdown:window_close:timeMA","value":111}, 956 {"delta":0,"name":"Shutdown:window_close:time_pe","time":111,"value":1}, 957 {"delta":0,"name":" Shutdown:window_close:time_p","value":111}, 958 {"delta":0,"name":"Shutdown:renderers:total","time":1,"value":1}, 959 {"delta":0,"name":" Shutdown:renderers:totalMAX","value":1}, 960 {"delta":0,"name":"Shutdown:renderers:slow","time":0,"value":1}, 961 {"delta":0,"name":" Shutdown:renderers:slowMAX","value":10}, 962 {"delta":0,"name":"DNS:PrefetchQueue","time":2,"value":6}, 963 {"delta":0,"name":" DNS:PrefetchQueueMAX","value":1}, 964 {"delta":0,"name":"DNS:PrefetchFoundNameL","time":1048,"value":1003}, 965 {"delta":0,"name":" DNS:PrefetchFoundNameLMAX","value":46}, 966 {"delta":0,"name":"SB:QueueDepth","time":0,"value":1}, 967 {"delta":0,"name":" SB:QueueDepthMAX","value":0}, 968 {"delta":102,"name":"IPC:SendMsgCount","value":1016}, 969 {"delta":98,"name":"Chrome:ProcMsgL UI","time":2777,"value":1378}, 970 {"delta":2381,"name":" Chrome:ProcMsgL UIMAX","value":2409}, 971 {"delta":98,"name":"Chrome:TotalMsgL UI","time":5715,"value":1378}, 972 {"delta":1518,"name":" Chrome:TotalMsgL UIMAX","value":2409}, 973 {"name":"Chrome:RendererInit","time":9}, 974 {"delta":0,"name":"WebFrameActiveCount","value":2}, 975 {"delta":0,"name":"Gears:LoadTime","time":1,"value":1}, 976 {"delta":0,"name":" Gears:LoadTimeMAX","value":1}, 977 {"delta":1,"name":"URLRequestCount","value":41}, 978 {"delta":1,"name":"mime_sniffer:ShouldSniffMimeT","time":27,"value":27}, 979 {"delta":0,"name":" mime_sniffer:ShouldSniffMime","value":1}, 980 {"delta":3,"name":"ResourceLoadServer","time":1065,"value":73}, 981 {"delta":0,"name":" ResourceLoadServerMAX","value":51}, 982 {"delta":11,"name":"WebFramePaintTime","time":232,"value":42}, 983 {"delta":9,"name":" WebFramePaintTimeMAX","value":41}, 984 {"delta":11,"name":"MPArch:RWH_OnMsgPaintRect","time":136,"value":42}, 985 {"delta":0,"name":" MPArch:RWH_OnMsgPaintRectMAX","value":9}, 986 {"delta":0,"name":"NPObjects","value":2}, 987 {"delta":6008832,"name":"V8:OsMemoryAllocated","value":28422144}, 988 {"delta":7905,"name":"V8:GlobalHandles","value":16832}, 989 {"delta":0,"name":"V8:PcreMallocCount","value":0}, 990 {"delta":1,"name":"V8:ObjectPropertiesToDictiona","value":16}, 991 {"delta":0,"name":"V8:ObjectElementsToDictionary","value":0}, 992 {"delta":1128652,"name":"V8:AliveAfterLastGC","value":4467596}, 993 {"delta":0,"name":"V8:ObjsSinceLastYoung","value":0}, 994 {"delta":0,"name":"V8:ObjsSinceLastFull","value":0}, 995 {"delta":2048,"name":"V8:SymbolTableCapacity","value":12288}, 996 {"delta":1493,"name":"V8:NumberOfSymbols","value":6865}, 997 {"delta":100442,"name":"V8:TotalExternalStringMemory","value":359184}, 998 {"delta":0,"name":"V8:ScriptWrappers","value":0}, 999 {"delta":3,"name":"V8:CallInitializeStubs","value":20}, 1000 {"delta":0,"name":"V8:CallPreMonomorphicStubs","value":4}, 1001 {"delta":0,"name":"V8:CallNormalStubs","value":0}, 1002 {"delta":6,"name":"V8:CallMegamorphicStubs","value":44}, 1003 {"delta":0,"name":"V8:ArgumentsAdaptors","value":0}, 1004 {"delta":647,"name":"V8:CompilationCacheHits","value":1269}, 1005 {"delta":9,"name":"V8:CompilationCacheMisses","value":57}, 1006 {"delta":0,"name":"V8:RegExpCacheHits","value":2}, 1007 {"delta":0,"name":"V8:RegExpCacheMisses","value":6}, 1008 {"delta":6260,"name":"V8:TotalEvalSize","value":12621}, 1009 {"delta":50221,"name":"V8:TotalLoadSize","value":217362}, 1010 {"delta":63734,"name":"V8:TotalParseSize","value":299135}, 1011 {"delta":15174,"name":"V8:TotalPreparseSkipped","value":61824}, 1012 {"delta":69932,"name":"V8:TotalCompileSize","value":313048}, 1013 {"delta":22,"name":"V8:CodeStubs","value":117}, 1014 {"delta":1185,"name":"V8:TotalStubsCodeSize","value":6456}, 1015 {"delta":45987,"name":"V8:TotalCompiledCodeSize","value":169546}, 1016 {"delta":0,"name":"V8:GCCompactorCausedByRequest","value":0}, 1017 {"delta":0,"name":"V8:GCCompactorCausedByPromote","value":0}, 1018 {"delta":0,"name":"V8:GCCompactorCausedByOldspac","value":0}, 1019 {"delta":0,"name":"V8:GCCompactorCausedByWeakHan","value":0}, 1020 {"delta":0,"name":"V8:GCLastResortFromJS","value":0}, 1021 {"delta":0,"name":"V8:GCLastResortFromHandles","value":0}, 1022 {"delta":0,"name":"V8:KeyedLoadGenericSmi","value":0}, 1023 {"delta":0,"name":"V8:KeyedLoadGenericSymbol","value":0}, 1024 {"delta":0,"name":"V8:KeyedLoadGenericSlow","value":0}, 1025 {"delta":0,"name":"V8:KeyedLoadFunctionPrototype","value":0}, 1026 {"delta":0,"name":"V8:KeyedLoadStringLength","value":0}, 1027 {"delta":0,"name":"V8:KeyedLoadArrayLength","value":0}, 1028 {"delta":0,"name":"V8:KeyedLoadConstantFunction","value":0}, 1029 {"delta":0,"name":"V8:KeyedLoadField","value":0}, 1030 {"delta":0,"name":"V8:KeyedLoadCallback","value":0}, 1031 {"delta":0,"name":"V8:KeyedLoadInterceptor","value":0}, 1032 {"delta":0,"name":"V8:KeyedStoreField","value":0}, 1033 {"delta":0,"name":"V8:ForIn","value":0}, 1034 {"delta":2,"name":"V8:EnumCacheHits","value":9}, 1035 {"delta":4,"name":"V8:EnumCacheMisses","value":23}, 1036 {"delta":3724,"name":"V8:RelocInfoCount","value":18374}, 1037 {"delta":6080,"name":"V8:RelocInfoSize","value":30287}, 1038 {"delta":0,"name":"History:InitTime","time":12,"value":1}, 1039 {"delta":0,"name":" History:InitTimeMAX","value":12}, 1040 {"delta":1,"name":"History:GetFavIconForURL","time":0,"value":22}, 1041 {"delta":0,"name":" History:GetFavIconForURLMAX","value":0}, 1042 {"delta":2,"name":"V8:PreParse","time":9,"value":11}, 1043 {"delta":9,"name":"V8:Parse","time":9,"value":57}, 1044 {"delta":3,"name":"V8:Compile","time":3,"value":22}, 1045 {"delta":49,"name":"V8:ParseLazy","time":17,"value":231}, 1046 {"delta":47,"name":"V8:CompileLazy","time":3,"value":221}, 1047 {"delta":12,"name":"V8:GCScavenger","time":13,"value":28}, 1048 {"delta":0,"name":"NewTabPage:SearchURLs:Total","time":0,"value":1}, 1049 {"delta":0,"name":" NewTabPage:SearchURLs:TotalM","value":0}, 1050 {"delta":6,"name":"V8:CompileEval","time":1,"value":35}, 1051 {"delta":0,"name":"Memory:CachedFontAndDC","time":3,"value":3}, 1052 {"delta":0,"name":" Memory:CachedFontAndDCMAX","value":2}, 1053 {"delta":0,"name":"ResourceLoaderWait","time":1296,"value":48}, 1054 {"delta":0,"name":" ResourceLoaderWaitMAX","value":55}, 1055 {"delta":0,"name":"History:GetPageThumbnail","time":15,"value":9}, 1056 {"delta":0,"name":" History:GetPageThumbnailMAX","value":10}, 1057 {"delta":9,"name":"MPArch:RWH_InputEventDelta","time":327,"value":170}, 1058 {"delta":0,"name":" MPArch:RWH_InputEventDeltaMA","value":154}, 1059 {"delta":0,"name":"Omnibox:QueryBookmarksTime","time":2,"value":44}, 1060 {"delta":0,"name":" Omnibox:QueryBookmarksTimeMA","value":1}, 1061 {"delta":0,"name":"Chrome:DelayMsgUI","value":3}, 1062 {"delta":0,"name":"Autocomplete:HistoryAsyncQuer","time":351,"value":86}, 1063 {"delta":0,"name":" Autocomplete:HistoryAsyncQue","value":10}, 1064 {"delta":0,"name":"History:QueryHistory","time":1018,"value":44}, 1065 {"delta":0,"name":" History:QueryHistoryMAX","value":233}, 1066 {"delta":0,"name":"DiskCache:GetFileForNewBlock","time":0,"value":34}, 1067 {"delta":0,"name":" DiskCache:GetFileForNewBlock","value":0}, 1068 {"delta":0,"name":"DiskCache:CreateBlock","time":0,"value":34}, 1069 {"delta":0,"name":" DiskCache:CreateBlockMAX","value":0}, 1070 {"delta":0,"name":"DiskCache:CreateTime","time":0,"value":10}, 1071 {"delta":0,"name":" DiskCache:CreateTimeMAX","value":0}, 1072 {"delta":0,"name":"DNS:PrefetchPositiveHitL","time":1048,"value":2}, 1073 {"delta":0,"name":" DNS:PrefetchPositiveHitLMAX","value":1002}, 1074 {"delta":0,"name":"DiskCache:GetRankings","time":0,"value":27}, 1075 {"delta":0,"name":" DiskCache:GetRankingsMAX","value":0}, 1076 {"delta":0,"name":"DiskCache:DeleteHeader","time":0,"value":3}, 1077 {"delta":0,"name":" DiskCache:DeleteHeaderMAX","value":0}, 1078 {"delta":0,"name":"DiskCache:DeleteData","time":0,"value":3}, 1079 {"delta":0,"name":" DiskCache:DeleteDataMAX","value":0}, 1080 {"delta":0,"name":"DiskCache:DeleteBlock","time":0,"value":6}, 1081 {"delta":0,"name":" DiskCache:DeleteBlockMAX","value":0}, 1082 {"delta":0,"name":"SessionRestore:last_session_f","time":0,"value":1}, 1083 {"delta":0,"name":" SessionRestore:last_session_","value":0}, 1084 {"delta":3,"name":"SessionRestore:command_size","time":2940,"value":36}, 1085 {"delta":0,"name":" SessionRestore:command_sizeM","value":277}, 1086 {"delta":0,"name":"DNS:IndependentNavigation","time":2,"value":4}, 1087 {"delta":0,"name":" DNS:IndependentNavigationMAX","value":1}, 1088 {"delta":0,"name":"DiskCache:UpdateRank","time":1,"value":25}, 1089 {"delta":0,"name":" DiskCache:UpdateRankMAX","value":1}, 1090 {"delta":0,"name":"DiskCache:WriteTime","time":1,"value":21}, 1091 {"delta":0,"name":" DiskCache:WriteTimeMAX","value":1}, 1092 {"delta":0,"name":"Net:Transaction_Latency","time":183,"value":7}, 1093 {"delta":0,"name":" Net:Transaction_LatencyMAX","value":37}, 1094 {"delta":0,"name":"Net:Transaction_Bandwidth","time":40,"value":7}, 1095 {"delta":0,"name":" Net:Transaction_BandwidthMAX","value":8}, 1096 {"delta":0,"name":"NewTabUI load","time":564,"value":1}, 1097 {"delta":0,"name":" NewTabUI loadMAX","value":564}, 1098 {"delta":0,"name":"DiskCache:OpenTime","time":0,"value":2}, 1099 {"delta":0,"name":" DiskCache:OpenTimeMAX","value":0}, 1100 {"delta":0,"name":"DiskCache:ReadTime","time":0,"value":4}, 1101 {"delta":0,"name":" DiskCache:ReadTimeMAX","value":0}, 1102 {"delta":0,"name":"MPArch:RWHH_WhiteoutDuration_","time":27,"value":1}, 1103 {"delta":0,"name":" MPArch:RWHH_WhiteoutDuration","value":27}, 1104 {"delta":1,"name":"AsyncIO:IPCChannelClose","time":0,"value":4}, 1105 {"delta":0,"name":" AsyncIO:IPCChannelCloseMAX","value":0}, 1106 {"name":"GetHistoryTimer","time":0}, 1107 {"delta":0,"name":"DiskCache:Entries","time":7,"value":1}, 1108 {"delta":0,"name":" DiskCache:EntriesMAX","value":7}, 1109 {"delta":0,"name":"DiskCache:Size","time":0,"value":1}, 1110 {"delta":0,"name":" DiskCache:SizeMAX","value":0}, 1111 {"delta":0,"name":"DiskCache:MaxSize","time":80,"value":1}, 1112 {"delta":0,"name":" DiskCache:MaxSizeMAX","value":80}, 1113 {"delta":0,"name":"History:AddFTSData","time":1,"value":1}, 1114 {"delta":0,"name":" History:AddFTSDataMAX","value":1}, 1115 {"delta":0,"name":"Chrome:SlowMsgUI","value":1} 1116 ], 1117 "timers":[ 1118 {"name":"Chrome:Init","time":5}, 1119 {"delta":0,"name":"Shutdown:window_close:time","time":111,"value":1}, 1120 {"delta":0,"name":"Shutdown:window_close:time_pe","time":111,"value":1}, 1121 {"delta":0,"name":"Shutdown:renderers:total","time":1,"value":1}, 1122 {"delta":0,"name":"Shutdown:renderers:slow","time":0,"value":1}, 1123 {"delta":0,"name":"DNS:PrefetchQueue","time":2,"value":6}, 1124 {"delta":0,"name":"DNS:PrefetchFoundNameL","time":1048,"value":1003}, 1125 {"delta":0,"name":"SB:QueueDepth","time":0,"value":1}, 1126 {"delta":98,"name":"Chrome:ProcMsgL UI","time":2777,"value":1378}, 1127 {"delta":98,"name":"Chrome:TotalMsgL UI","time":5715,"value":1378}, 1128 {"name":"Chrome:RendererInit","time":9}, 1129 {"delta":0,"name":"Gears:LoadTime","time":1,"value":1}, 1130 {"delta":1,"name":"mime_sniffer:ShouldSniffMimeT","time":27,"value":27}, 1131 {"delta":3,"name":"ResourceLoadServer","time":1065,"value":73}, 1132 {"delta":11,"name":"WebFramePaintTime","time":232,"value":42}, 1133 {"delta":11,"name":"MPArch:RWH_OnMsgPaintRect","time":136,"value":42}, 1134 {"delta":0,"name":"History:InitTime","time":12,"value":1}, 1135 {"delta":1,"name":"History:GetFavIconForURL","time":0,"value":22}, 1136 {"delta":2,"name":"V8:PreParse","time":9,"value":11}, 1137 {"delta":9,"name":"V8:Parse","time":9,"value":57}, 1138 {"delta":3,"name":"V8:Compile","time":3,"value":22}, 1139 {"delta":49,"name":"V8:ParseLazy","time":17,"value":231}, 1140 {"delta":47,"name":"V8:CompileLazy","time":3,"value":221}, 1141 {"delta":12,"name":"V8:GCScavenger","time":13,"value":28}, 1142 {"delta":0,"name":"NewTabPage:SearchURLs:Total","time":0,"value":1}, 1143 {"delta":6,"name":"V8:CompileEval","time":1,"value":35}, 1144 {"delta":0,"name":"Memory:CachedFontAndDC","time":3,"value":3}, 1145 {"delta":0,"name":"ResourceLoaderWait","time":1296,"value":48}, 1146 {"delta":0,"name":"History:GetPageThumbnail","time":15,"value":9}, 1147 {"delta":9,"name":"MPArch:RWH_InputEventDelta","time":327,"value":170}, 1148 {"delta":0,"name":"Omnibox:QueryBookmarksTime","time":2,"value":44}, 1149 {"delta":0,"name":"Autocomplete:HistoryAsyncQuer","time":351,"value":86}, 1150 {"delta":0,"name":"History:QueryHistory","time":1018,"value":44}, 1151 {"delta":0,"name":"DiskCache:GetFileForNewBlock","time":0,"value":34}, 1152 {"delta":0,"name":"DiskCache:CreateBlock","time":0,"value":34}, 1153 {"delta":0,"name":"DiskCache:CreateTime","time":0,"value":10}, 1154 {"delta":0,"name":"DNS:PrefetchPositiveHitL","time":1048,"value":2}, 1155 {"delta":0,"name":"DiskCache:GetRankings","time":0,"value":27}, 1156 {"delta":0,"name":"DiskCache:DeleteHeader","time":0,"value":3}, 1157 {"delta":0,"name":"DiskCache:DeleteData","time":0,"value":3}, 1158 {"delta":0,"name":"DiskCache:DeleteBlock","time":0,"value":6}, 1159 {"delta":0,"name":"SessionRestore:last_session_f","time":0,"value":1}, 1160 {"delta":3,"name":"SessionRestore:command_size","time":2940,"value":36}, 1161 {"delta":0,"name":"DNS:IndependentNavigation","time":2,"value":4}, 1162 {"delta":0,"name":"DiskCache:UpdateRank","time":1,"value":25}, 1163 {"delta":0,"name":"DiskCache:WriteTime","time":1,"value":21}, 1164 {"delta":0,"name":"Net:Transaction_Latency","time":183,"value":7}, 1165 {"delta":0,"name":"Net:Transaction_Bandwidth","time":40,"value":7}, 1166 {"delta":0,"name":"NewTabUI load","time":564,"value":1}, 1167 {"delta":0,"name":"DiskCache:OpenTime","time":0,"value":2}, 1168 {"delta":0,"name":"DiskCache:ReadTime","time":0,"value":4}, 1169 {"delta":0,"name":"MPArch:RWHH_WhiteoutDuration_","time":27,"value":1}, 1170 {"delta":1,"name":"AsyncIO:IPCChannelClose","time":0,"value":4}, 1171 {"name":"GetHistoryTimer","time":0}, 1172 {"delta":0,"name":"DiskCache:Entries","time":7,"value":1}, 1173 {"delta":0,"name":"DiskCache:Size","time":0,"value":1}, 1174 {"delta":0,"name":"DiskCache:MaxSize","time":80,"value":1}, 1175 {"delta":0,"name":"History:AddFTSData","time":1,"value":1} 1176 ]}); 1177 jstProcess(cx, tp); 1178 } 1179 1180 var TemplateTest = new BenchmarkSuite('Template', [new Benchmark("Template",TemplateTest)]); 1181