<!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>[196708] 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/196708">196708</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2016-02-17 12:18:05 -0800 (Wed, 17 Feb 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>v3 UI has the capability to schedule an A/B testing in a specific range
https://bugs.webkit.org/show_bug.cgi?id=154329

Reviewed by Chris Dumez.

Extended AnalysisTaskChartPane and ResultsTable so that users can select a range of points in either
the overview chart pane and the results viewer table. Extracted TestGroupForm out of the analysis task
page and used right below those two components in the analysis task page.

* public/v3/components/results-table.js:
(ResultsTable):
(ResultsTable.prototype.setRangeSelectorLabels): Added.
(ResultsTable.prototype.setRangeSelectorCallback): Added.
(ResultsTable.prototype.selectedRange): Added.
(ResultsTable.prototype._rangeSelectorClicked): Added.
(ResultsTable.prototype.render): Generate radio boxes to select a range.

* public/v3/components/test-group-form.js:
(TestGroupForm):
(TestGroupForm.prototype.setStartCallback): Added.
(TestGroupForm.prototype.setNeedsName): Added.
(TestGroupForm.prototype.setDisabled): Added.
(TestGroupForm.prototype.setLabel): Added.
(TestGroupForm.prototype.setRepetitionCount): Added.
(TestGroupForm.prototype._submitted): Added.
(TestGroupForm.htmlTemplate): Extracted from AnalysisTaskPage.htmlTemplate.

* public/v3/index.html:

* public/v3/pages/analysis-task-page.js:
(AnalysisTaskChartPane.prototype._mainSelectionDidChange): Added. Delegates the work to AnalysisTaskPage.
(AnalysisTaskChartPane.prototype.selectedPoints): Added.
(AnalysisTaskPage):
(AnalysisTaskPage.prototype.title):
(AnalysisTaskPage.prototype.render):
(AnalysisTaskPage.prototype._renderTestGroupDetails): Use TestGroupForm's methods instead of mutating DOM. 
(AnalysisTaskPage.prototype._retryCurrentTestGroup):
(AnalysisTaskPage.prototype._chartSelectionDidChange): Added.
(AnalysisTaskPage.prototype._createNewTestGroupFromChart): Added.
(AnalysisTaskPage.prototype._selectedRowInAnalysisResultsViewer): Added.
(AnalysisTaskPage.prototype._createNewTestGroupFromViewer): Added.
(AnalysisTaskPage.prototype._createRetryNameForTestGroup):
(AnalysisTaskPage.prototype._createTestGroupAfterVerifyingRootSetList): Extracted from _retryCurrentTestGroup
so that we can call it in _createNewTestGroupFromChart and _createNewTestGroupFromViewer.
(AnalysisTaskPage.htmlTemplate):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgChangeLog">trunk/Websites/perf.webkit.org/ChangeLog</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3componentsresultstablejs">trunk/Websites/perf.webkit.org/public/v3/components/results-table.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3indexhtml">trunk/Websites/perf.webkit.org/public/v3/index.html</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3pagesanalysistaskpagejs">trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3componentstestgroupformjs">trunk/Websites/perf.webkit.org/public/v3/components/test-group-form.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 (196707 => 196708)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/ChangeLog        2016-02-17 20:11:05 UTC (rev 196707)
+++ trunk/Websites/perf.webkit.org/ChangeLog        2016-02-17 20:18:05 UTC (rev 196708)
</span><span class="lines">@@ -1,3 +1,51 @@
</span><ins>+2016-02-16  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        v3 UI has the capability to schedule an A/B testing in a specific range
+        https://bugs.webkit.org/show_bug.cgi?id=154329
+
+        Reviewed by Chris Dumez.
+
+        Extended AnalysisTaskChartPane and ResultsTable so that users can select a range of points in either
+        the overview chart pane and the results viewer table. Extracted TestGroupForm out of the analysis task
+        page and used right below those two components in the analysis task page.
+
+        * public/v3/components/results-table.js:
+        (ResultsTable):
+        (ResultsTable.prototype.setRangeSelectorLabels): Added.
+        (ResultsTable.prototype.setRangeSelectorCallback): Added.
+        (ResultsTable.prototype.selectedRange): Added.
+        (ResultsTable.prototype._rangeSelectorClicked): Added.
+        (ResultsTable.prototype.render): Generate radio boxes to select a range.
+
+        * public/v3/components/test-group-form.js:
+        (TestGroupForm):
+        (TestGroupForm.prototype.setStartCallback): Added.
+        (TestGroupForm.prototype.setNeedsName): Added.
+        (TestGroupForm.prototype.setDisabled): Added.
+        (TestGroupForm.prototype.setLabel): Added.
+        (TestGroupForm.prototype.setRepetitionCount): Added.
+        (TestGroupForm.prototype._submitted): Added.
+        (TestGroupForm.htmlTemplate): Extracted from AnalysisTaskPage.htmlTemplate.
+
+        * public/v3/index.html:
+
+        * public/v3/pages/analysis-task-page.js:
+        (AnalysisTaskChartPane.prototype._mainSelectionDidChange): Added. Delegates the work to AnalysisTaskPage.
+        (AnalysisTaskChartPane.prototype.selectedPoints): Added.
+        (AnalysisTaskPage):
+        (AnalysisTaskPage.prototype.title):
+        (AnalysisTaskPage.prototype.render):
+        (AnalysisTaskPage.prototype._renderTestGroupDetails): Use TestGroupForm's methods instead of mutating DOM. 
+        (AnalysisTaskPage.prototype._retryCurrentTestGroup):
+        (AnalysisTaskPage.prototype._chartSelectionDidChange): Added.
+        (AnalysisTaskPage.prototype._createNewTestGroupFromChart): Added.
+        (AnalysisTaskPage.prototype._selectedRowInAnalysisResultsViewer): Added.
+        (AnalysisTaskPage.prototype._createNewTestGroupFromViewer): Added.
+        (AnalysisTaskPage.prototype._createRetryNameForTestGroup):
+        (AnalysisTaskPage.prototype._createTestGroupAfterVerifyingRootSetList): Extracted from _retryCurrentTestGroup
+        so that we can call it in _createNewTestGroupFromChart and _createNewTestGroupFromViewer.
+        (AnalysisTaskPage.htmlTemplate):
+
</ins><span class="cx"> 2016-02-15  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Extract the code specific to v2 UI out of shared statistics.js
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3componentsresultstablejs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/components/results-table.js (196707 => 196708)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/components/results-table.js        2016-02-17 20:11:05 UTC (rev 196707)
+++ trunk/Websites/perf.webkit.org/public/v3/components/results-table.js        2016-02-17 20:18:05 UTC (rev 196708)
</span><span class="lines">@@ -4,10 +4,23 @@
</span><span class="cx">         super(name);
</span><span class="cx">         this._repositoryList = [];
</span><span class="cx">         this._valueFormatter = null;
</span><ins>+        this._rangeSelectorLabels = null;
+        this._rangeSelectorCallback = null;
+        this._selectedRange = {};
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     setValueFormatter(valueFormatter) { this._valueFormatter = valueFormatter; }
</span><ins>+    setRangeSelectorLabels(labels) { this._rangeSelectorLabels = labels; }
+    setRangeSelectorCallback(callback) { this._rangeSelectorCallback = callback; }
+    selectedRange() { return this._selectedRange; }
</ins><span class="cx"> 
</span><ins>+    _rangeSelectorClicked(label, row)
+    {
+        this._selectedRange[label] = row;
+        if (this._rangeSelectorCallback)
+            this._rangeSelectorCallback();
+    }
+
</ins><span class="cx">     render()
</span><span class="cx">     {
</span><span class="cx">         if (!this._valueFormatter)
</span><span class="lines">@@ -23,13 +36,22 @@
</span><span class="cx">         var barGraphGroup = new BarGraphGroup(this._valueFormatter);
</span><span class="cx">         var element = ComponentBase.createElement;
</span><span class="cx">         var self = this;
</span><ins>+        var hasGroupHeading = false;
</ins><span class="cx">         var tableBodies = rowGroups.map(function (group) {
</span><span class="cx">             var groupHeading = group.heading;
</span><span class="cx">             var revisionSupressionCount = {};
</span><ins>+            hasGroupHeading = !!groupHeading;
</ins><span class="cx"> 
</span><span class="cx">             return element('tbody', group.rows.map(function (row, rowIndex) {
</span><span class="cx">                 var cells = [];
</span><span class="cx"> 
</span><ins>+                if (self._rangeSelectorLabels) {
+                    self._selectedRange = {};
+                    for (var label of self._rangeSelectorLabels)
+                        cells.push(element('td', element('input',
+                            {type: 'radio', name: label, onchange: self._rangeSelectorClicked.bind(self, label, row)})));
+                }
+
</ins><span class="cx">                 if (groupHeading !== undefined &amp;&amp; !rowIndex)
</span><span class="cx">                     cells.push(element('th', {rowspan: group.rows.length}, groupHeading));
</span><span class="cx">                 cells.push(element('td', row.heading()));
</span><span class="lines">@@ -47,6 +69,7 @@
</span><span class="cx"> 
</span><span class="cx">         this.renderReplace(this.content().querySelector('table'), [
</span><span class="cx">             element('thead', [
</span><ins>+                this._rangeSelectorLabels ? this._rangeSelectorLabels.map(function (label) { return element('th', label) }) : [],
</ins><span class="cx">                 this.heading(),
</span><span class="cx">                 element('th', 'Result'),
</span><span class="cx">                 repositoryList.map(function (repository) { return element('th', repository.label()); }),
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3componentstestgroupformjs"></a>
<div class="addfile"><h4>Added: trunk/Websites/perf.webkit.org/public/v3/components/test-group-form.js (0 => 196708)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/components/test-group-form.js                                (rev 0)
+++ trunk/Websites/perf.webkit.org/public/v3/components/test-group-form.js        2016-02-17 20:18:05 UTC (rev 196708)
</span><span class="lines">@@ -0,0 +1,55 @@
</span><ins>+
+class TestGroupForm extends ComponentBase {
+
+    constructor()
+    {
+        super('test-group-form');
+        this._startCallback = null;
+        this._repetitionCountControl = this.content().querySelector('.repetition-count');
+        this._repetitionCountControl.value = 4;
+        this._buttonControl = this.content().querySelector('button');
+        this._nameControl = this.content().querySelector('.name');
+        this.content().querySelector('form').onsubmit = this._submitted.bind(this);
+    }
+
+    setStartCallback(callback) { this._startCallback = callback; }
+    setNeedsName(needsName) { this._nameControl.style.display = needsName ? null : 'none'; }
+    setDisabled(disabled) { this._buttonControl.disabled = disabled; }
+
+    setLabel(label) { this._buttonControl.textContent = label; }
+    setRepetitionCount(count) { this._repetitionCountControl.value = count; }
+
+    _submitted(event)
+    {
+        event.preventDefault();
+        if (this._startCallback)
+            this._startCallback(this._nameControl.value, this._repetitionCountControl.value);
+    }
+
+    static htmlTemplate()
+    {
+        return `
+            &lt;form&gt;
+                &lt;button type=&quot;submit&quot;&gt;Start A/B testing&lt;/button&gt;
+                &lt;input class=&quot;name&quot; type=&quot;text&quot;&gt;
+                with
+                &lt;select class=&quot;repetition-count&quot;&gt;
+                    &lt;option&gt;1&lt;/option&gt;
+                    &lt;option&gt;2&lt;/option&gt;
+                    &lt;option&gt;3&lt;/option&gt;
+                    &lt;option&gt;4&lt;/option&gt;
+                    &lt;option&gt;5&lt;/option&gt;
+                    &lt;option&gt;6&lt;/option&gt;
+                    &lt;option&gt;7&lt;/option&gt;
+                    &lt;option&gt;8&lt;/option&gt;
+                    &lt;option&gt;9&lt;/option&gt;
+                    &lt;option&gt;10&lt;/option&gt;
+                &lt;/select&gt;
+                iterations per set
+            &lt;/form&gt;
+        `;
+    }
+
+}
+
+ComponentBase.defineElement('test-group-form', TestGroupForm);
</ins></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3indexhtml"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/index.html (196707 => 196708)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/index.html        2016-02-17 20:11:05 UTC (rev 196707)
+++ trunk/Websites/perf.webkit.org/public/v3/index.html        2016-02-17 20:18:05 UTC (rev 196708)
</span><span class="lines">@@ -75,6 +75,7 @@
</span><span class="cx">         &lt;script src=&quot;components/results-table.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">         &lt;script src=&quot;components/analysis-results-viewer.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">         &lt;script src=&quot;components/test-group-results-table.js&quot;&gt;&lt;/script&gt;
</span><ins>+        &lt;script src=&quot;components/test-group-form.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx">         &lt;script src=&quot;components/chart-styles.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">         &lt;script src=&quot;components/chart-pane-base.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">         &lt;script src=&quot;pages/page.js&quot;&gt;&lt;/script&gt;
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3pagesanalysistaskpagejs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js (196707 => 196708)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js        2016-02-17 20:11:05 UTC (rev 196707)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js        2016-02-17 20:18:05 UTC (rev 196708)
</span><span class="lines">@@ -8,6 +8,22 @@
</span><span class="cx"> 
</span><span class="cx">     setPage(page) { this._page = page; }
</span><span class="cx">     router() { return this._page.router(); }
</span><ins>+
+    _mainSelectionDidChange(selection, didEndDrag)
+    {
+        super._mainSelectionDidChange(selection);
+        if (didEndDrag)
+            this._page._chartSelectionDidChange();
+    }
+
+    selectedPoints()
+    {
+        var selection = this._mainChart ? this._mainChart.currentSelection() : null;
+        if (!selection)
+            return null;
+
+        return this._mainChart.sampledDataBetween('current', selection[0], selection[1]);
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> ComponentBase.defineElement('analysis-task-chart-pane', AnalysisTaskChartPane);
</span><span class="lines">@@ -33,6 +49,8 @@
</span><span class="cx">         this._chartPane.setPage(this);
</span><span class="cx">         this._analysisResultsViewer = this.content().querySelector('analysis-results-viewer').component();
</span><span class="cx">         this._analysisResultsViewer.setTestGroupCallback(this._showTestGroup.bind(this));
</span><ins>+        this._analysisResultsViewer.setRangeSelectorLabels(['A', 'B']);
+        this._analysisResultsViewer.setRangeSelectorCallback(this._selectedRowInAnalysisResultsViewer.bind(this));
</ins><span class="cx">         this._testGroupResultsTable = this.content().querySelector('test-group-results-table').component();
</span><span class="cx">         this._taskNameLabel = this.content().querySelector('.analysis-task-name editable-text').component();
</span><span class="cx">         this._taskNameLabel.setStartedEditingCallback(this._didStartEditingTaskName.bind(this));
</span><span class="lines">@@ -45,7 +63,16 @@
</span><span class="cx">         this._bugTrackerControl = this.content().querySelector('.bug-tracker-control');
</span><span class="cx">         this._bugNumberControl = this.content().querySelector('.bug-number-control');
</span><span class="cx"> 
</span><del>-        this.content().querySelector('.test-group-retry-form').onsubmit = this._retryCurrentTestGroup.bind(this);
</del><ins>+        this._newTestGroupFormForChart = this.content().querySelector('.overview-chart test-group-form').component();
+        this._newTestGroupFormForChart.setStartCallback(this._createNewTestGroupFromChart.bind(this));
+
+        this._newTestGroupFormForViewer = this.content().querySelector('.analysis-results-view test-group-form').component();
+        this._newTestGroupFormForViewer.setStartCallback(this._createNewTestGroupFromViewer.bind(this));
+        this._selectedRowInAnalysisResultsViewer();
+
+        this._retryForm = this.content().querySelector('.test-group-retry-form').firstChild.component();
+        this._retryForm.setStartCallback(this._retryCurrentTestGroup.bind(this));
+        this._retryForm.setNeedsName(false);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     title() { return this._task ? this._task.label() : 'Analysis Task'; }
</span><span class="lines">@@ -228,6 +255,8 @@
</span><span class="cx">                 }));
</span><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        this._newTestGroupFormForChart.render();
+
</ins><span class="cx">         this._analysisResultsViewer.setCurrentTestGroup(this._currentTestGroup);
</span><span class="cx">         this._analysisResultsViewer.render();
</span><span class="cx"> 
</span><span class="lines">@@ -297,16 +326,14 @@
</span><span class="cx">                     this._chartPane.setMainSelection([startTime, endTime]);
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            this.content().querySelector('.test-group-retry-button').textContent = this._currentTestGroup ? 'Retry' : 'Confirm the change';
</del><ins>+            this._retryForm.setLabel('Retry');
+            if (this._currentTestGroup)
+                this._retryForm.setRepetitionCount(this._currentTestGroup.repetitionCount());
+            this._retryForm.element().style.display = this._currentTestGroup ? null : 'none';
</ins><span class="cx"> 
</span><del>-            var repetitionCount = this._currentTestGroup ? this._currentTestGroup.repetitionCount() : 4;
-            var repetitionCountController = this.content().querySelector('.test-group-retry-repetition-count');
-            repetitionCountController.value = repetitionCount;
-
</del><span class="cx">             this._renderedCurrentTestGroup = this._currentTestGroup;
</span><span class="cx">         }
</span><del>-
-        this.content().querySelector('.test-group-retry-button').disabled = !(this._currentTestGroup || this._startPoint);
</del><ins>+        this._retryForm.render();
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     _showTestGroup(testGroup)
</span><span class="lines">@@ -390,26 +417,49 @@
</span><span class="cx">         });
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    _retryCurrentTestGroup(event)
</del><ins>+    _retryCurrentTestGroup(unusedName, repetitionCount)
</ins><span class="cx">     {
</span><del>-        event.preventDefault();
-        console.assert(this._currentTestGroup || this._startPoint);
</del><ins>+        console.assert(this._currentTestGroup);
+        var testGroup = this._currentTestGroup;
+        var newName = this._createRetryNameForTestGroup(testGroup.name());
+        var rootSetList = testGroup.requestedRootSets();
+        var rootSetLabels = rootSetList.map(function (rootSet) { return testGroup.labelForRootSet(rootSet); });
+        return this._createTestGroupAfterVerifyingRootSetList(newName, repetitionCount, rootSetList, rootSetLabels);
+    }
</ins><span class="cx"> 
</span><del>-        var testGroupName;
-        var rootSetList;
-        var rootSetLabels;
</del><ins>+    _chartSelectionDidChange()
+    {
+        var points = this._chartPane.selectedPoints();
+        this._newTestGroupFormForChart.setDisabled(!points || points.length &lt; 2);
+    }
</ins><span class="cx"> 
</span><del>-        if (this._currentTestGroup) {
-            var testGroup = this._currentTestGroup;
-            testGroupName = this._createRetryNameForTestGroup(testGroup.name());
-            rootSetList = testGroup.requestedRootSets();
-            rootSetLabels = rootSetList.map(function (rootSet) { return testGroup.labelForRootSet(rootSet); });
-        } else {
-            testGroupName = 'Confirming the change';
-            rootSetList = [this._startPoint.rootSet(), this._endPoint.rootSet()];
-            rootSetLabels = ['Point 0', `Point ${this._endPoint.seriesIndex - this._startPoint.seriesIndex}`];
-        }
</del><ins>+    _createNewTestGroupFromChart(name, repetitionCount)
+    {
+        var points = this._chartPane.selectedPoints();
+        console.assert(points &amp;&amp; points.length &gt;= 2);
+        return this._createTestGroupAfterVerifyingRootSetList(name, repetitionCount,
+            [points[0].rootSet(), points[points.length - 1].rootSet()], ['A', 'B']);
+    }
</ins><span class="cx"> 
</span><ins>+    _selectedRowInAnalysisResultsViewer()
+    {
+        var selectedRange = this._analysisResultsViewer.selectedRange();
+        this._newTestGroupFormForViewer.setDisabled(!selectedRange['A'] || !selectedRange['B']);
+    }
+
+    _createNewTestGroupFromViewer(name, repetitionCount)
+    {
+        var selectedRange = this._analysisResultsViewer.selectedRange();
+        console.assert(selectedRange &amp;&amp; selectedRange['A'] &amp;&amp; selectedRange['B']);
+        return this._createTestGroupAfterVerifyingRootSetList(name, repetitionCount,
+            [selectedRange['A'].rootSet(), selectedRange['B'].rootSet()], ['A', 'B']);
+    }
+
+    _createTestGroupAfterVerifyingRootSetList(testGroupName, repetitionCount, rootSetList, rootSetLabels)
+    {
+        if (this._hasDuplicateTestGroupName(testGroupName))
+            alert(`There is already a test group named &quot;${testGroupName}&quot;`);
+
</ins><span class="cx">         var rootSetsByName = {};
</span><span class="cx">         for (var repository of rootSetList[0].repositories())
</span><span class="cx">             rootSetsByName[repository.name()] = [];
</span><span class="lines">@@ -434,8 +484,6 @@
</span><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        var repetitionCount = this.content().querySelector('.test-group-retry-repetition-count').value;
-
</del><span class="cx">         TestGroup.createAndRefetchTestGroups(this._task, testGroupName, repetitionCount, rootSetsByName)
</span><span class="cx">             .then(this._didFetchTestGroups.bind(this), function (error) {
</span><span class="cx">             alert('Failed to create a new test group: ' + error);
</span><span class="lines">@@ -507,31 +555,17 @@
</span><span class="cx">                 &lt;/div&gt;
</span><span class="cx">                 &lt;section class=&quot;overview-chart&quot;&gt;
</span><span class="cx">                     &lt;analysis-task-chart-pane&gt;&lt;/analysis-task-chart-pane&gt;
</span><ins>+                    &lt;div class=&quot;new-test-group-form&quot;&gt;&lt;test-group-form&gt;&lt;/test-group-form&gt;&lt;/div&gt;
</ins><span class="cx">                 &lt;/section&gt;
</span><span class="cx">                 &lt;section class=&quot;analysis-results-view&quot;&gt;
</span><span class="cx">                     &lt;analysis-results-viewer&gt;&lt;/analysis-results-viewer&gt;
</span><ins>+                    &lt;div class=&quot;new-test-group-form&quot;&gt;&lt;test-group-form&gt;&lt;/test-group-form&gt;&lt;/div&gt;
</ins><span class="cx">                 &lt;/section&gt;
</span><span class="cx">                 &lt;section class=&quot;test-group-view&quot;&gt;
</span><span class="cx">                     &lt;ul class=&quot;test-group-list&quot;&gt;&lt;/ul&gt;
</span><span class="cx">                     &lt;div class=&quot;test-group-details&quot;&gt;
</span><span class="cx">                         &lt;test-group-results-table&gt;&lt;/test-group-results-table&gt;
</span><del>-                        &lt;form class=&quot;test-group-retry-form&quot;&gt;
-                            &lt;button class=&quot;test-group-retry-button&quot; type=&quot;submit&quot;&gt;Retry&lt;/button&gt;
-                            with
-                            &lt;select class=&quot;test-group-retry-repetition-count&quot;&gt;
-                                &lt;option&gt;1&lt;/option&gt;
-                                &lt;option&gt;2&lt;/option&gt;
-                                &lt;option&gt;3&lt;/option&gt;
-                                &lt;option&gt;4&lt;/option&gt;
-                                &lt;option&gt;5&lt;/option&gt;
-                                &lt;option&gt;6&lt;/option&gt;
-                                &lt;option&gt;7&lt;/option&gt;
-                                &lt;option&gt;8&lt;/option&gt;
-                                &lt;option&gt;9&lt;/option&gt;
-                                &lt;option&gt;10&lt;/option&gt;
-                            &lt;/select&gt;
-                            iterations per set
-                        &lt;/form&gt;
</del><ins>+                        &lt;div class=&quot;test-group-retry-form&quot;&gt;&lt;test-group-form&gt;&lt;/test-group-form&gt;&lt;/div&gt;
</ins><span class="cx">                     &lt;/div&gt;
</span><span class="cx">                 &lt;/section&gt;
</span><span class="cx">             &lt;/div&gt;
</span><span class="lines">@@ -617,7 +651,7 @@
</span><span class="cx">             }
</span><span class="cx"> 
</span><span class="cx">             .analysis-results-view {
</span><del>-                margin: 1rem;
</del><ins>+                margin: 2.5rem 1rem;
</ins><span class="cx">             }
</span><span class="cx"> 
</span><span class="cx">             .test-configuration h3 {
</span><span class="lines">@@ -641,6 +675,7 @@
</span><span class="cx">                 margin: 0;
</span><span class="cx">             }
</span><span class="cx"> 
</span><ins>+            .new-test-group-form,
</ins><span class="cx">             .test-group-retry-form {
</span><span class="cx">                 padding: 0;
</span><span class="cx">                 margin: 0.5rem;
</span></span></pre>
</div>
</div>

</body>
</html>