1<head>
2  <title>Page Benchmark Options</title>
3
4  <script src="jst/util.js" type="text/javascript"></script>
5  <script src="jst/jsevalcontext.js" type="text/javascript"></script>
6  <script src="jst/jstemplate.js" type="text/javascript"></script>
7  <script src="jquery/jquery-1.4.2.min.js" type="text/javascript"></script>
8  <script src="jquery/jquery.flot.min.js" type="text/javascript"></script>
9  <script src="jquery/jquery.flot.dashes.js" type="text/javascript"></script>
10  <script src="util/table2CSV.js" type="text/javascript"></script> 
11  <script src="util/sorttable.js" type="text/javascript"></script>
12
13<style>
14body {
15  font-size: 84%;
16  font-family: Helvetica, Arial, sans-serif;
17  padding: 0.75em;
18  margin: 0;
19  min-width: 45em;
20}
21
22h1 {
23  font-size: 110%;
24  font-weight: bold;
25  color: #4a8ee6;
26  letter-spacing: -1px;
27  padding: 0;
28  margin: 0;
29}
30
31div#header {
32  padding: 0.75em 1em;
33  padding-top: 0.6em;
34  padding-left: 10;
35  margin-bottom: 0.75em;
36  position: relative;
37  overflow: hidden;
38  background: #5296de;
39  background-size: 100%;
40  border: 1px solid #3a75bd;
41  border-radius: 6px;
42  color: white;
43  text-shadow: 0 0 2px black;
44}
45div#header h1 {
46  padding-left: 37px;
47  margin: 0;
48  display: inline;
49  color: white;
50}
51div#header p {
52  font-size: 84%;
53  font-style: italic;
54  padding: 0;
55  margin: 0;
56  color: white;
57  padding-left: 0.4em;
58  display: inline;
59}
60
61table.sortable {
62  font-size: 84%;
63  table-layout: fixed;
64}
65
66table.sortable:not([class*='filtered']) tr:nth-child(even) td:not([class*='filtered']) {
67  background: #eff3ff;
68}
69
70.nobg {
71  padding: 0 0.5em;
72  vertical-align: bottom;
73  font-weight: bold;
74  color: #315d94;
75  color: black;
76  text-align: center;
77}
78
79.bg{
80  padding: 0 0.5em;
81  vertical-align: bottom;
82  font-weight: bold;
83  color: #315d94;
84  color: black;
85  text-align: center;
86  cursor: pointer;
87}
88
89.bg:hover {
90  background: #eff3aa;
91}
92
93.avg {
94  font-weight: bold;
95  text-align: center;
96}
97
98.data {
99  text-align: left;
100  white-space: nowrap;
101}
102
103.bggraph {
104  white-space: nowrap;
105}
106
107.file_input
108{
109  position: absolute;
110  width: 140px;
111  height: 26px;
112  overflow: hidden;
113}
114
115.file_input_button
116{
117  width: 140px;
118  position: absolute;
119  top: 0px;
120}
121
122.file_input_hidden
123{
124  font-size: 25px;
125  position: absolute;
126  right: 0px;
127  top: 0px;
128  opacity: 0;
129}
130</style>
131
132<script>
133var max_sample = 0;
134
135Array.max = function(array) {
136  return Math.max.apply( Math, array );
137}
138
139Array.min = function(array) {
140  return Math.min.apply( Math, array );
141};
142
143// Compute the average of an array, removing the min/max.
144Array.avg = function(array) {
145  var count = array.length;
146  var sum = 0;
147  var min = array[0];
148  var max = array[0];
149  for (var i = 0; i < count; i++) {
150    sum += array[i];
151    if (array[i] < min) {
152      min = array[i];
153    }
154    if (array[i] > max) {
155      max = array[i];
156    }
157  }
158  if (count >= 3) {
159    sum = sum - min - max;
160    count -= 2;
161  }
162  return sum / count;
163}
164
165// Compute the standard deviation of an array
166Array.stddev = function(array) {
167  var count = array.length;
168  var mean = Array.avg(array);
169  var variance = 0;
170  for (var i = 0; i < count; i++) {
171    var deviation = mean - array[i];
172    variance = variance + deviation * deviation;
173  }
174  variance = variance / count;
175  return Math.sqrt(variance);
176}
177
178function handleFileSelect(evt) {
179  var files = evt.target.files;
180  for (var i = 0, f; f = files[i]; i++) {
181    var reader = new FileReader();
182    reader.onload = function(evt) {
183      document.getElementById("testurl").value = evt.target.result;
184    }
185    reader.readAsText(f);
186  };
187}
188
189var THTAG = "th";
190var TDTAG = "td";
191var NONE_DISPLAY = "none";
192var CELL_DISPLAY = "table-cell";
193var BRIEF_VIEW = "Show More Details";
194var FULL_VIEW = "Hide Details";
195
196// Expand or shrink the result table.
197// Called when clicking button "Show More Details/Hide Details".
198function expand() {
199  if (document.getElementById("expand").value == BRIEF_VIEW) {
200    // From biref view to detailed view.	  
201    var headers = document.getElementsByTagName(THTAG);
202    var cells = document.getElementsByTagName(TDTAG);
203    
204    // Display the hidden metrics (both headers and data cells).
205    for (var i = 0; i < headers.length; i++) {
206      if (headers[i].style.display == NONE_DISPLAY) {
207        headers[i].style.display = CELL_DISPLAY;
208      }
209    }
210
211    for (i = 0; i < cells.length; i++) {
212      if (cells[i].style.display == NONE_DISPLAY) {
213        cells[i].style.display = CELL_DISPLAY;
214      }
215    }
216
217    document.getElementById("expand").value = FULL_VIEW;
218  } else {
219    // From detailed view to brief view.
220    var headers = document.getElementsByTagName(THTAG);
221    var cells = document.getElementsByTagName(TDTAG);
222
223    // Hide some metrics.
224    for (var i = 0; i < headers.length; i++) {
225      if (headers[i].style.display == CELL_DISPLAY) {
226        headers[i].style.display = NONE_DISPLAY;
227      }
228    }
229    
230    for (i = 0; i < cells.length; i++) {
231      if (cells[i].style.display == CELL_DISPLAY) {
232        cells[i].style.display = NONE_DISPLAY;
233      }
234    }
235    
236    document.getElementById("expand").value = BRIEF_VIEW;
237  }
238  
239  // Use cookie to store current expand/hide status. 
240  var lastValue = document.getElementById("expand").value;
241  document.cookie = "lastValue=" + lastValue;
242}
243
244// Reloading the page causes table to shrink (default original status).
245// Cookie remembers last status of table (in terms of expanded or shrunk).
246function restoreTable() {
247  if (document.cookie == "lastValue=Hide Details") {
248    var headers = document.getElementsByTagName(THTAG);
249    var cells = document.getElementsByTagName(TDTAG);
250    
251    for (var i = 0; i < headers.length; i++) {
252      if (headers[i].style.display == NONE_DISPLAY) {
253        headers[i].style.display = CELL_DISPLAY;
254      }
255    }
256    for (i = 0; i < cells.length; i++) {
257      if (cells[i].style.display == NONE_DISPLAY) {
258        cells[i].style.display = CELL_DISPLAY;
259      }
260    }
261    document.getElementById("expand").value = FULL_VIEW;
262  }
263}
264
265// A class to store the data to plot.
266function PData() {
267  this.xAxis = "Iteration(s)";
268  this.yAxis = "";
269  this.A = []; // Two data sets for comparison.
270  this.B = [];
271  this.avgA = [];  // Avg value is plotted as a line.
272  this.avgB = [];
273  this.maxA = 0;
274  this.maxB = 0;
275  this.countA = 0; // Size of the data sets.
276  this.countB = 0;
277
278  this.setYAxis = function (str) {
279    this.yAxis = str;
280  }
281
282  this.setAvg = function (arr, cha) {
283    if (cha == 'A') {
284      var avgA = Array.avg(arr);
285      for (var i = 1; i <= this.countA; i++) {
286        this.avgA.push([i, avgA]);
287      }       
288    } else if (cha == 'B') {
289      var avgB = Array.avg(arr);
290      for (var i = 1; i <= this.countB; i++) {
291        this.avgB.push([i, avgB]);
292      } 
293    }
294  }
295  
296  this.setMax = function (arr, cha) {
297    if (cha == 'A') {
298      this.maxA = Array.max(arr);
299    } else if (cha == 'B') {
300      this.maxB = Array.max(arr);
301    }
302  }
303   
304  // Add an entry to the array.
305  this.addArr = function (val, cha) {
306    if (cha == 'A') {
307      this.countA++;
308      this.A.push([this.countA, val]); 
309    } else if (cha == 'B') {
310      this.countB++;
311      this.B.push([this.countB, val]);
312    }
313  }
314  
315  // Plot the graph at the specified place.
316  this.plot = function (placeholder) {
317    $.plot(placeholder,
318      [// Line A 
319       {
320         data: this.A,
321         label: "A's " + this.yAxis + " in " + this.countA + " " + this.xAxis,
322         points: {
323           show: true
324         },
325         lines: {
326           show: true
327         }
328       },
329       
330       // Line B
331       { 
332         data: this.B,
333         label: "B's " + this.yAxis + " in " + this.countB + " " + this.xAxis,
334         points: {
335           show: true
336         },
337         lines: {
338           show: true
339         }
340       },
341       
342       // Line avgA
343       { 
344         data: this.avgA,
345         label: "A's avg " + this.yAxis,
346         dashes: {
347           show: true
348         }
349       },
350       
351       // Line avgB 
352       {
353         data: this.avgB,
354         label: "B's avg " + this.yAxis,
355         dashes: {
356           show: true
357         }
358       }],
359       
360       // Axis and legend setup.
361       { xaxis: { 
362           max: this.countA > this.countB ? this.countA : this.countB,
363           tickSize: 1,
364           tickDecimals: 0
365         },
366         yaxis: {
367           // Leave some space for legend.
368           max: this.maxA > this.maxB ? this.maxA * 1.5 : this.maxB * 1.5 
369         },
370         legend: {
371           backgroundOpacity: 0
372         }
373       });
374  }
375}
376
377// Compare the selected metric of the two selected data sets.
378function compare() {
379  var checkboxArr = document.getElementsByName("checkboxArr");
380  var radioArr = document.getElementsByName("radioArr");
381 
382  if (checkAmount(checkboxArr) != 2) {
383    alert("please select two rows to compare");
384    return;
385  }
386 
387  var rowIndexArr = getSelectedIndex(checkboxArr);
388  var colIndexArr = getSelectedIndex(radioArr);
389  // To this point, it is for sure that rowIndexArr has two elements 
390  // while colIndexArr has one.
391  var selectedRowA = rowIndexArr[0];
392  var selectedRowB = rowIndexArr[1];
393  var selectedCol = colIndexArr[0];
394
395  var extension = chrome.extension.getBackgroundPage();
396  var data = extension.results.data;
397  var selectedA = getSelectedResults(data,selectedRowA,selectedCol);
398  var selectedB = getSelectedResults(data,selectedRowB,selectedCol);
399  var yAxis = getMetricName(selectedCol);
400 
401  // Indicate A and B on selected rows.
402  checkboxArr[selectedRowA].parentElement.firstChild.data = "A";
403  checkboxArr[selectedRowB].parentElement.firstChild.data = "B";
404 
405  plot(selectedA, selectedB, yAxis);
406}
407
408// Show the comparison graph.
409function plot(A, B, axis) {
410  var plotData = new PData();
411  
412  plotData.setYAxis(axis);
413  for (var i = 0; i < A.length; i++) {
414    plotData.addArr(A[i],'A');
415  }
416  for (var i = 0; i < B.length; i++) {
417    plotData.addArr(B[i],'B');
418  }
419  plotData.setAvg(A,'A');
420  plotData.setAvg(B,'B');
421  plotData.setMax(A,'A');
422  plotData.setMax(B,'B');
423
424  var placeholder = document.getElementById("placeholder");
425  placeholder.style.display = "";  
426  plotData.plot(placeholder);
427}
428
429var METRIC = {"STARTLOAD": 0, "COMMITLOAD": 1, "DOCLOAD": 2, "PAINT": 3,
430               "TOTAL": 4, "REQUESTS": 5, "CONNECTS": 6, "READKB": 7,
431               "WRITEKB": 8, "READKBPS": 9, "WRITEKBPS": 10};
432
433// Retrieve the metric name from index.
434function getMetricName (index) {
435  switch (index) {
436    case METRIC.STARTLOAD:
437      return "Start Load Time";
438    case METRIC.COMMITLOAD:
439      return "Commit Load Time";
440    case METRIC.DOCLOAD:
441      return "Doc Load Time";
442    case METRIC.PAINT:
443      return "Paint Time";
444    case METRIC.TOTAL:
445      return "Total Load Time";
446    case METRIC.REQUESTS: 
447      return "# Requests";
448    case METRIC.CONNECTS: 
449      return "# Connects";
450    case METRIC.READKB:
451      return "Read KB";
452    case METRIC.WRITEKB:
453      return "Write KB";
454    case METRIC.READKBPS:
455      return "Read KBps";
456    case METRIC.WRITEKBPS:
457      return "Write KBps";
458    default:
459      return ""; 
460  }
461}
462
463// Get the results with a specific row (data set) and column (metric). 
464function getSelectedResults(arr, rowIndex, colIndex) {
465  switch (colIndex) {
466    case METRIC.STARTLOAD:
467      return arr[rowIndex].startLoadResults;
468    case METRIC.COMMITLOAD:
469      return arr[rowIndex].commitLoadResults;
470    case METRIC.DOCLOAD:
471      return arr[rowIndex].docLoadResults;
472    case METRIC.PAINT:
473      return arr[rowIndex].paintResults;
474    case METRIC.TOTAL:
475      return arr[rowIndex].totalResults;
476    case METRIC.REQUESTS:
477      return arr[rowIndex].requests;
478    case METRIC.CONNECTS:
479      return arr[rowIndex].connects;
480    case METRIC.READKB:
481      return arr[rowIndex].KbytesRead;
482    case METRIC.WRITEKB:
483      return arr[rowIndex].KbytesWritten;
484    case METRIC.READKBPS:
485      return arr[rowIndex].readbpsResults;
486    case METRIC.WRITEKBPS:
487      return arr[rowIndex].writebpsResults;
488    default:
489      return undefined;
490  }
491}
492
493// Ensure only two data sets (rows) are selected. 
494function checkAmount(arr) {
495  var amount = 0;
496  for (var i = 0; i < arr.length; i++) { 
497    if (arr[i].checked) {
498      amount++;
499    }
500  }
501  return amount;
502}
503
504// Get the index of selected row or column.
505function getSelectedIndex(arr) {
506  var selectedArr = new Array();
507  for (var i = 0; i < arr.length; i++) {
508    if(arr[i].checked) {
509      selectedArr.push(i);
510    }
511  }
512  return selectedArr;
513}
514
515// Repaint or hide the chart.
516function updateChart(caller) {
517  var placeholder = document.getElementById("placeholder");
518  if (caller.type == "radio") {
519    // Other radio button is clicked.
520    if (placeholder.style.display == "") {
521      compare();
522    }
523  } else {
524    // Other checkbox or clearing results is clicked.
525    if (placeholder.style.display == "") {
526      placeholder.style.display = "none";
527    }
528  }
529}
530
531// Clear indicators besides checkbox.
532function clearIndicator () {
533  var checkboxArr = document.getElementsByName("checkboxArr");
534  for (var i = 0; i < checkboxArr.length; i++) {
535    checkboxArr[i].parentElement.firstChild.data = "";
536  }
537}
538
539// Enable/Disable buttons according to checkbox change.
540function checkSelected () {
541  var checkboxArr = document.getElementsByName("checkboxArr"); 
542  if (checkAmount(checkboxArr) !=0) {
543    document.getElementById("clearSelected").disabled = false;
544    document.getElementById("compare").disabled = false;
545  } else {
546    document.getElementById("clearSelected").disabled = true;
547    document.getElementById("compare").disabled = true;
548  }
549}
550
551// Object to summarize everything
552var totals = {};
553
554// Compute the results for a data set.
555function computeDisplayResults(data) {
556  var count = data.data.length;
557  for (var i = 0; i < count; i++) {
558    var obj = data.data[i];
559    obj.displayTime = setDisplayTime(obj.timestamp);
560    var resultList = obj.totalResults; 
561    obj.mean = Array.avg(resultList);
562    obj.stddev = Array.stddev(resultList);
563    obj.stderr = obj.stddev / Math.sqrt(obj.iterations);
564    var ci = 1.96 * obj.stderr;
565    obj.cihigh = obj.mean + ci;
566    obj.cilow = obj.mean - ci;
567    obj.min = Array.min(resultList);
568    obj.max = Array.max(resultList);
569    obj.readbps = Array.avg(obj.readbpsResults);
570    obj.writebps = Array.avg(obj.writebpsResults);
571    obj.readKB = Array.avg(obj.KbytesRead);
572    obj.writeKB = Array.avg(obj.KbytesWritten);
573    obj.paintMean = Array.avg(obj.paintResults);
574    obj.startLoadMean = Array.avg(obj.startLoadResults);
575    obj.commitLoadMean = Array.avg(obj.commitLoadResults);
576    obj.docLoadMean = Array.avg(obj.docLoadResults);
577
578    obj.displayRequests = Array.avg(obj.requests);
579    obj.displayConnects = Array.avg(obj.connects);
580    obj.displaySpdySessions = Array.avg(obj.spdySessions);
581    
582    obj.displayDomNum = obj.domNum;
583    obj.displayMaxDepth = obj.maxDepth;
584    obj.displayMinDepth = obj.minDepth;
585    obj.displayAvgDepth = obj.avgDepth;
586    }
587  return count;
588}
589
590// Convert timestamp to readable string.
591function setDisplayTime(ts) {
592  var year = ts.getFullYear();
593  var mon = ts.getMonth()+1;
594  var date = ts.getDate();
595  var hrs = ts.getHours();
596  var mins = ts.getMinutes();
597  var secs = ts.getSeconds();
598  
599  mon = ( mon < 10 ? "0" : "" ) + mon;
600  date = ( date < 10 ? "0" : "" ) + date;
601  mins = ( mins < 10 ? "0" : "" ) + mins;
602  secs = ( secs < 10 ? "0" : "" ) + secs;
603  
604  return (year + "/" + mon + "/" + date + " " + hrs + ":" + mins + ":" + secs);
605}
606
607// Subtract the results from two data sets.
608// This function could be smarter about what it subtracts,
609// for now it just subtracts everything.
610// Returns true if it was able to compare the two data sets.
611function subtractData(data, baseline) {
612  var count = data.data.length;
613  if (baseline.data.length != count) {
614    return false;
615  }
616  for (var i = 0; i < count; i++) {
617    var obj = data.data[i];
618    var obj2 = baseline.data[i];
619
620    // The data sets are different.
621    if (obj.url != obj2.url ||
622        obj.iterations != obj2.iterations) {
623      return false;
624    }
625
626    obj.mean -= obj2.mean;
627    obj.stddev -= obj2.stddev;
628    obj.min -= obj2.min;
629    obj.max -= obj2.max;
630    obj.readbps -= obj2.readbps;
631    obj.writebps -= obj2.writebps;
632    obj.readKB -= obj2.readKB;
633    obj.writeKB -= obj2.writeKB;
634    obj.paintMean -= obj2.paintMean;
635    obj.startLoadMean -= obj2.startLoadMean;
636    obj.commitLoadMean -= obj2.commitLoadMean;
637    obj.docLoadMean -= obj2.docLoadMean;
638
639    obj.displayRequests -= obj2.displayRequests;
640    obj.displayConnects -= obj2.displayConnects;
641    obj.displaySpdySessions -= obj2.displaySpdySessions;
642  }
643  return true;
644}
645
646// Compute totals based on a data set.
647function computeTotals(data) {
648  var count = data.data.length;
649  for (var i = 0; i < count; i++) {
650    var obj = data.data[i];
651    totals.mean += obj.mean;
652    totals.paintMean += obj.paintMean;
653    totals.startLoadMean += obj.startLoadMean;
654    totals.commitLoadMean += obj.commitLoadMean;
655    totals.docLoadMean += obj.docLoadMean;
656  }
657}
658
659// Compute results for the data with an optional baseline.
660// If |baseline| is undefined, will compute the results of this
661// run.  Otherwise, computes the diff between this data and the baseline.
662function computeResults(data, baseline) {
663  totals = {};
664  totals.mean = 0;
665  totals.paintMean = 0;
666  totals.startLoadMean = 0;
667  totals.commitLoadMean = 0;
668  totals.docLoadMean = 0;
669
670  var count = computeDisplayResults(data);
671
672  if (baseline) {
673    computeDisplayResults(baseline);
674    if (!subtractData(data, baseline)) {
675      alert("These data sets are different");
676      document.getElementById("baseline").value = "";
677      return;
678    }
679  }
680
681  computeTotals(data);
682  totals.url = "(" + count + " urls)";
683  if (count > 0) {
684    totals.mean /= count;
685    totals.paintMean /= count;
686    totals.startLoadMean /= count;
687    totals.commitLoadMean /= count;
688    totals.docLoadMean /= count;
689  }
690
691  // Find the biggest average for our bar graph.
692  max_sample = 0;
693  for (var i = 0; i < data.data.length; i++) {
694    if (data.data[i].max > max_sample) {
695      max_sample = data.data[i].mean;
696    }
697  }
698}
699
700function jsinit() {
701  var extension = chrome.extension.getBackgroundPage();
702
703  // Run the template to show results
704  var data = extension.results;
705
706  // Get the baseline results
707  var elt = document.getElementById("baseline");
708  var baseline_json = document.getElementById("baseline").value;
709  var baseline;
710  if (baseline_json) {
711    try {
712      baseline = JSON.parse(baseline_json);
713    } catch (e) {
714      alert("JSON parse error: " + e);
715    }
716  }
717
718  // Compute
719  computeResults(data, baseline);
720
721  var context = new JsEvalContext(data);
722  context.setVariable('$width', 0);
723  context.setVariable('$samples', 0);
724  var template = document.getElementById("t");
725  jstProcess(context, template);
726
727  // Set the options
728  document.getElementById("iterations").value = extension.iterations;
729  document.getElementById("clearconns").checked = extension.clearConnections;
730  document.getElementById("clearcache").checked = extension.clearCache;
731  document.getElementById("enablespdy").checked = extension.enableSpdy;
732  setUrl(extension.testUrl);
733
734  if (!baseline) {
735    var json_data = JSON.stringify(data);
736    document.getElementById("json").value = json_data;
737  }
738
739  // Activate loading Urls from local file.
740  document.getElementById('files').addEventListener('change', 
741                                                    handleFileSelect, false);
742}
743
744function getWidth(mean, obj) {
745  var kMinWidth = 200;
746  var max_width = obj.offsetWidth;
747  if (max_width < kMinWidth) {
748    max_width = kMinWidth;
749  }
750  return Math.floor(max_width * (mean / max_sample));
751}
752
753// Apply configuration back to our extension
754function config() {
755  var extension = chrome.extension.getBackgroundPage();
756  var iterations = parseInt(document.getElementById("iterations").value);
757  var clearConnections = document.getElementById("clearconns").checked;
758  var clearCache = document.getElementById("clearcache").checked;
759  var enableSpdy = document.getElementById("enablespdy").checked;
760  if (iterations > 0) {
761    extension.iterations = iterations;
762    extension.clearConnections = clearConnections;
763    extension.clearCache = clearCache;
764    extension.enableSpdy = enableSpdy;
765  }
766}
767
768// Set the url in the benchmark url box.
769function setUrl(url) {
770  document.getElementById("testurl").value = url;
771}
772
773// Start the benchmark.
774function run() {
775  if (!chrome.benchmarking) {
776    alert("Warning:  Looks like you forgot to run chrome with " +
777          " --enable-benchmarking set.");
778    return;
779  }
780  var extension = chrome.extension.getBackgroundPage();
781  var testUrl = document.getElementById("testurl").value;
782  extension.testUrl = testUrl;
783  extension.run();
784}
785
786function showConfirm() {
787  var r = confirm("Are you sure to clear results?");
788  if (r) {
789    // Find out the event source element.
790    var evtSrc = window.event.srcElement; 
791    if (evtSrc.value == "Clear Selected") {
792      clearSelected();
793    } else if (evtSrc.value == "Clear All") {
794      clearResults();
795    }
796  }
797}
798
799// Clear the selected results
800function clearSelected() {
801  var extension = chrome.extension.getBackgroundPage();
802  var checkboxArr = document.getElementsByName("checkboxArr");
803  var rowIndexArr = getSelectedIndex(checkboxArr);
804  var currIndex;
805  for (var i = 0; i < rowIndexArr.length; i++) {
806    currIndex = rowIndexArr[i];
807    // Update the index of the original row in the modified array. 
808    currIndex -= i;
809    extension.results.data.splice(currIndex, 1);
810    document.location.reload(true);
811    updateChart(this);
812    jsinit();
813  }
814}
815
816// Clear all the results
817function clearResults() {
818  var extension = chrome.extension.getBackgroundPage();
819  extension.results = {};
820  extension.results.data = new Array();
821  document.getElementById("json").value = "";
822  document.getElementById("baseline").value = "";
823  updateChart(this);
824  jsinit();
825}
826
827// Export html table into CSV format.
828function export() {
829  var checkboxArr = document.getElementsByName("checkboxArr");
830  var rowNum = checkboxArr.length + 1; // # of data rows plus total-stats row. 
831  $('#t').table2CSV(rowNum);
832}
833
834// Toggle display of an element
835function toggle(id) {
836  var elt = document.getElementById(id);
837  if (elt.style.display == "none") {
838    elt.style.display = "block";
839  } else {
840    elt.style.display = "none";
841  }
842}
843</script>
844
845</head>
846
847<body onload="jsinit(); restoreTable()">
848
849<h1><div id="header">Page Benchmark Results</div></h1>
850
851<h1>Configuration <a href="http://sites.google.com/a/chromium.org/dev/chrome-benchmarking-extension" target="_blank" style="float:right"><font size="4%">Help</font></a></h1>
852
853<span>Iterations</span> 
854<input id="iterations" type="text" style="text-align:right" size="4">
855Clear Connections?<input id="clearconns" type="checkbox">
856Clear Cache?<input id="clearcache" type="checkbox">
857Enable Spdy?<input id="enablespdy" type="checkbox">
858<br>
859<span>URLs to load</span> <input type="text" id="testurl" size="80">
860<span class="file_input">
861<input class="file_input_button" type="button" value="Load URLs From File" />
862<input class="file_input_hidden" type="file" id="files" name="files[]" multiple />
863</span>
864<form onsubmit="config();run()">
865<input type="submit" value="Run">
866</form>
867<p>
868
869<h1>Results</h1>
870
871<input id="expand" type="button" value="Show More Details" onclick="expand()">
872<input id="clearSelected" type="button" value="Clear Selected" disabled="true" onclick="showConfirm()">
873<input id="clearAll" type="button" value="Clear All" onclick="showConfirm()">
874<input type="button" value="Export As .csv" onclick="export()">
875<table id="t" class="sortable" width="100%">
876  <tr>
877  <th width=35 class="nobg"></th>
878  <th width=215 class="nobg">url</th>
879  <th width=110 class="nobg" style="display:none">timestamp</th>
880  <th width=50 class="nobg">iterations</th>
881  <th width=50 class="nobg">via spdy</th>
882  <th width=50 class="bg" style="display:none">start load mean</th>
883  <th width=50 class="bg" style="display:none">commit load mean</th>
884  <th width=50 class="bg">doc load mean</th>
885  <th width=50 class="bg">paint mean</th>
886  <th width=50 class="bg">total load mean</th>
887  <th width=50 class="bg">stddev</th>
888  <th width=50 class="bg" style="display:none">stderr</th>
889  <th width=50 class="bg" style="display:none">95% CI-low</th>
890  <th width=50 class="bg" style="display:none">95% CI-high</th>
891  <th width=50 class="bg" style="display:none">min</th>
892  <th width=50 class="bg" style="display:none">max</th>
893  <th width=60 class="bg" style="display:none"># Requests</th>
894  <th width=60 class="bg" style="display:none"># Connects</th>
895  <th width=50 class="bg" style="display:none"># SPDY Sessions</th>
896  <th width=50 class="bg" style="display:none">Read KB</th>
897  <th width=50 class="bg" style="display:none">Write KB</th>
898  <th width=50 class="bg">Read KBps</th>
899  <th width=50 class="bg">Write KBps</th>
900  <th width=50 class="bg"># DOM</th>
901  <th width=70 class="bg" style="display:none">max DOM depth</th>
902  <th width=30 class="bg" style="display:none">min</th>
903  <th width=30 class="bg" style="display:none">avg</th>
904  <th samples class="nobg" style="display:none">total loan time samples</th>
905  </tr>
906
907  <tr id="t.total" jsselect="totals">
908  <td class="avg" jseval="1"></td>
909  <td class="url">TOTALS <span jscontent="url"></span></td>
910  <td class="avg" style="display:none"></td>
911  <td class="avg" jseval="1"></td>
912  <td class="avg" jseval="1"></td>
913  <td class="avg" style="display:none"><span jseval="val = startLoadMean.toFixed(1)" jscontent="val"></span></td>
914  <td class="avg" style="display:none"><span jseval="val = commitLoadMean.toFixed(1)" jscontent="val"></span></td>
915  <td class="avg"><span jseval="val = docLoadMean.toFixed(1)" jscontent="val"></span></td>
916  <td class="avg"><span jseval="val = paintMean.toFixed(1)" jscontent="val"></span></td>
917  <td class="avg"><span jseval="val = mean.toFixed(1)" jscontent="val"></span></td>
918  <td class="avg" jseval="1"></td>
919  <td class="avg" jseval="1"></td>
920  <td class="avg" jseval="1"></td>
921  <td class="avg" jseval="1"></td>
922  <td class="avg" jseval="1"></td>
923  <td class="avg" jseval="1"></td>
924  <td class="avg" jseval="1"></td>
925  <td class="avg" jseval="1"></td>
926  <td class="avg" jseval="1"></td>
927  <td class="avg" jseval="1"></td>
928  <td class="avg" jseval="1"></td>
929  <td class="avg" jseval="1"></td>
930  <td class="avg" jseval="1"></td>
931  <td class="avg" jseval="1"></td>
932  <td class="avg" jseval="1"></td>
933  <td class="avg" jseval="1"></td>
934  <td class="avg" jseval="1"></td>
935  <td class="data"></td>
936  </tr>
937
938  <tr jsselect="data">
939  <td align=right> <input type="checkbox" name="checkboxArr" onclick="updateChart(this);clearIndicator();checkSelected()"></td>
940  <td class="url" jseval="$width = getWidth($this.mean, this); url.length > 40 ? $suburl = url.substring(0,27) + '...' + url.substring(url.length-10, url.length) : $suburl=url"><div jsvalues=".style.width:$width" class="bggraph"><a jsvalues="href:$this.url" jscontent="$suburl"></a></div></td>
941  <td class="avg" style="display:none" jseval="val = displayTime" jscontent="val"></td>
942  <td class="avg" jseval="val = iterations" jscontent="val"></td>
943  <td class="avg" jseval="val = viaSpdy" jscontent="val"></td>
944  <td class="avg" style="display:none" jseval="val = startLoadMean.toFixed(1)" jscontent="val"></td>
945  <td class="avg" style="display:none" jseval="val = commitLoadMean.toFixed(1)" jscontent="val"></td>
946  <td class="avg" jseval="val = docLoadMean.toFixed(1)" jscontent="val"></td>
947  <td class="avg" jseval="val = paintMean.toFixed(1)" jscontent="val"></td>
948  <td class="avg" jseval="val = mean.toFixed(1)" jscontent="val"></td>
949  <td class="avg" jseval="val = stddev.toFixed(1)" jscontent="val"></td>
950  <td class="avg" style="display:none" jseval="val = stderr.toFixed(1)" jscontent="val"></td>
951  <td class="avg" style="display:none" jseval="val = cilow.toFixed(1)" jscontent="val"></td>
952  <td class="avg" style="display:none" jseval="val = cihigh.toFixed(1)" jscontent="val"></td>
953  <td class="avg" style="display:none" jseval="val = min.toFixed(1)" jscontent="val"></td>
954  <td class="avg" style="display:none" jseval="val = max.toFixed(1)" jscontent="val"></td>
955  <td class="avg" style="display:none" jseval="val = displayRequests.toFixed(1)" jscontent="val"></td>
956  <td class="avg" style="display:none" jseval="val = displayConnects.toFixed(1)" jscontent="val"></td>
957  <td class="avg" style="display:none" jseval="val = displaySpdySessions.toFixed(1)" jscontent="val"></td>
958  <td class="avg" style="display:none" jseval="val = readKB.toFixed(1)" jscontent="val"></td>
959  <td class="avg" style="display:none" jseval="val = writeKB.toFixed(1)" jscontent="val"></td>
960  <td class="avg" jseval="val = readbps.toFixed(1)" jscontent="val"></td>
961  <td class="avg" jseval="val = writebps.toFixed(1)" jscontent="val"></td>
962  <td class="avg" jseval="val = displayDomNum" jscontent="val"></td>
963  <td class="avg" style="display:none" jseval="val = displayMaxDepth" jscontent="val"></td>
964  <td class="avg" style="display:none" jseval="val = displayMinDepth" jscontent="val"></td>
965  <td class="avg" style="display:none" jseval="val = displayAvgDepth.toFixed(1)" jscontent="val"></td>
966  <td class="data" style="display:none"><span jsselect="totalResults"><span jscontent="$this"></span>,</span> </td>
967  </tr>
968  <tr jsdisplay="data.length == 0">
969  <td colspan=2>No tests have been run yet.</td>
970  </tr>
971  <tr jsdisplay="data.length > 1">
972  <td width=25 jseval="1"></td>
973  <td class="url" jseval="1"></td>
974  <td class="avg" style="display:none" jseval="1"></td>
975  <td class="avg" jseval="1"></td>
976  <td class="avg" jseval="1"></td>
977  <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"> </td>
978  <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td>
979  <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)"></td>
980  <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)"></td>
981  <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)" checked></td>
982  <td class="avg" jseval="1"></td>
983  <td class="avg" style="display:none" jseval="1"></td>
984  <td class="avg" style="display:none" jseval="1"></td>
985  <td class="avg" style="display:none" jseval="1"></td>
986  <td class="avg" style="display:none" jseval="1"></td>
987  <td class="avg" style="display:none" jseval="1"></td>
988  <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td>
989  <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td>
990  <td class="avg" style="display:none" jseval="1"></td>
991  <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td>
992  <td class="avg" style="display:none"><input name="radioArr" type="radio" onclick="updateChart(this)"></td>
993  <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)"></td>
994  <td class="avg"><input name="radioArr" type="radio" onclick="updateChart(this)"></td>
995  </tr>
996  <tr jsdisplay="data.length > 1">
997  <td> <input id="compare" type="button" value="Compare" disabled="true" onclick="compare()"></td>
998  </tr>
999</table>
1000<hr>
1001<center>
1002<div id="placeholder" style="width:430px;height:230px;display:none">graph place</div>
1003</center>
1004<span onclick="toggle('json')">JSON data</span><br>
1005<textarea style="display:none" type=text id=json rows=10 cols=50></textarea><p>
1006
1007<span onclick="toggle('baseline')">COMPARE to</span><br>
1008<textarea style="display:none" type=text id=baseline rows=10 cols=50
1009onchange="jsinit()"></textarea><p>
1010</body>
1011