<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[179763] trunk/Websites/perf.webkit.org</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/179763">179763</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2015-02-06 14:43:08 -0800 (Fri, 06 Feb 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>New perf dashboard should have multiple dashboard pages
https://bugs.webkit.org/show_bug.cgi?id=141339

Reviewed by Chris Dumez.

Added the support for multiple dashboard pages. Also added the status of the latest data point.
e.g. &quot;5% better than target&quot;

* public/v2/app.css: Tweaked the styles to work around the fact Ember.js creates empty script elements.
Also hid the border lines around charts on the dashboard page for a cleaner look.

* public/v2/app.js:
(App.IndexRoute): Added. Navigate to /dashboard/&lt;defaultDashboardName&gt; once the manifest.json is loaded.
(App.IndexRoute.beforeModel): Added.
(App.DashboardRoute): Added.
(App.DashboardRoute.model): Added. Return the dashboard specified by the name.
(App.CustomDashboardRoute): Added. This route is used for a customized dashboard specified by &quot;grid&quot;.
(App.CustomDashboardRoute.model): Create a dashboard model from &quot;grid&quot; query parameter.
(App.CustomDashboardRoute.renderTemplate): Use the dashboard template.
(App.DashboardController): Renamed from App.IndexController.
(App.DashboardController.modelChanged): Renamed from gridChanged. Removed the code to deal with &quot;grid&quot;
and &quot;defaultDashboard&quot; as these are taken care of by newly added routers.
(App.DashboardController.computeGrid): Renamed from updateGrid. No longer updates &quot;grid&quot; since this is
now done in actions.toggleEditMode.
(App.DashboardController.actions.toggleEditMode): Navigate to CustomDashboardRoute when start editing
an existing dashboard.

(App.Pane.computeStatus): Moved from App.PaneController so that to be called in App.Pane.latestStatus.
Also moved the code to compute the delta with respect to the previous data point from _updateDetails.
(App.Pane._relativeDifferentToLaterPointInTimeSeries): Ditto.
(App.Pane.latestStatus): Added. Used by the dashboard template to show the status of the latest result.

(App.createChartData): Added deltaFormatter to show less significant digits for differences.

(App.PaneController._updateDetails): Updated per changes to computeStatus.

* public/v2/chart-pane.css: Added style rules for the status labels on the dashboard.

* public/v2/data.js:
(TimeSeries.prototype.lastPoint): Added.

* public/v2/index.html: Prefetch manifest.json as soon as possible, show the latest data points' status
on the dashboard, and enumerate all predefined dashboards.

* public/v2/interactive-chart.js:
(App.InteractiveChartComponent._relayoutDataAndAxes): Slightly adjust the offset at which we show unit
for the dashboard page.

* public/v2/manifest.js:
(App.Dashboard): Inherit from App.NameLabelModel now that each predefined dashboard has a name.
(App.MetricSerializer.normalizePayload): Parse all predefined dashboards instead of a single dashboard.
IDs are generated for each dashboard for forward compatibility.
(App.Manifest):
(App.Manifest.dashboardByName): Added.
(App.Manifest.defaultDashboardName): Added.
(App.Manifest._fetchedManifest): Create dashboard model objects for all predefined ones.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgChangeLog">trunk/Websites/perf.webkit.org/ChangeLog</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2appcss">trunk/Websites/perf.webkit.org/public/v2/app.css</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2appjs">trunk/Websites/perf.webkit.org/public/v2/app.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2chartpanecss">trunk/Websites/perf.webkit.org/public/v2/chart-pane.css</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2datajs">trunk/Websites/perf.webkit.org/public/v2/data.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2indexhtml">trunk/Websites/perf.webkit.org/public/v2/index.html</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2interactivechartjs">trunk/Websites/perf.webkit.org/public/v2/interactive-chart.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2manifestjs">trunk/Websites/perf.webkit.org/public/v2/manifest.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkWebsitesperfwebkitorgChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/ChangeLog (179762 => 179763)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/ChangeLog        2015-02-06 22:14:20 UTC (rev 179762)
+++ trunk/Websites/perf.webkit.org/ChangeLog        2015-02-06 22:43:08 UTC (rev 179763)
</span><span class="lines">@@ -1,3 +1,62 @@
</span><ins>+2015-02-06  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        New perf dashboard should have multiple dashboard pages
+        https://bugs.webkit.org/show_bug.cgi?id=141339
+
+        Reviewed by Chris Dumez.
+
+        Added the support for multiple dashboard pages. Also added the status of the latest data point.
+        e.g. &quot;5% better than target&quot;
+
+        * public/v2/app.css: Tweaked the styles to work around the fact Ember.js creates empty script elements.
+        Also hid the border lines around charts on the dashboard page for a cleaner look.
+
+        * public/v2/app.js:
+        (App.IndexRoute): Added. Navigate to /dashboard/&lt;defaultDashboardName&gt; once the manifest.json is loaded.
+        (App.IndexRoute.beforeModel): Added.
+        (App.DashboardRoute): Added.
+        (App.DashboardRoute.model): Added. Return the dashboard specified by the name.
+        (App.CustomDashboardRoute): Added. This route is used for a customized dashboard specified by &quot;grid&quot;.
+        (App.CustomDashboardRoute.model): Create a dashboard model from &quot;grid&quot; query parameter.
+        (App.CustomDashboardRoute.renderTemplate): Use the dashboard template.
+        (App.DashboardController): Renamed from App.IndexController.
+        (App.DashboardController.modelChanged): Renamed from gridChanged. Removed the code to deal with &quot;grid&quot;
+        and &quot;defaultDashboard&quot; as these are taken care of by newly added routers.
+        (App.DashboardController.computeGrid): Renamed from updateGrid. No longer updates &quot;grid&quot; since this is
+        now done in actions.toggleEditMode.
+        (App.DashboardController.actions.toggleEditMode): Navigate to CustomDashboardRoute when start editing
+        an existing dashboard.
+
+        (App.Pane.computeStatus): Moved from App.PaneController so that to be called in App.Pane.latestStatus.
+        Also moved the code to compute the delta with respect to the previous data point from _updateDetails.
+        (App.Pane._relativeDifferentToLaterPointInTimeSeries): Ditto.
+        (App.Pane.latestStatus): Added. Used by the dashboard template to show the status of the latest result.
+
+        (App.createChartData): Added deltaFormatter to show less significant digits for differences.
+
+        (App.PaneController._updateDetails): Updated per changes to computeStatus.
+
+        * public/v2/chart-pane.css: Added style rules for the status labels on the dashboard.
+
+        * public/v2/data.js:
+        (TimeSeries.prototype.lastPoint): Added.
+
+        * public/v2/index.html: Prefetch manifest.json as soon as possible, show the latest data points' status
+        on the dashboard, and enumerate all predefined dashboards.
+
+        * public/v2/interactive-chart.js:
+        (App.InteractiveChartComponent._relayoutDataAndAxes): Slightly adjust the offset at which we show unit
+        for the dashboard page.
+
+        * public/v2/manifest.js:
+        (App.Dashboard): Inherit from App.NameLabelModel now that each predefined dashboard has a name.
+        (App.MetricSerializer.normalizePayload): Parse all predefined dashboards instead of a single dashboard.
+        IDs are generated for each dashboard for forward compatibility.
+        (App.Manifest):
+        (App.Manifest.dashboardByName): Added.
+        (App.Manifest.defaultDashboardName): Added.
+        (App.Manifest._fetchedManifest): Create dashboard model objects for all predefined ones.
+
</ins><span class="cx"> 2015-02-05  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Move commits viewer to the end of details view
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2appcss"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/app.css (179762 => 179763)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/app.css        2015-02-06 22:14:20 UTC (rev 179762)
+++ trunk/Websites/perf.webkit.org/public/v2/app.css        2015-02-06 22:43:08 UTC (rev 179763)
</span><span class="lines">@@ -201,12 +201,12 @@
</span><span class="cx">     margin: 0;
</span><span class="cx">     line-height: 1rem;
</span><span class="cx"> }
</span><del>-#navigation li:not(:last-child) a {
</del><ins>+#navigation li:not(:last-of-type) a {
</ins><span class="cx">     border-top-right-radius: 0;
</span><span class="cx">     border-bottom-right-radius: 0;
</span><span class="cx">     border-right: 0;
</span><span class="cx"> }
</span><del>-#navigation li:not(:first-child) a {
</del><ins>+#navigation li:not(:first-of-type) a {
</ins><span class="cx">     border-top-left-radius: 0;
</span><span class="cx">     border-bottom-left-radius: 0;
</span><span class="cx"> }
</span><span class="lines">@@ -397,7 +397,7 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> table.dashboard tbody td .chart {
</span><del>-    border: solid 1px #ddd;
</del><ins>+    border: solid 0px #ddd;
</ins><span class="cx">     border-radius: 0.5rem;
</span><span class="cx">     margin: 0.5rem 0.5rem;
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2appjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/app.js (179762 => 179763)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/app.js        2015-02-06 22:14:20 UTC (rev 179762)
+++ trunk/Websites/perf.webkit.org/public/v2/app.js        2015-02-06 22:43:08 UTC (rev 179763)
</span><span class="lines">@@ -1,6 +1,8 @@
</span><span class="cx"> window.App = Ember.Application.create();
</span><span class="cx"> 
</span><span class="cx"> App.Router.map(function () {
</span><ins>+    this.resource('customDashboard', {path: 'dashboard/custom'});
+    this.resource('dashboard', {path: 'dashboard/:name'});
</ins><span class="cx">     this.resource('charts', {path: 'charts'});
</span><span class="cx">     this.resource('analysis', {path: 'analysis'});
</span><span class="cx">     this.resource('analysisTask', {path: 'analysis/task/:taskId'});
</span><span class="lines">@@ -54,29 +56,48 @@
</span><span class="cx">     }.property('platformId', 'metricId'),
</span><span class="cx"> });
</span><span class="cx"> 
</span><del>-App.IndexController = Ember.Controller.extend({
</del><ins>+App.IndexRoute = Ember.Route.extend({
+    beforeModel: function ()
+    {
+        var self = this;
+        App.Manifest.fetch(this.store).then(function () {
+            self.transitionTo('dashboard', App.Manifest.defaultDashboardName());
+        });
+    },
+});
+
+App.DashboardRoute = Ember.Route.extend({
+    model: function (param)
+    {
+        return App.Manifest.fetch(this.store).then(function () {
+            return App.Manifest.dashboardByName(param.name);
+        });
+    },
+});
+
+App.CustomDashboardRoute = Ember.Route.extend({
+    controllerName: 'dashboard',
+    model: function (param)
+    {
+        return this.store.createRecord('dashboard', {serialized: param.grid});
+    },
+    renderTemplate: function()
+    {
+        this.render('dashboard');
+    }
+});
+
+App.DashboardController = Ember.Controller.extend({
</ins><span class="cx">     queryParams: ['grid', 'numberOfDays'],
</span><del>-    _previousGrid: {},
</del><span class="cx">     headerColumns: [],
</span><span class="cx">     rows: [],
</span><span class="cx">     numberOfDays: 7,
</span><span class="cx">     editMode: false,
</span><span class="cx"> 
</span><del>-    gridChanged: function ()
</del><ins>+    modelChanged: function ()
</ins><span class="cx">     {
</span><del>-        var grid = this.get('grid');
-        if (grid === this._previousGrid)
-            return;
-
-        var dashboard = null;
-        if (grid) {
-            dashboard = this.store.createRecord('dashboard', {serialized: grid});
-            if (!dashboard.get('headerColumns').length)
-                dashboard = null;
-        }
</del><ins>+        var dashboard = this.get('model');
</ins><span class="cx">         if (!dashboard)
</span><del>-            dashboard = App.Manifest.get('defaultDashboard');
-        if (!dashboard)
</del><span class="cx">             return;
</span><span class="cx"> 
</span><span class="cx">         var headerColumns = dashboard.get('headerColumns');
</span><span class="lines">@@ -95,9 +116,9 @@
</span><span class="cx">         }));
</span><span class="cx"> 
</span><span class="cx">         this.set('emptyRow', new Array(columnCount));
</span><del>-    }.observes('grid', 'App.Manifest.defaultDashboard').on('init'),
</del><ins>+    }.observes('model').on('init'),
</ins><span class="cx"> 
</span><del>-    updateGrid: function()
</del><ins>+    computeGrid: function()
</ins><span class="cx">     {
</span><span class="cx">         var headers = this.get('headerColumns').map(function (header) { return header.label; });
</span><span class="cx">         var table = [headers].concat(this.get('rows').map(function (row) {
</span><span class="lines">@@ -106,8 +127,7 @@
</span><span class="cx">                 return platformAndMetric[0] || platformAndMetric[1] ? platformAndMetric : [];
</span><span class="cx">             }));
</span><span class="cx">         }));
</span><del>-        this._previousGrid = JSON.stringify(table);
-        this.set('grid', this._previousGrid);
</del><ins>+        return JSON.stringify(table);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     _sharedDomainChanged: function ()
</span><span class="lines">@@ -173,8 +193,10 @@
</span><span class="cx">         toggleEditMode: function ()
</span><span class="cx">         {
</span><span class="cx">             this.toggleProperty('editMode');
</span><del>-            if (!this.get('editMode'))
-                this.updateGrid();
</del><ins>+            if (this.get('editMode'))
+                this.transitionToRoute('dashboard', 'custom', {name: null, queryParams: {grid: this.computeGrid()}});
+            else
+                this.set('grid', this.computeGrid());
</ins><span class="cx">         },
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="lines">@@ -362,7 +384,56 @@
</span><span class="cx">         if (typeof(id) == &quot;string&quot;)
</span><span class="cx">             return !!id.match(/^[A-Za-z0-9_]+$/);
</span><span class="cx">         return false;
</span><del>-    }
</del><ins>+    },
+    computeStatus: function (currentPoint, previousPoint)
+    {
+        var chartData = this.get('chartData');
+        var diffFromBaseline = this._relativeDifferentToLaterPointInTimeSeries(currentPoint, chartData.baseline);
+        var diffFromTarget = this._relativeDifferentToLaterPointInTimeSeries(currentPoint, chartData.target);
+
+        var label = '';
+        var className = '';
+        var formatter = d3.format('.3p');
+
+        var smallerIsBetter = chartData.smallerIsBetter;
+        if (diffFromBaseline !== undefined &amp;&amp; diffFromBaseline &gt; 0 == smallerIsBetter) {
+            label = formatter(Math.abs(diffFromBaseline)) + ' ' + (smallerIsBetter ? 'above' : 'below') + ' baseline';
+            className = 'worse';
+        } else if (diffFromTarget !== undefined &amp;&amp; diffFromTarget &lt; 0 == smallerIsBetter) {
+            label = formatter(Math.abs(diffFromTarget)) + ' ' + (smallerIsBetter ? 'below' : 'above') + ' target';
+            className = 'better';
+        } else if (diffFromTarget !== undefined)
+            label = formatter(Math.abs(diffFromTarget)) + ' until target';
+
+        var valueDelta = previousPoint ? chartData.deltaFormatter(currentPoint.value - previousPoint.value) : null;
+        if (valueDelta &amp;&amp; valueDelta &gt; 0)
+            valueDelta = '+' + valueDelta;
+
+        return {className: className, label: label, currentValue: chartData.formatter(currentPoint.value), valueDelta: valueDelta};
+    },
+    _relativeDifferentToLaterPointInTimeSeries: function (currentPoint, timeSeries)
+    {
+        if (!currentPoint || !timeSeries)
+            return undefined;
+
+        var referencePoint = timeSeries.findPointAfterTime(currentPoint.time);
+        if (!referencePoint)
+            return undefined;
+
+        return (currentPoint.value - referencePoint.value) / referencePoint.value;
+    },
+    latestStatus: function ()
+    {
+        var chartData = this.get('chartData');
+        if (!chartData || !chartData.current)
+            return null;
+
+        var lastPoint = chartData.current.lastPoint();
+        if (!lastPoint)
+            return null;
+
+        return this.computeStatus(lastPoint, chartData.current.previousPoint(lastPoint));
+    }.property('chartData'),
</ins><span class="cx"> });
</span><span class="cx"> 
</span><span class="cx"> App.createChartData = function (data)
</span><span class="lines">@@ -374,6 +445,7 @@
</span><span class="cx">         target: runs.target ? runs.target.timeSeriesByCommitTime() : null,
</span><span class="cx">         unit: data.unit,
</span><span class="cx">         formatter: data.useSI ? d3.format('.4s') : d3.format('.4g'),
</span><ins>+        deltaFormatter: data.useSI ? d3.format('.2s') : d3.format('.2g'),
</ins><span class="cx">         smallerIsBetter: data.smallerIsBetter,
</span><span class="cx">     };
</span><span class="cx"> }
</span><span class="lines">@@ -730,15 +802,15 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         var currentMeasurement;
</span><del>-        var oldMeasurement;
</del><ins>+        var previousPoint;
</ins><span class="cx">         if (currentPoint) {
</span><span class="cx">             currentMeasurement = currentPoint.measurement;
</span><del>-            var previousPoint = currentPoint.series.previousPoint(currentPoint);
-            oldMeasurement = previousPoint ? previousPoint.measurement : null;
</del><ins>+            previousPoint = currentPoint.series.previousPoint(currentPoint);
</ins><span class="cx">         } else {
</span><span class="cx">             currentMeasurement = selectedPoints[selectedPoints.length - 1].measurement;
</span><del>-            oldMeasurement = selectedPoints[0].measurement;            
</del><ins>+            previousPoint = selectedPoints[0];
</ins><span class="cx">         }
</span><ins>+        var oldMeasurement = previousPoint ? previousPoint.measurement : null;
</ins><span class="cx"> 
</span><span class="cx">         var formattedRevisions = currentMeasurement.formattedRevisions(oldMeasurement);
</span><span class="cx">         var revisions = App.Manifest.get('repositories')
</span><span class="lines">@@ -762,15 +834,8 @@
</span><span class="cx">                 buildURL = builder.urlFromBuildNumber(buildNumber);
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        var chartData = this.get('chartData');
-        var valueDiff = oldMeasurement ? chartData.formatter(currentMeasurement.mean() - oldMeasurement.mean()) : null;
-        if (valueDiff &amp;&amp; valueDiff &gt; 0)
-            valueDiff = '+' + valueDiff;
-
</del><span class="cx">         this.set('details', Ember.Object.create({
</span><del>-            status: this._computeStatus(currentPoint),
-            currentValue: chartData.formatter(currentMeasurement.mean()),
-            valueDiff: valueDiff,
</del><ins>+            status: this.get('model').computeStatus(currentPoint, previousPoint),
</ins><span class="cx">             buildNumber: buildNumber,
</span><span class="cx">             buildURL: buildURL,
</span><span class="cx">             buildTime: currentMeasurement.formattedBuildTime(),
</span><span class="lines">@@ -783,40 +848,6 @@
</span><span class="cx">         var points = this.get('selectedPoints');
</span><span class="cx">         this.set('cannotAnalyze', !this.get('newAnalysisTaskName') || !points || points.length &lt; 2);
</span><span class="cx">     }.observes('newAnalysisTaskName'),
</span><del>-    _computeStatus: function (currentPoint)
-    {
-        var chartData = this.get('chartData');
-
-        var diffFromBaseline = this._relativeDifferentToLaterPointInTimeSeries(currentPoint, chartData.baseline);
-        var diffFromTarget = this._relativeDifferentToLaterPointInTimeSeries(currentPoint, chartData.target);
-
-        var label = '';
-        var className = '';
-        var formatter = d3.format('.3p');
-
-        var smallerIsBetter = chartData.smallerIsBetter;
-        if (diffFromBaseline !== undefined &amp;&amp; diffFromBaseline &gt; 0 == smallerIsBetter) {
-            label = formatter(Math.abs(diffFromBaseline)) + ' ' + (smallerIsBetter ? 'above' : 'below') + ' baseline';
-            className = 'worse';
-        } else if (diffFromTarget !== undefined &amp;&amp; diffFromTarget &lt; 0 == smallerIsBetter) {
-            label = formatter(Math.abs(diffFromTarget)) + ' ' + (smallerIsBetter ? 'below' : 'above') + ' target';
-            className = 'better';
-        } else if (diffFromTarget !== undefined)
-            label = formatter(Math.abs(diffFromTarget)) + ' until target';
-
-        return {className: className, label: label};
-    },
-    _relativeDifferentToLaterPointInTimeSeries: function (currentPoint, timeSeries)
-    {
-        if (!currentPoint || !timeSeries)
-            return undefined;
-        
-        var referencePoint = timeSeries.findPointAfterTime(currentPoint.time);
-        if (!referencePoint)
-            return undefined;
-
-        return (currentPoint.value - referencePoint.value) / referencePoint.value;
-    }
</del><span class="cx"> });
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2chartpanecss"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/chart-pane.css (179762 => 179763)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/chart-pane.css        2015-02-06 22:14:20 UTC (rev 179762)
+++ trunk/Websites/perf.webkit.org/public/v2/chart-pane.css        2015-02-06 22:43:08 UTC (rev 179763)
</span><span class="lines">@@ -320,17 +320,23 @@
</span><span class="cx"> .chart path.baseline {
</span><span class="cx">     stroke: #f66;
</span><span class="cx"> }
</span><del>-.chart-pane .status .worse {
</del><ins>+.chart-pane .status .worse,
+.dashboard-status .worse {
</ins><span class="cx">     color: #c33;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .chart path.target {
</span><span class="cx">     stroke: #66f;
</span><span class="cx"> }
</span><del>-.chart-pane .status .better {
</del><ins>+.chart-pane .status .better,
+.dashboard-status .better {
</ins><span class="cx">     color: #33c;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.dashboard-status .status-label {
+    margin-left: 1rem;
+}
+
</ins><span class="cx"> .chart .axis,
</span><span class="cx"> .chart .domain {
</span><span class="cx">   fill: none;
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2datajs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/data.js (179762 => 179763)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/data.js        2015-02-06 22:14:20 UTC (rev 179762)
+++ trunk/Websites/perf.webkit.org/public/v2/data.js        2015-02-06 22:43:08 UTC (rev 179763)
</span><span class="lines">@@ -405,6 +405,13 @@
</span><span class="cx"> 
</span><span class="cx"> TimeSeries.prototype.series = function () { return this._series; }
</span><span class="cx"> 
</span><ins>+TimeSeries.prototype.lastPoint = function ()
+{
+    if (!this._series || !this._series.length)
+        return null;
+    return this._series[this._series.length - 1];
+}
+
</ins><span class="cx"> TimeSeries.prototype.previousPoint = function (point)
</span><span class="cx"> {
</span><span class="cx">     if (!point.seriesIndex)
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2indexhtml"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/index.html (179762 => 179763)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/index.html        2015-02-06 22:14:20 UTC (rev 179762)
+++ trunk/Websites/perf.webkit.org/public/v2/index.html        2015-02-06 22:43:08 UTC (rev 179763)
</span><span class="lines">@@ -3,7 +3,15 @@
</span><span class="cx"> &lt;head&gt;
</span><span class="cx">     &lt;meta charset=&quot;utf-8&quot;&gt;
</span><span class="cx">     &lt;title&gt;WebKit Performance Monitor (Beta)&lt;/title&gt;
</span><ins>+
+    &lt;link rel=&quot;prefetch&quot; href=&quot;../data/manifest.json&quot;&gt;
+    &lt;script type=&quot;application/json&quot; src=&quot;../data/manifest.json&quot;&gt;&lt;/script&gt;
+
+    &lt;link rel=&quot;stylesheet&quot; href=&quot;app.css&quot;&gt;
+    &lt;link rel=&quot;stylesheet&quot; href=&quot;chart-pane.css&quot;&gt;
+
</ins><span class="cx">     &lt;script src=&quot;js/jquery.min.js&quot; defer&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;js/jquery.min.js&quot; defer&gt;&lt;/script&gt;
</ins><span class="cx">     &lt;script src=&quot;js/handlebars.js&quot; defer&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;js/ember.js&quot; defer&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;js/ember-data.js&quot; defer&gt;&lt;/script&gt;
</span><span class="lines">@@ -16,10 +24,8 @@
</span><span class="cx">     &lt;script src=&quot;popup.js&quot; defer&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;interactive-chart.js&quot; defer&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;commits-viewer.js&quot; defer&gt;&lt;/script&gt;
</span><del>-    &lt;link rel=&quot;stylesheet&quot; href=&quot;app.css&quot;&gt;
-    &lt;link rel=&quot;stylesheet&quot; href=&quot;chart-pane.css&quot;&gt;
</del><span class="cx"> 
</span><del>-    &lt;script type=&quot;text/x-handlebars&quot; data-template-name=&quot;index&quot;&gt;
</del><ins>+    &lt;script type=&quot;text/x-handlebars&quot; data-template-name=&quot;dashboard&quot;&gt;
</ins><span class="cx">         &lt;header id=&quot;header&quot;&gt;
</span><span class="cx">             {{partial &quot;navbar&quot;}}
</span><span class="cx">             {{view App.NumberOfDaysControlView tagName=&quot;ul&quot; numberOfDays=numberOfDays}}
</span><span class="lines">@@ -78,11 +84,19 @@
</span><span class="cx">                                 {{/if}}
</span><span class="cx">                             {{else}}
</span><span class="cx">                                 {{#if chartData}}
</span><ins>+                                    &lt;div class=&quot;dashboard-status&quot;&gt;
+                                        {{#if latestStatus}}
+                                            {{latestStatus.currentValue}} {{chartData.unit}}
+                                            {{#if latestStatus.label}}
+                                                &lt;span {{bind-attr class=&quot;:status-label latestStatus.className&quot;}}&gt;{{latestStatus.label}}&lt;/span&gt;
+                                            {{/if}}
+                                        {{/if}}
+                                    &lt;/div&gt;
</ins><span class="cx">                                     {{#link-to 'charts' (query-params paneList=paneList since=controller.since)}}
</span><del>-                                    {{interactive-chart
-                                        chartData=chartData
-                                        domain=controller.sharedDomain
-                                        enableSelection=false}}
</del><ins>+                                        {{interactive-chart
+                                            chartData=chartData
+                                            domain=controller.sharedDomain
+                                            enableSelection=false}}
</ins><span class="cx">                                     {{/link-to}}
</span><span class="cx">                                 {{else}}
</span><span class="cx">                                     {{#if failure}}
</span><span class="lines">@@ -258,9 +272,9 @@
</span><span class="cx">                 &lt;tr&gt;
</span><span class="cx">                     &lt;th&gt;Current&lt;/th&gt;
</span><span class="cx">                     &lt;td&gt;
</span><del>-                        {{details.currentValue}} {{chartData.unit}}
-                        {{#if details.valueDiff}}
-                            ({{details.valueDiff}} {{chartData.unit}})
</del><ins>+                        {{details.status.currentValue}} {{chartData.unit}}
+                        {{#if details.status.valueDelta}}
+                            ({{details.status.valueDelta}} {{chartData.unit}})
</ins><span class="cx">                         {{/if}}
</span><span class="cx">                         {{#if details.status.label}}
</span><span class="cx">                             &lt;br&gt;
</span><span class="lines">@@ -378,9 +392,13 @@
</span><span class="cx">         &lt;nav id=&quot;navigation&quot; role=&quot;navigation&quot;&gt;
</span><span class="cx">             &lt;h1&gt;&lt;a href=&quot;#&quot;&gt;WebKit Perf Monitor&lt;/a&gt;&lt;/h1&gt;
</span><span class="cx">             &lt;ul&gt;
</span><del>-                {{#link-to 'index' tagName='li'}}
-                    {{#link-to 'index'}}Dashboard{{/link-to}}
-                {{/link-to}}
</del><ins>+                {{#each App.Manifest.dashboards}}
+                    {{#if name}}
+                        {{#link-to 'dashboard' name tagName='li'}}
+                            {{#link-to 'dashboard' name}}{{label}}{{/link-to}}
+                        {{/link-to}}
+                    {{/if}}
+                {{/each}}
</ins><span class="cx">                 {{#link-to 'charts' tagName='li'}}
</span><span class="cx">                     {{#link-to 'charts'}}Charts{{/link-to}}
</span><span class="cx">                 {{/link-to}}
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2interactivechartjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/interactive-chart.js (179762 => 179763)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/interactive-chart.js        2015-02-06 22:14:20 UTC (rev 179762)
+++ trunk/Websites/perf.webkit.org/public/v2/interactive-chart.js        2015-02-06 22:43:08 UTC (rev 179763)
</span><span class="lines">@@ -288,7 +288,7 @@
</span><span class="cx">         this._yAxisLabels.call(this._yAxis);
</span><span class="cx">         if (this._yAxisUnitContainer)
</span><span class="cx">             this._yAxisUnitContainer.remove();
</span><del>-        var x = - 3 * this._rem;
</del><ins>+        var x = - 3.2 * this._rem;
</ins><span class="cx">         var y = this._contentHeight / 2;
</span><span class="cx">         this._yAxisUnitContainer = this._yAxisLabels.append(&quot;text&quot;)
</span><span class="cx">             .attr(&quot;transform&quot;, &quot;rotate(90 0 0) translate(&quot; + y + &quot;, &quot; + (-x) + &quot;)&quot;)
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2manifestjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/manifest.js (179762 => 179763)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/manifest.js        2015-02-06 22:14:20 UTC (rev 179762)
+++ trunk/Websites/perf.webkit.org/public/v2/manifest.js        2015-02-06 22:43:08 UTC (rev 179763)
</span><span class="lines">@@ -92,7 +92,7 @@
</span><span class="cx">     },
</span><span class="cx"> });
</span><span class="cx"> 
</span><del>-App.Dashboard = App.Model.extend({
</del><ins>+App.Dashboard = App.NameLabelModel.extend({
</ins><span class="cx">     serialized: DS.attr('string'),
</span><span class="cx">     table: function ()
</span><span class="cx">     {
</span><span class="lines">@@ -152,7 +152,7 @@
</span><span class="cx">             metrics: this._normalizeIdMap(payload['metrics']),
</span><span class="cx">             repositories: this._normalizeIdMap(payload['repositories']),
</span><span class="cx">             bugTrackers: this._normalizeIdMap(payload['bugTrackers']),
</span><del>-            dashboards: [{id: 1, serialized: JSON.stringify(payload['defaultDashboard'])}],
</del><ins>+            dashboards: [],
</ins><span class="cx">         };
</span><span class="cx"> 
</span><span class="cx">         for (var testId in payload['tests']) {
</span><span class="lines">@@ -173,6 +173,17 @@
</span><span class="cx">             test['metrics'].push(metricId);
</span><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        var id = 1;
+        var dashboardsInPayload = payload['dashboards'];
+        for (var dashboardName in dashboardsInPayload) {
+            results['dashboards'].push({
+                id: id,
+                name: dashboardName,
+                serialized: JSON.stringify(dashboardsInPayload[dashboardName])
+            });
+            id++;
+        }
+
</ins><span class="cx">         return results;
</span><span class="cx">     },
</span><span class="cx">     _normalizeIdMap: function (idMap)
</span><span class="lines">@@ -211,6 +222,8 @@
</span><span class="cx">     _metricById: {},
</span><span class="cx">     _builderById: {},
</span><span class="cx">     _repositoryById: {},
</span><ins>+    _dashboardByName: {},
+    _defaultDashboardName: null,
</ins><span class="cx">     _fetchPromise: null,
</span><span class="cx">     fetch: function (store)
</span><span class="cx">     {
</span><span class="lines">@@ -223,6 +236,8 @@
</span><span class="cx">     metric: function (id) { return this._metricById[id]; },
</span><span class="cx">     builder: function (id) { return this._builderById[id]; },
</span><span class="cx">     repository: function (id) { return this._repositoryById[id]; },
</span><ins>+    dashboardByName: function (name) { return this._dashboardByName[name]; },
+    defaultDashboardName: function () { return this._defaultDashboardName; },
</ins><span class="cx">     _fetchedManifest: function (store)
</span><span class="cx">     {
</span><span class="cx">         var startTime = Date.now();
</span><span class="lines">@@ -259,7 +274,10 @@
</span><span class="cx"> 
</span><span class="cx">         this.set('bugTrackers', store.all('bugTracker').sortBy('name'));
</span><span class="cx"> 
</span><del>-        this.set('defaultDashboard', store.all('dashboard').objectAt(0));
</del><ins>+        var dashboards = store.all('dashboard').sortBy('name');
+        this.set('dashboards', dashboards);
+        dashboards.forEach(function (dashboard) { self._dashboardByName[dashboard.get('name')] = dashboard; });
+        this._defaultDashboardName = dashboards.length ? dashboards[0].get('name') : null;
</ins><span class="cx">     },
</span><span class="cx">     fetchRunsWithPlatformAndMetric: function (store, platformId, metricId)
</span><span class="cx">     {
</span></span></pre>
</div>
</div>

</body>
</html>