OLD | NEW |
---|---|
(Empty) | |
1 <html> | |
2 <!-- | |
3 Copyright 2016 the V8 project authors. All rights reserved. Use of this source | |
4 code is governed by a BSD-style license that can be found in the LICENSE file. | |
5 --> | |
6 | |
7 <head> | |
8 <meta charset="UTF-8"> | |
9 <style> | |
10 body { | |
11 font-family: arial; | |
12 } | |
13 | |
14 table { | |
15 display: table; | |
16 border-spacing: 0px; | |
17 } | |
18 | |
19 tr { | |
20 border-spacing: 0px; | |
21 padding: 10px; | |
22 } | |
23 | |
24 td, | |
25 th { | |
26 padding: 3px 10px 3px 5px; | |
27 } | |
28 | |
29 .inline { | |
30 display: inline-block; | |
31 vertical-align: top; | |
32 } | |
33 | |
34 h2, | |
35 h3 { | |
36 margin-bottom: 0px; | |
37 } | |
38 | |
39 .hidden { | |
40 display: none; | |
41 } | |
42 | |
43 .view { | |
44 display: table; | |
45 } | |
46 | |
47 .column { | |
48 display: table-cell; | |
49 border-right: 1px black dotted; | |
50 min-width: 200px; | |
51 } | |
52 | |
53 .column .header { | |
54 padding: 0 10px 0 10px | |
55 } | |
56 | |
57 #column { | |
58 display: none; | |
59 } | |
60 | |
61 .list { | |
62 width: 100%; | |
63 } | |
64 | |
65 select { | |
66 width: 100% | |
67 } | |
68 | |
69 .list tbody { | |
70 cursor: pointer; | |
71 } | |
72 | |
73 .list tr:nth-child(even) { | |
74 background-color: #EFEFEF; | |
75 } | |
76 | |
77 .list tr:nth-child(even).selected { | |
78 background-color: #DDD; | |
79 } | |
80 | |
81 .list tr.child { | |
82 display: none; | |
83 } | |
84 | |
85 .list tr.child.visible { | |
86 display: table-row; | |
87 } | |
88 | |
89 .list .child .name { | |
90 padding-left: 20px; | |
91 } | |
92 | |
93 .list .parent td { | |
94 border-top: 1px solid #AAA; | |
95 } | |
96 | |
97 .list .total { | |
98 font-weight: bold | |
99 } | |
100 | |
101 .list tr.parent { | |
102 background-color: #FFF; | |
103 } | |
104 | |
105 .list tr.parent.selected { | |
106 background-color: #DDD; | |
107 } | |
108 | |
109 tr.selected { | |
110 background-color: #DDD; | |
111 } | |
112 | |
113 .list .position { | |
114 text-align: right; | |
115 display: none; | |
116 } | |
117 | |
118 .list div.toggle { | |
119 cursor: pointer; | |
120 } | |
121 | |
122 #column_0 .position { | |
123 display: table-cell; | |
124 } | |
125 | |
126 #column_0 .name { | |
127 display: table-cell; | |
128 } | |
129 | |
130 .list .name { | |
131 display: none; | |
132 white-space: nowrap; | |
133 } | |
134 | |
135 .value { | |
136 text-align: right; | |
137 } | |
138 | |
139 .selectedVersion { | |
140 font-weight: bold; | |
141 } | |
142 | |
143 #baseline { | |
144 width: auto; | |
145 } | |
146 | |
147 .compareSelector { | |
148 padding-bottom: 20px; | |
149 } | |
150 | |
151 .pageDetailTable tbody { | |
152 cursor: pointer | |
153 } | |
154 | |
155 #popover { | |
156 position: absolute; | |
157 transform: translateY(-50%) translateX(40px); | |
158 box-shadow: -2px 10px 44px -10px #000; | |
159 border-radius: 5px; | |
160 z-index: 1; | |
161 background-color: #FFF; | |
162 display: none; | |
163 } | |
164 | |
165 #popover table { | |
166 position: relative; | |
167 z-index: 1; | |
168 text-align: right; | |
169 } | |
170 | |
171 .popoverArrow { | |
172 background-color: #FFF; | |
173 position: absolute; | |
174 width: 30px; | |
175 height: 30px; | |
176 transform: translateY(-50%)rotate(45deg); | |
177 top: 50%; | |
178 left: -10px; | |
179 z-index: 0; | |
180 } | |
181 | |
182 #popover .name { | |
183 padding: 5px; | |
184 font-weight: bold; | |
185 text-align: center; | |
186 } | |
187 | |
188 #popover table .compare { | |
189 display: none | |
190 } | |
191 | |
192 #popover table.compare .compare { | |
193 display: table-cell; | |
194 } | |
195 </style> | |
196 <script> | |
197 "use strict" | |
198 | |
199 // Did anybody say monkeypatching? | |
200 if (!NodeList.prototype.forEach) { | |
201 NodeList.prototype.forEach = function(func) { | |
202 for (var i = 0; i < this.length; i++) { | |
203 func(this[i]); | |
204 } | |
205 } | |
206 } | |
207 | |
208 var versions; | |
209 var selectedPage; | |
210 var baselineVersion; | |
211 var selectedEntry; | |
212 | |
213 function initialize() { | |
214 var original = $("column"); | |
215 for (var i = 0; i < versions.length || i < 2; i++) { | |
216 // add column | |
217 var column = original.cloneNode(true); | |
218 column.id = "column_" + i; | |
219 // Fill in all versions | |
220 var select = column.querySelector(".version"); | |
221 select.id = "selectVersion_" + i; | |
222 // add all select options | |
223 versions.forEach((version) => { | |
224 var option = document.createElement("option"); | |
225 option.textContent = version.name; | |
226 option.version = version; | |
227 select.appendChild(option); | |
228 }); | |
229 // Fill in all page versions | |
230 select = column.querySelector(".pageVersion"); | |
231 select.id = "select_" + i; | |
232 // add all select options | |
233 versions.forEach((version) => { | |
234 var optgroup = document.createElement("optgroup"); | |
235 optgroup.label = version.name; | |
236 optgroup.version = version; | |
237 version.pages.forEach((page) => { | |
238 var option = document.createElement("option"); | |
239 option.textContent = page.name; | |
240 option.page = page; | |
241 optgroup.appendChild(option); | |
242 }); | |
243 select.appendChild(optgroup); | |
244 }); | |
245 $("view").appendChild(column); | |
246 } | |
247 var select = $('baseline'); | |
248 removeAllChildren(select); | |
249 select.appendChild(document.createElement('option')); | |
250 versions.forEach((version) => { | |
251 var option = document.createElement("option"); | |
252 option.textContent = version.name; | |
253 option.version = version; | |
254 select.appendChild(option); | |
255 }); | |
256 $('results').querySelectorAll('#results > .hidden').forEach((node) => { | |
257 toggleCssClass(node, 'hidden', false); | |
258 }); | |
259 } | |
260 | |
261 function showPage(firstPage) { | |
262 selectedPage = firstPage; | |
263 selectedPage.sort(); | |
264 showPageInColumn(firstPage, 0); | |
265 // Show the other versions of this page in the following columns. | |
266 var pageVersions = versions.pageVersions(firstPage.name); | |
267 var index = 1; | |
268 pageVersions.forEach((page) => { | |
269 if (page !== firstPage) { | |
270 showPageInColumn(page, index); | |
271 index++; | |
272 } | |
273 }); | |
274 showImpactList(selectedPage); | |
275 } | |
276 | |
277 function showPageInColumn(page, columnIndex) { | |
278 page.sort(); | |
279 var showDiff = (baselineVersion === undefined && columnIndex !== 0) || | |
280 (baselineVersion !== undefined && page.version !== baselineVersion); | |
281 var diffStatus = (td, a, b) => {}; | |
282 if (showDiff) { | |
283 if (baselineVersion !== undefined) { | |
284 diffStatus = (td, a, b) => { | |
285 if (a == 0) return; | |
286 td.style.color = a < 0 ? '#FF0000' : '#00BB00'; | |
287 }; | |
288 } else { | |
289 diffStatus = (td, a, b) => { | |
290 if (a == b) return; | |
291 var color; | |
292 var ratio = a / b; | |
293 if (ratio > 1) { | |
294 ratio = Math.min(Math.round((ratio - 1) * 255 * 10), 200); | |
295 color = '#' + ratio.toString(16) + "0000"; | |
296 } else { | |
297 ratio = Math.min(Math.round((1 - ratio) * 255 * 10), 200); | |
298 color = '#00' + ratio.toString(16) + "00"; | |
299 } | |
300 td.style.color = color; | |
301 } | |
302 } | |
303 } | |
304 | |
305 var column = $('column_' + columnIndex); | |
306 var select = $('select_' + columnIndex); | |
307 // Find the matching option | |
308 selectOption(select, (i, option) => { | |
309 return option.page == page | |
310 }); | |
311 var table = column.querySelector("table"); | |
312 var oldTbody = table.querySelector('tbody'); | |
313 var tbody = document.createElement('tbody'); | |
314 var referencePage = selectedPage; | |
315 page.forEachSorted(selectedPage, (parentEntry, entry, referenceEntry) => { | |
316 // Filter out entries that do not exist in the first column for the defa ult | |
nickie
2016/05/06 12:40:48
Comment line too long. Move "default" to next lin
| |
317 // view. | |
318 if (baselineVersion === undefined && referenceEntry && | |
319 referenceEntry.time == 0) { | |
320 return; | |
321 } | |
322 var tr = document.createElement('tr'); | |
323 tbody.appendChild(tr); | |
324 tr.entry = entry; | |
325 tr.parentEntry = parentEntry; | |
326 if (!parentEntry) { | |
327 tr.className = 'parent' | |
328 } else { | |
329 tr.className = 'child' | |
330 } | |
331 // Don't show entries that do not exist on the current page or if we com pare | |
nickie
2016/05/06 12:40:48
Same. Move "compare" to next line?
| |
332 // against the current page | |
333 if (entry !== undefined && page.version !== baselineVersion) { | |
334 // If we show a diff, use the baselineVersion as the referenceEntry | |
335 if (baselineVersion !== undefined) { | |
336 var baselineEntry = baselineVersion.getEntry(entry); | |
337 if (baselineEntry !== undefined) referenceEntry = baselineEntry | |
338 } | |
339 if (!parentEntry) { | |
340 var node = td(tr, '<div class="toggle">►</div>', 'position'); | |
341 node.firstChild.addEventListener('click', handleToggleGroup); | |
342 } else { | |
343 td(tr, entry.position == 0 ? '' : entry.position, 'position'); | |
344 } | |
345 td(tr, entry.name, 'name ' + entry.cssClass()); | |
346 diffStatus( | |
347 td(tr, ms(entry.time), 'value time'), | |
348 entry.time, referenceEntry.time); | |
349 diffStatus( | |
350 td(tr, percent(entry.timePercent), 'value time'), | |
351 entry.time, referenceEntry.time); | |
352 diffStatus( | |
353 td(tr, count(entry.count), 'value count'), | |
354 entry.count, referenceEntry.count); | |
355 } else if (baselineVersion !== undefined && referenceEntry && page.versi on !== | |
nickie
2016/05/06 12:40:48
Bad formatting. Break after last "&&"?
| |
356 baselineVersion) { | |
357 // Show comparison of entry that does not exist on the current page. | |
358 tr.entry = referenceEntry; | |
359 td(tr, '-', 'position'); | |
360 td(tr, referenceEntry.name, 'name'); | |
361 diffStatus( | |
362 td(tr, ms(referenceEntry.time), 'value time'), | |
363 referenceEntry.time, 0); | |
364 diffStatus( | |
365 td(tr, percent(referenceEntry.timePercent), 'value time'), | |
366 referenceEntry.timePercent, 0); | |
367 diffStatus( | |
368 td(tr, count(referenceEntry.count), 'value count'), | |
369 referenceEntry.count, 0); | |
370 } else { | |
371 // Display empty entry / baseline entry | |
372 if (entry !== undefined) { | |
373 if (!parentEntry) { | |
374 var node = td(tr, '<div class="toggle">►</div>', 'position'); | |
375 node.firstChild.addEventListener('click', handleToggleGroup); | |
376 } else { | |
377 td(tr, entry.position == 0 ? '' : entry.position, 'position'); | |
378 } | |
379 td(tr, entry.name, 'name'); | |
380 } else { | |
381 td(tr, '-', 'position'); | |
382 td(tr, '-', 'name'); | |
383 } | |
384 td(tr, '-', 'value time'); | |
385 td(tr, '-', 'value time'); | |
386 td(tr, '-', 'value count'); | |
387 } | |
388 }); | |
389 table.replaceChild(tbody, oldTbody); | |
390 var versionSelect = column.querySelector('select.version'); | |
391 selectOption(versionSelect, (index, option) => { | |
392 return option.version == page.version | |
393 }); | |
394 } | |
395 | |
396 function selectEntry(entry, updateSelectedPage) { | |
397 if (updateSelectedPage) { | |
398 entry = selectedPage.version.getEntry(entry); | |
399 } | |
400 var rowIndex; | |
401 var needsPageSwitch = updateSelectedPage && entry.page != selectedPage; | |
402 // If clicked in the detail row change the first column to that page. | |
403 if (needsPageSwitch) showPage(entry.page); | |
404 var childNodes = $('column_0').querySelector('.list tbody').childNodes; | |
405 for (var i = 0; i < childNodes.length; i++) { | |
406 if (childNodes[i].entry.name == entry.name) { | |
407 rowIndex = i; | |
408 break; | |
409 } | |
410 } | |
411 var firstEntry = childNodes[rowIndex].entry; | |
412 if (rowIndex) { | |
413 if (firstEntry.parent) showGroup(firstEntry.parent); | |
414 } | |
415 // Deselect all | |
416 $('view').querySelectorAll('.list tbody tr').forEach((tr) => { | |
417 toggleCssClass(tr, 'selected', false); | |
418 }); | |
419 // Select the entry row | |
420 $('view').querySelectorAll("tbody").forEach((body) => { | |
421 var row = body.childNodes[rowIndex]; | |
422 if (!row) return; | |
423 toggleCssClass(row, 'selected', row.entry && row.entry.name == | |
424 firstEntry.name); | |
425 }); | |
426 if (updateSelectedPage) { | |
427 entry = selectedEntry.page.version.getEntry(entry); | |
428 } | |
429 selectedEntry = entry; | |
430 showEntryDetail(entry); | |
431 } | |
432 | |
433 function showEntryDetail(entry) { | |
434 var table, tbody, entries; | |
435 table = $('detailView').querySelector('.versionDetailTable'); | |
436 tbody = document.createElement('tbody'); | |
437 if (entry !== undefined) { | |
438 $('detailView').querySelector('.versionDetail h3 span').innerHTML = | |
439 entry.name; | |
440 entries = versions.pageVersions(entry.page.name).map( | |
441 (page) => { | |
442 return page.get(entry.name) | |
443 }); | |
444 entries.sort((a, b) => { | |
445 return a.time - b.time | |
446 }); | |
447 entries.forEach((pageEntry) => { | |
448 if (pageEntry === undefined) return; | |
449 var tr = document.createElement('tr'); | |
450 if (pageEntry == entry) tr.className += 'selected'; | |
451 tr.entry = pageEntry; | |
452 td(tr, pageEntry.page.version.name, 'version'); | |
453 td(tr, pageEntry.position, 'value position'); | |
454 td(tr, ms(pageEntry.time), 'value time'); | |
455 td(tr, percent(pageEntry.timePercent), 'value time'); | |
456 td(tr, count(pageEntry.count), 'value count'); | |
457 tbody.appendChild(tr); | |
458 }); | |
459 } | |
460 table.replaceChild(tbody, table.querySelector('tbody')); | |
461 | |
462 table = $('detailView').querySelector('.pageDetailTable'); | |
463 tbody = document.createElement('tbody'); | |
464 if (entry !== undefined) { | |
465 var version = entry.page.version; | |
466 $('detailView').querySelector('.pageDetail h3 span').innerHTML = | |
467 version.name; | |
468 entries = version.pages.map( | |
469 (page) => { | |
470 return page.get(entry.name) | |
471 }); | |
472 entries.sort((a, b) => { | |
473 return b.timePercent - a.timePercent | |
474 }); | |
475 entries.forEach((pageEntry) => { | |
476 if (pageEntry === undefined) return; | |
477 var tr = document.createElement('tr'); | |
478 if (pageEntry === entry) tr.className += 'selected'; | |
479 tr.entry = pageEntry; | |
480 td(tr, pageEntry.page.name, 'name'); | |
481 td(tr, pageEntry.position, 'value position'); | |
482 td(tr, ms(pageEntry.time), 'value time'); | |
483 td(tr, percent(pageEntry.timePercent), 'value time'); | |
484 td(tr, count(pageEntry.count), 'value count'); | |
485 tbody.appendChild(tr); | |
486 }); | |
487 // show the total for all pages | |
488 var tds = table.querySelectorAll('tfoot td'); | |
489 tds[2].innerHTML = ms(entry.getTimeImpact()); | |
490 // Only show the percentage total if we are in diff mode: | |
491 if (baselineVersion !== undefined) { | |
492 tds[3].innerHTML = percent(entry.getTimePercentImpact()); | |
493 } else { | |
494 tds[3].innerHTML = '' | |
495 } | |
496 tds[4].innerHTML = count(entry.getCountImpact()); | |
497 } | |
498 table.replaceChild(tbody, table.querySelector('tbody')); | |
499 showImpactList(entry.page); | |
500 } | |
501 | |
502 function showImpactList(page) { | |
503 var impactView = $('detailView').querySelector('.impactView'); | |
504 impactView.querySelector('h3 span').innerHTML = page.version.name; | |
505 | |
506 var table = impactView.querySelector('table'); | |
507 var tbody = document.createElement('tbody'); | |
508 var version = page.version; | |
509 var entries = version.allEntries(); | |
510 if (selectedEntry !== undefined && selectedEntry.isGroup) { | |
511 impactView.querySelector('h3 span').innerHTML += " " + selectedEntry.nam e; | |
nickie
2016/05/06 12:40:48
Bad formatting. Break after "+="?
| |
512 entries = entries.filter((entry) => { | |
513 return entry.name == selectedEntry.name || | |
nickie
2016/05/06 12:40:48
"(entry) => { return ... }" can be written as "ent
| |
514 (entry.parent && entry.parent.name == selectedEntry.name) | |
515 }); | |
516 } | |
517 entries.sort((a, b) => { | |
518 return b.getTimePercentImpact() - a.getTimePercentImpact(); | |
519 }); | |
520 entries.forEach((entry) => { | |
521 var tr = document.createElement('tr'); | |
522 tr.entry = entry; | |
523 td(tr, entry.name, 'name'); | |
524 td(tr, ms(entry.getTimeImpact()), 'value time'); | |
525 td(tr, percent(entry.getTimePercentImpact()), 'value time'); | |
526 var topPages = entry.getPagesByPercentImpact().slice(0, 2) | |
527 .map((each) => { | |
528 return each.name + ' (' + percent(each.getEntry(entry).timePercent) + | |
nickie
2016/05/06 12:40:48
Bad formatting. You can skip the braces and the "
| |
529 ')' | |
530 }); | |
531 td(tr, topPages.join(', '), 'name'); | |
532 tbody.appendChild(tr); | |
533 }); | |
534 table.replaceChild(tbody, table.querySelector('tbody')); | |
535 } | |
536 | |
537 function showGroup(entry) { | |
538 toggleGroup(entry, true); | |
539 } | |
540 | |
541 function toggleGroup(group, show) { | |
542 $('view').querySelectorAll(".child").forEach((tr) => { | |
543 var entry = tr.parentEntry; | |
544 if (!entry) return; | |
545 if (entry.name !== group.name) return; | |
546 toggleCssClass(tr, 'visible', show); | |
547 }); | |
548 } | |
549 | |
550 function showPopover(entry) { | |
551 var popover = $('popover'); | |
552 popover.querySelector('td.name').innerHTML = entry.name; | |
553 popover.querySelector('td.page').innerHTML = entry.page.name; | |
554 setPopoverDetail(popover, entry, ''); | |
555 popover.querySelector('table').className = ""; | |
556 if (baselineVersion !== undefined) { | |
557 entry = baselineVersion.getEntry(entry); | |
558 if (entry === undefined) return; | |
559 setPopoverDetail(popover, entry, '.compare '); | |
560 popover.querySelector('table').className = "compare"; | |
561 } | |
562 } | |
563 | |
564 function setPopoverDetail(popover, entry, prefix) { | |
565 popover.querySelector(prefix + '.version').innerHTML = entry.page.version | |
566 .name; | |
567 popover.querySelector(prefix + '.time').innerHTML = ms(entry._time); | |
568 popover.querySelector(prefix + '.timeVariance').innerHTML = percent(entry | |
569 .timeVariancePercent); | |
570 popover.querySelector(prefix + '.percent').innerHTML = percent(entry.timeP ercent); | |
nickie
2016/05/06 12:40:48
Bad formatting. Break after "="?
| |
571 popover.querySelector(prefix + '.percentVariance').innerHTML = percent( | |
572 entry.timePercentVariancePercent); | |
573 popover.querySelector(prefix + '.count').innerHTML = count(entry._count); | |
574 popover.querySelector(prefix + '.countVariance').innerHTML = percent( | |
575 entry.timeVariancePercent); | |
576 popover.querySelector(prefix + '.timeImpact').innerHTML = ms(entry.getTime Impact()); | |
nickie
2016/05/06 12:40:48
Bad formatting. Break after "="?
| |
577 popover.querySelector(prefix + '.timePercentImpact').innerHTML = percent( | |
578 entry.getTimePercentImpact()); | |
579 } | |
580 | |
581 // ========================================================================= == | |
nickie
2016/05/06 12:40:48
Bad formatting. Fewer "="?
| |
582 // Helpers | |
583 function $(id) { | |
584 return document.getElementById(id) | |
585 } | |
586 | |
587 function removeAllChildren(node) { | |
588 while (node.firstChild) { | |
589 node.removeChild(node.firstChild); | |
590 } | |
591 } | |
592 | |
593 function selectOption(select, match) { | |
594 var options = select.options; | |
595 for (var i = 0; i < options.length; i++) { | |
596 if (match(i, options[i])) { | |
597 select.selectedIndex = i; | |
598 return; | |
599 } | |
600 } | |
601 } | |
602 | |
603 function td(tr, content, className) { | |
604 var td = document.createElement("td"); | |
605 td.innerHTML = content; | |
606 td.className = className | |
607 tr.appendChild(td); | |
608 return td | |
609 } | |
610 | |
611 function nodeIndex(node) { | |
612 var children = node.parentNode.childNodes, | |
613 i = 0; | |
614 for (; i < children.length; i++) { | |
615 if (children[i] == node) { | |
616 return i; | |
617 } | |
618 } | |
619 return -1; | |
620 } | |
621 | |
622 function toggleCssClass(node, cssClass, toggleState) { | |
623 var index = -1; | |
624 var classes; | |
625 if (node.className != undefined) { | |
626 classes = node.className.split(' '); | |
627 index = classes.indexOf(cssClass); | |
628 } | |
629 if (index == -1) { | |
630 if (toggleState === false) return; | |
631 node.className += ' ' + cssClass; | |
632 return; | |
633 } | |
634 if (toggleState === true) return; | |
635 classes.splice(index, 1); | |
636 node.className = classes.join(' '); | |
637 } | |
638 | |
639 function diffSign(value) { | |
640 if (value <= 0) return ''; | |
641 if (baselineVersion == undefined) return ''; | |
642 return '+'; | |
643 } | |
644 | |
645 function ms(time) { | |
646 return diffSign(time) + time.toFixed(1) + 'ms'; | |
647 } | |
648 | |
649 function count(time) { | |
650 return diffSign(time) + time.toFixed(0) + '#'; | |
651 } | |
652 | |
653 function percent(time) { | |
654 return diffSign(time) + time.toFixed(1) + '%'; | |
655 } | |
656 // ========================================================================= == | |
nickie
2016/05/06 12:40:48
Bad formatting. Fewer "="? Also, insert one blan
| |
657 // EventHandlers | |
658 function handleLoadFile() { | |
659 var files = document.getElementById("uploadInput").files; | |
660 var file = files[0]; | |
661 var reader = new FileReader(); | |
662 | |
663 reader.onload = function(evt) { | |
664 versions = Versions.fromJSON(JSON.parse(this.result)); | |
665 initialize() | |
666 showPage(versions.versions[0].pages[0]); | |
667 } | |
668 reader.readAsText(file); | |
669 } | |
670 | |
671 function handleToggleGroup(event) { | |
672 var group = event.target.parentNode.parentNode.entry; | |
673 toggleGroup(selectedPage.get(group.name)); | |
674 } | |
675 | |
676 function handleSelectPage(select, event) { | |
677 var option = select.options[select.selectedIndex]; | |
678 if (select.id == "select_0") { | |
679 showPage(option.page); | |
680 } else { | |
681 var columnIndex = select.id.split('_')[1]; | |
682 showPageInColumn(option.page, columnIndex); | |
683 } | |
684 } | |
685 | |
686 function handleSelectVersion(select, event) { | |
687 var option = select.options[select.selectedIndex]; | |
688 var version = option.version; | |
689 if (select.id == "selectVersion_0") { | |
690 var page = version.get(selectedPage.name); | |
691 showPage(page); | |
692 } else { | |
693 var columnIndex = select.id.split('_')[1]; | |
694 var pageSelect = $('select_' + columnIndex); | |
695 var page = pageSelect.options[select.selectedIndex].page; | |
696 page = version.get(page.name); | |
697 showPageInColumn(page, columnIndex); | |
698 } | |
699 } | |
700 | |
701 function handleSelectDetailRow(table, event) { | |
702 if (event.target.tagName != 'TD') return; | |
703 var tr = event.target.parentNode; | |
704 if (tr.tagName != 'TR') return; | |
705 if (tr.entry === undefined) return; | |
706 selectEntry(tr.entry, true); | |
707 } | |
708 | |
709 function handleSelectRow(table, event, fromDetail) { | |
710 if (event.target.tagName != 'TD') return; | |
711 var tr = event.target.parentNode; | |
712 if (tr.tagName != 'TR') return; | |
713 if (tr.entry === undefined) return; | |
714 selectEntry(tr.entry, false); | |
715 } | |
716 | |
717 function handleSelectBaseline(select, event) { | |
718 var option = select.options[select.selectedIndex]; | |
719 baselineVersion = option.version | |
720 showPage(selectedPage); | |
721 selectEntry(selectedEntry, true); | |
722 } | |
723 | |
724 function handleUpdatePopover(event) { | |
725 var popover = $('popover'); | |
726 popover.style.left = event.pageX + 'px'; | |
727 popover.style.top = event.pageY + 'px'; | |
728 popover.style.display = event.shiftKey ? 'block' : 'none'; | |
729 var target = event.target; | |
730 while (target.entry === undefined) { | |
731 target = target.parentNode; | |
732 if (!target) return; | |
733 } | |
734 showPopover(target.entry); | |
735 } | |
736 | |
737 // ========================================================================= == | |
nickie
2016/05/06 12:40:48
Bad formatting. Fewer "="?
| |
738 | |
739 class Versions { | |
740 constructor() { | |
741 this.versions = []; | |
742 } | |
743 add(version) { | |
744 this.versions.push(version) | |
745 } | |
746 pageVersions(name) { | |
747 var result = []; | |
748 this.versions.forEach((version) => { | |
749 var page = version.get(name); | |
750 if (page !== undefined) result.push(page); | |
751 }); | |
752 return result; | |
753 } | |
754 get length() { | |
755 return this.versions.length | |
756 } | |
757 get(index) { | |
758 return this.versions[index] | |
759 }; | |
760 forEach(f) { | |
761 this.versions.forEach(f); | |
762 } | |
763 sort() { | |
764 this.versions.sort((a, b) => { | |
765 if (a.name > b.name) return 1; | |
766 if (a.name < b.name) return -1; | |
767 return 0 | |
768 }) | |
769 } | |
770 } | |
771 Versions.fromJSON = function(json) { | |
772 var versions = new Versions(); | |
773 for (var version in json) { | |
774 versions.add(Version.fromJSON(version, json[version])); | |
775 } | |
776 versions.sort(); | |
777 return versions; | |
778 } | |
779 | |
780 class Version { | |
781 constructor(name) { | |
782 this.name = name; | |
783 this.pages = [] | |
784 } | |
785 add(page) { | |
786 this.pages.push(page); | |
787 } | |
788 indexOf(name) { | |
789 for (var i = 0; i < this.pages.length; i++) { | |
790 if (this.pages[i].name == name) return i; | |
791 } | |
792 return -1; | |
793 } | |
794 get(name) { | |
795 var index = this.indexOf(name); | |
796 if (0 <= index) return this.pages[index]; | |
797 return undefined | |
798 } | |
799 get length() { | |
800 return this.versions.length | |
801 } | |
802 getEntry(entry) { | |
803 if (entry === undefined) return undefined; | |
804 var page = this.get(entry.page.name); | |
805 if (page === undefined) return undefined; | |
806 return page.get(entry.name); | |
807 } | |
808 forEachEntry(fun) { | |
809 this.pages.forEach((page) => { | |
810 page.forEach(fun); | |
811 }); | |
812 } | |
813 allEntries() { | |
814 var map = new Map(); | |
815 this.forEachEntry((group, entry) => { | |
816 if (!map.has(entry.name)) map.set(entry.name, entry); | |
817 }); | |
818 return Array.from(map.values()); | |
819 } | |
820 getTotalValue(name, property) { | |
821 if (name === undefined) name = this.pages[0].total.name; | |
822 var sum = 0; | |
823 this.pages.forEach((page) => { | |
824 var entry = page.get(name); | |
825 if (entry !== undefined) sum += entry[property]; | |
826 }); | |
827 return sum; | |
828 } | |
829 getTotalTime(name) { | |
830 return this.getTotalValue(name, 'time'); | |
831 } | |
832 getTotalTimePercent(name) { | |
833 return this.getTotalValue(name, 'timePercent'); | |
834 } | |
835 getTotalCount(name) { | |
836 return this.getTotalValue(name, 'count'); | |
837 } | |
838 getPagesByPercentImpact(name) { | |
839 var sortedPages = | |
840 this.pages.filter((each) => { | |
841 return each.get(name) !== undefined | |
842 }); | |
843 sortedPages.sort((a, b) => { | |
844 return b.get(name).timePercent - a.get(name).timePercent; | |
845 }); | |
846 return sortedPages; | |
847 } | |
848 } | |
849 Version.fromJSON = function(name, data) { | |
850 var version = new Version(name); | |
851 for (var page in data) { | |
852 version.add(Page.fromJSON(version, page, data[page])); | |
853 } | |
854 return version; | |
855 } | |
856 | |
857 | |
858 class Page { | |
859 constructor(version, name) { | |
860 this.name = name; | |
861 this.total = new GroupedEntry('Total', /.*Total.*/); | |
862 this.unclassified = new UnclassifiedEntry(this) | |
863 this.groups = [ | |
864 this.total, | |
865 new GroupedEntry('IC', /.*IC.*/), | |
866 new GroupedEntry('Optimize', | |
867 /StackGuard|.*Optimize.*|.*Deoptimize.*|Recompile.*/), | |
868 new GroupedEntry('Compile', /.*Compile.*|Parse.*/), | |
869 new GroupedEntry('Callback', /.*Callback$/), | |
870 new GroupedEntry('API', /.*API.*/), | |
871 new GroupedEntry('GC', /GC|AllocateInTargetSpace/), | |
872 new GroupedEntry('JavaScript', /JS_Execution/), | |
873 this.unclassified | |
874 ]; | |
875 this.entryDict = new Map(); | |
876 this.groups.forEach((entry) => { | |
877 entry.page = this; | |
878 this.entryDict.set(entry.name, entry); | |
879 }); | |
880 this.version = version; | |
881 } | |
882 add(entry) { | |
883 entry.page = this; | |
884 this.entryDict.set(entry.name, entry); | |
885 var added = false; | |
886 this.groups.forEach((group) => { | |
887 if (!added) added = group.add(entry); | |
888 }); | |
889 if (added) return; | |
890 this.unclassified.push(entry); | |
891 } | |
892 get(name) { | |
893 return this.entryDict.get(name) | |
894 } | |
895 getEntry(entry) { | |
896 if (entry === undefined) return undefined; | |
897 return this.get(entry.name); | |
898 } | |
899 get length() { | |
900 return this.versions.length | |
901 } | |
902 forEachSorted(referencePage, func) { | |
903 // Iterate over all the entries in the order they appear on the referenc e page. | |
nickie
2016/05/06 12:40:48
Bad formatting. Move two words to next line?
| |
904 referencePage.forEach((parent, referenceEntry) => { | |
905 var entry; | |
906 if (parent) parent = this.entryDict.get(parent.name); | |
907 if (referenceEntry) entry = this.entryDict.get(referenceEntry.name); | |
908 func(parent, entry, referenceEntry); | |
909 }); | |
910 } | |
911 forEach(fun) { | |
912 this.forEachGroup((group) => { | |
913 fun(undefined, group); | |
914 group.forEach((entry) => { | |
915 fun(group, entry) | |
916 }); | |
917 }); | |
918 } | |
919 forEachGroup(fun) { | |
920 this.groups.forEach(fun) | |
921 } | |
922 sort() { | |
923 this.groups.sort((a, b) => { | |
924 return b.time - a.time; | |
925 }); | |
926 this.groups.forEach((group) => { | |
927 group.sort() | |
928 }); | |
929 } | |
930 } | |
931 Page.fromJSON = function(version, name, data) { | |
932 var page = new Page(version, name); | |
933 for (var i = 0; i < data.length; i++) { | |
934 page.add(Entry.fromJSON(i, data[data.length - i - 1])); | |
935 } | |
936 page.sort(); | |
937 return page | |
938 } | |
939 | |
940 | |
941 class Entry { | |
942 constructor(position, name, time, timeVariance, timeVariancePercent, | |
943 count, | |
944 countVariance, countVariancePercent) { | |
945 this.position = position; | |
946 this.name = name; | |
947 this._time = time; | |
948 this._timeVariance = timeVariance; | |
949 this._timeVariancePercent = timeVariancePercent; | |
950 this._count = count; | |
951 this.countVariance = countVariance; | |
952 this.countVariancePercent = countVariancePercent; | |
953 this.page = undefined; | |
954 this.parent = undefined; | |
955 } | |
956 getCompareWithBaseline(value, property) { | |
957 if (baselineVersion == undefined) return value; | |
958 var baselineEntry = baselineVersion.getEntry(this); | |
959 if (!baselineEntry) return value; | |
960 if (baselineVersion === this.page.version) return value; | |
961 return value - baselineEntry[property]; | |
962 } | |
963 cssClass() { | |
964 return '' | |
965 } | |
966 get time() { | |
967 return this.getCompareWithBaseline(this._time, '_time'); | |
968 } | |
969 get count() { | |
970 return this.getCompareWithBaseline(this._count, '_count'); | |
971 } | |
972 get timePercent() { | |
973 var value = this._time / this.page.total._time * 100; | |
974 if (baselineVersion == undefined) return value; | |
975 var baselineEntry = baselineVersion.getEntry(this); | |
976 if (!baselineEntry) return value; | |
977 return (this._time - baselineEntry._time) / this.page.total._time * | |
978 100; | |
979 } | |
980 get timePercentVariancePercent() { | |
981 // Get the absolute values for the percentages | |
982 return this.timeVariance / this.page.total._time * 100; | |
983 } | |
984 getTimeImpact() { | |
985 return this.page.version.getTotalTime(this.name); | |
986 } | |
987 getTimePercentImpact() { | |
988 return this.page.version.getTotalTimePercent(this.name); | |
989 } | |
990 getCountImpact() { | |
991 return this.page.version.getTotalCount(this.name); | |
992 } | |
993 getPagesByPercentImpact() { | |
994 return this.page.version.getPagesByPercentImpact(this.name); | |
995 } | |
996 get isGroup() { | |
997 return false | |
998 } | |
999 get timeVariance() { | |
1000 return this._timeVariance | |
1001 } | |
1002 get timeVariancePercent() { | |
1003 return this._timeVariancePercent | |
1004 } | |
1005 } | |
1006 Entry.fromJSON = function(position, data) { | |
1007 return new Entry(position, ...data); | |
1008 } | |
1009 | |
1010 | |
1011 class GroupedEntry extends Entry { | |
1012 constructor(name, regexp) { | |
1013 super(0, 'Group-' + name, 0, 0, 0, 0, 0, 0); | |
1014 this.regexp = regexp; | |
1015 this.entries = []; | |
1016 } | |
1017 add(entry) { | |
1018 if (!entry.name.match(this.regexp)) return false; | |
1019 this._time += entry.time; | |
1020 this._count += entry.count; | |
1021 // TODO: sum up variance | |
1022 this.entries.push(entry); | |
1023 entry.parent = this; | |
1024 return true; | |
1025 } | |
1026 forEach(fun) { | |
1027 if (baselineVersion === undefined) { | |
1028 this.entries.forEach(fun); | |
1029 return; | |
1030 } | |
1031 // If we have a baslineVersion to compare against show also all entries from the | |
nickie
2016/05/06 12:40:48
Bad formatting. Move two words to next line?
| |
1032 // other group. | |
1033 var tmpEntries = baselineVersion.getEntry(this) | |
1034 .entries.filter((entry) => { | |
1035 return this.page.get(entry.name) == undefined | |
1036 }); | |
1037 | |
1038 // The compared entries are sorted by absolute impact. | |
1039 tmpEntries = tmpEntries.map((entry) => { | |
1040 var tmpEntry = new Entry(0, entry.name, 0, 0, 0, 0, 0, 0); | |
1041 tmpEntry.page = this.page; | |
1042 return tmpEntry; | |
1043 }); | |
1044 tmpEntries = tmpEntries.concat(this.entries); | |
1045 tmpEntries.sort((a, b) => { | |
1046 return a.time - b.time | |
1047 }); | |
1048 tmpEntries.forEach(fun); | |
1049 } | |
1050 sort() { | |
1051 this.entries.sort((a, b) => { | |
1052 return b.time - a.time; | |
1053 }); | |
1054 } | |
1055 cssClass() { | |
1056 if (this.page.total == this) return 'total'; | |
1057 return ''; | |
1058 } | |
1059 get isGroup() { | |
1060 return true | |
1061 } | |
1062 getVariancePercentForProperty(property) { | |
1063 var sum = 0; | |
1064 this.entries.forEach((entry) => { | |
1065 sum += entry[property + 'Variance'] * entry[property + | |
1066 'Variance']; | |
1067 }); | |
1068 return Math.sqrt(sum) / this['_' + property] * 100; | |
1069 } | |
1070 get timeVariancePercent() { | |
1071 return this.getVariancePercentForProperty('time') | |
1072 } | |
1073 get timeVariance() { | |
1074 return this.getVariancePercentForProperty('timePercent') | |
1075 } | |
1076 } | |
1077 | |
1078 class UnclassifiedEntry extends GroupedEntry { | |
1079 constructor(page) { | |
1080 super('Unclassified'); | |
1081 this.page = page; | |
1082 this._time = undefined; | |
1083 this._count = undefined; | |
1084 } | |
1085 add(entry) { | |
1086 this.entries.push(entry); | |
1087 entry.parent = this; | |
1088 return true; | |
1089 } | |
1090 forEachPageGroup(fun) { | |
1091 this.page.forEachGroup((group) => { | |
1092 if (group == this) return; | |
1093 if (group == this.page.total) return; | |
1094 fun(group); | |
1095 }); | |
1096 } | |
1097 get time() { | |
1098 if (this._time !== undefined) { | |
1099 return this.getCompareWithBaseline(this._time, '_time'); | |
1100 } | |
1101 this._time = 0; | |
1102 var t = this.page.total.time; | |
1103 this.forEachPageGroup((group) => { | |
1104 t -= group.time; | |
1105 }); | |
1106 this._time = Math.round(t); | |
1107 return this._time; | |
1108 } | |
1109 get count() { | |
1110 if (this._count !== undefined) { | |
1111 return this.getCompareWithBaseline(this._count, '_count'); | |
1112 } | |
1113 this._count = 0; | |
1114 var c = this.page.total.count; | |
1115 this.forEachPageGroup((group) => { | |
1116 c -= group.count; | |
1117 }); | |
1118 this._count = Math.round(c); | |
1119 return this._count; | |
1120 } | |
1121 } | |
1122 </script> | |
1123 </head> | |
1124 | |
1125 <body onmousemove="handleUpdatePopover(event)"> | |
1126 <h1>Runtime Stats Komparator</h1> | |
1127 | |
1128 <div id="results"> | |
1129 <div class="inline"> | |
1130 <h2>Data</h2> | |
1131 <form name="fileForm"> | |
1132 <p> | |
1133 <input id="uploadInput" type="file" name="files" onchange="handleLoadF ile();"> | |
1134 </p> | |
1135 </form> | |
1136 </div> | |
1137 <div class="inline hidden"> | |
1138 <h2>Result</h2> | |
1139 <div class="compareSelector"> | |
1140 Compare against: <select id="baseline" onchange="handleSelectBaseli ne(this, event)"></select><br/> | |
1141 <span style="color: #060">Green</span> the selected version above perfor ms | |
1142 better on this measurement. | |
1143 </div> | |
1144 </div> | |
1145 <div id="view"> | |
1146 </div> | |
1147 | |
1148 <div id="detailView" class="hidden"> | |
1149 <h2></h2> | |
1150 <div class="versionDetail inline"> | |
1151 <h3><span></span></h3> | |
1152 <table class="versionDetailTable" onclick="handleSelectDetailRow(this, e vent);"> | |
1153 <thead> | |
1154 <tr> | |
1155 <th class="version">Version </th> | |
1156 <th class="position">Pos. </th> | |
1157 <th class="value time">Time▴ </th> | |
1158 <th class="value time">Percent </th> | |
1159 <th class="value count">Count </th> | |
1160 </tr> | |
1161 </thead> | |
1162 <tbody></tbody> | |
1163 </table> | |
1164 </div> | |
1165 <div class="pageDetail inline"> | |
1166 <h3><span></span></h3> | |
1167 <table class="pageDetailTable" onclick="handleSelectDetailRow(this, even t);"> | |
1168 <thead> | |
1169 <tr> | |
1170 <th class="page">Page </th> | |
1171 <th class="position">Pos. </th> | |
1172 <th class="value time">Time </th> | |
1173 <th class="value time">Percent▾ </th> | |
1174 <th class="value count">Count </th> | |
1175 </tr> | |
1176 </thead> | |
1177 <tfoot> | |
1178 <tr> | |
1179 <td class="page">Total:</td> | |
1180 <td class="position"></td> | |
1181 <td class="value time"></td> | |
1182 <td class="value time"></td> | |
1183 <td class="value count"></td> | |
1184 </tr> | |
1185 </tfoot> | |
1186 <tbody></tbody> | |
1187 </table> | |
1188 </div> | |
1189 <div class="impactView inline"> | |
1190 <h3>Impact list for <span></span></h3> | |
1191 <table class="pageDetailTable" onclick="handleSelectDetailRow(this, even t);"> | |
1192 <thead> | |
1193 <tr> | |
1194 <th class="page">Name </th> | |
1195 <th class="value time">Time </th> | |
1196 <th class="value time">Percent▾ </th> | |
1197 <th class="">Top Pages</th> | |
1198 </tr> | |
1199 </thead> | |
1200 <tbody></tbody> | |
1201 </table> | |
1202 </div> | |
1203 </div> | |
1204 | |
1205 <div id="column" class="column"> | |
1206 <div class="header"> | |
1207 <select class="version" onchange="handleSelectVersion(this, event);"></s elect> | |
1208 <select class="pageVersion" onchange="handleSelectPage(this, event);"></ select> | |
1209 </div> | |
1210 <table class="list" onclick="handleSelectRow(this, event);"> | |
1211 <thead> | |
1212 <tr> | |
1213 <th class="position">Pos. </th> | |
1214 <th class="name">Name </th> | |
1215 <th class="value time">Time </th> | |
1216 <th class="value time">Percent </th> | |
1217 <th class="value count">Count </th> | |
1218 </tr> | |
1219 </thead> | |
1220 <tbody></tbody> | |
1221 </table> | |
1222 </div> | |
1223 </div> | |
1224 | |
1225 <div class="inline"> | |
1226 <h2>Usage</h2> | |
1227 <ol> | |
1228 <li>Build chrome with the <a href="https://codereview.chromium.org/1923893 002">extended runtime callstats</a>.</li> | |
1229 <li>Run callstats.py with a web-page-replay archive: | |
1230 <pre>./callstats.py run \ | |
1231 --replay-bin=$CHROME_SRC/third_party/webpagereplay/replay.py \ | |
1232 --replay-wpr=top25.wpr \ | |
1233 --js-flags="" \ | |
1234 --with-chrome=$CHROME_SRC/out/Release/chrome \ | |
1235 --sites-file=top25.json</pre> | |
1236 </li> | |
1237 <li>Move results file to a subdirectory: <code>mkdir $VERSION; mv *.txt $V ERSION</code></li> | |
1238 <li>Repeat from step 1 with a different configuration (e.g. <code>--js-fla gs="--nolazy"</code>).</li> | |
1239 <li>Create the final results file: <code>./callstats.py json $VERSION1 $VE RSION2 > result.json</code></li> | |
1240 <li>Use <code>results.json</code> on this site.</code> | |
1241 </ol> | |
1242 </div> | |
1243 | |
1244 <div id="popover"> | |
1245 <div class="popoverArrow"></div> | |
1246 <table> | |
1247 <tr> | |
1248 <td class="name" colspan="2"></td> | |
1249 </tr> | |
1250 <tr> | |
1251 <td>Page:</td> | |
1252 <td class="page"></td> | |
1253 </tr> | |
1254 <tr> | |
1255 <td>Version:</td> | |
1256 <td><span class="version"></span></td> | |
1257 <td class="compare"><span class="version"></span></td> | |
1258 </tr> | |
1259 <tr> | |
1260 <td>Time:</td> | |
1261 <td><span class="time"></span> ± <span class="timeVariance"></span></td> | |
1262 <td class="compare"><span class="time"></span> ± <span class="timeVarian ce"></span></td> | |
1263 </tr> | |
1264 <tr> | |
1265 <td>Percent:</td> | |
1266 <td><span class="percent"></span> ± <span class="percentVariance"></span ></td> | |
1267 <td class="compare"><span class="percent"></span> ± <span class="percent Variance"></span></td> | |
1268 </tr> | |
1269 <tr> | |
1270 <td>Count:</td> | |
1271 <td><span class="count"></span> ± <span class="countVariance"></span></t d> | |
1272 <td class="compare"><span class="count"></span> ± <span class="countVari ance"></span></td> | |
1273 </tr> | |
1274 <tr> | |
1275 <td>Overall Impact:</td> | |
1276 <td><span class="timeImpact"></span> / <span class="timePercentImpact">< /span></td> | |
1277 <td class="compare"><span class="timeImpact"></span> / <span class="time PercentImpact"></span></td> | |
1278 </tr> | |
1279 </table> | |
1280 </div> | |
1281 | |
1282 </body> | |
1283 | |
1284 </html> | |
OLD | NEW |