Make the size of the benchmark canvas adaptive to the screen size and screen resolution
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 1 Nov 2015 08:26:46 +0000 (08:26 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 1 Nov 2015 08:26:46 +0000 (08:26 +0000)
https://bugs.webkit.org/show_bug.cgi?id=150530

Patch by Said Abou-Hallawa <sabouhallawa@apple,com> on 2015-11-01
Reviewed by Darin Adler.

We want to set the size of the benchmark stage dynamically such that it
depends on the screen resolution and the device scale factor. This patch
does more than that because the home page css was not done properly. To
use the flex box layout, the animometer.css has to be rewritten almost from
scratch. The suites tree has to be rewritten also because it was not collapsing
and with the flex box layout it was going outside of the window area. The
options handling and the local storage handling had to be rewritten to
allow more flexibility with this patch and the future patches. The code
in animometer.js was reorganized into objects to allow distributing the code
nicely among separate entities.

* Animometer/resources/extensions.js:
(Point.elementClientSize): Returns the client size of an HTMLElement as a Point object.
(Insets.prototype.get width): Follow the function opening brace style guidelines.
(Insets.prototype.get height):
(Insets.prototype.get size): Returns the size of an Insets as a Point object.

(window.DocumentExtension): Provides document helper functions. It should be assailable from the runner and the tests.
(window.DocumentExtension.createElement): Creates an HTMLElement given its name, attributes and parentElement.
(window.DocumentExtension.createSvgElement): Creates an SVGElement given its name, attributes and parentElement (moved from utilities.js).
(window.DocumentExtension.insertCssRuleAfter): Inserts a CSS rule after an exiting rule given its text.

(ResultsTable.prototype._showHeader): Use DocumentExtension functions.
(ResultsTable.prototype._showGraph): Use DocumentExtension functions and create a real button for "Graph..." option.
(ResultsTable.prototype._showJSON): Use DocumentExtension functions and create a real button for "JSON..." option.
(Options): Deleted.

* Animometer/runner/animometer.html: Restructure the page to use the flex box layout.

* Animometer/runner/resources/animometer.css:
(html,body):
(button):

(button.large-button):The large button appears in the animometer.html.
(button.large-button:active):
(button.large-button:disabled):

(button.small-button): The small button appears in the results table.
(button.small-button:active):

(.tree): The tree class is used to list the suites and their tests.
(.tree .expand-button): This button expands a tree element.
(.tree .expand-button ~ ul): Hide the children (<ul>...</ul>) of a parent node by default.
(.tree .expand-button:checked ~ ul): Show the children of a parent node only when checked.
(.tree ul): Hide the list bullets.
(.tree li): Indent every node in the tree relative to its parent.
(.tree ul li): Indent all the non top level nodes only (the tests nodes in our case).
(.tree > li:last-child): Do not indent the bottom of the last child node.
(.tree-label): Style for all the labels in the tree.
(label.tree-label): Style for the labels in the top level only (the suites nodes in our case).
(label.tree-label:before): Style the unchecked case of the expand-button.
(:checked ~ label.tree-label:before): Style the checked case of the expand-button.

(table.results-table): The results table appears while running the test and at the end.
(.results-table td):
(.results-table th):

(div.results-json): The JSON div appears per test or for the whole run.

(main): This is the flex box container.

(section): A section is displayed exclusively inside the <main>. It is hidden by default.
(section.selected): When it is selected, its layout is flex layout.
(section > footer): The header or the footer of a section should not take more than 15% of the container.

(section#home): The home section has <suites> and <options> parts to be laid out in the middle.
(section#home > options):
(section#home > suites): The <suites> should not take more than 40% of the width.
(section#home > options > label): The benchmark title.
(section#home > header > h2): The benchmark title.
(section#home > options > label > input[type="number"]): Sets the width of the option edit control.

(section#running): The running section contain the runner <iframe> which takes the whole area of the <main>.
(section#running > #running-test): This is the <iframe> container.
(section#running > #running-test > iframe): The <iframe> is created by the runner for each test.
(section#running > #progress): This is the progress bar.
(section#running > #progress > #progress-completed): This is another element which grows while the runner is progressing.
(section#running > #record): This the container of the record results table which is shown while running a test.

(section#results):
(section#json):
(section#test-json):
(section#test-graph): All these sections have the same layout. A <data> element is laid out between <header> and <footer>.

(section#results > data):
(section#json > data):
(section#test-json > data):
(section#test-graph > data): The <data> element should take 70% of the <section>.

(section#test-graph > data > svg):
(.axis line):
(.left-samples): These styles are for the d3 graph.

(section#test-json > data): This is the style of the JSON <data> element.

(iframe): Deleted.
(label, p): Deleted.
(section > p): Deleted.
(section#home > p): Deleted.
(section#home > p:first-child): Deleted.
(#testContainer): Deleted.
(section#running #progress-completed): Deleted.
(section#results > table): Deleted.
(section#results > table td, th): Deleted.
(section#results > table tr.alt, td): Deleted.
(section#results > table th): Deleted.
(section#json > textarea): Deleted.
(.options): Deleted.
(.options p): Deleted.
(#suites ul): Deleted.
(#suites ul ul): Deleted.
(#suites ul ul input, #suites ul ul label): Deleted.
(#suites.showTests ul ul): Deleted.
(.column): Deleted.
(input[type="number"]): Deleted.
(.buttons): Deleted.
(.small-button): Deleted.
(#graphContainer): Deleted.
(.right-samples): Deleted.
(.sample-time): Deleted.
(.left-mean): Deleted.
(.right-mean): Deleted.

* Animometer/runner/resources/animometer.js:
(window.benchmarkRunnerClient.initialize): Initialize the client object with the options and the suites.
(window.benchmarkRunnerClient.willStartFirstIteration): Use new css selectors for results and the record table.
(window.benchmarkRunnerClient.didFinishLastIteration): Move the code which sets the JSON text to sectionsManager.showJSON().

(window.sectionsManager): Responsible of managing the <section>s elements inside animometer.html.
(window.sectionsManager._sectionHeaderH1Element): Return the <h1> inside the <header> of a given section.
(window.sectionsManager._sectionDataDivElement): Return the <div> inside the <data> of a given section.
(window.sectionsManager.showScore): Show the score of the last benchmark run.
(window.sectionsManager.showTestName): Show the test name for detailed results <section>.
(window.sectionsManager.showJSON): Shows the JSON text of the last benchmark or for a specific test.
(window.sectionsManager.showSection): Shows a specific <section> in the <main> container.
(window.sectionsManager.setupSectionStyle): Sets css attributes for all the <section>s.
(window.sectionsManager.setupRunningSectionStyle): Sets the css attributes for the running <section> only.

(window.optionsManager): Responsible of managing the user options and streaming them to/form the localStorage.
(window.optionsManager._optionsElements): Returns the children <input> elements of the <options>.
(window.optionsManager.updateUIFromLocalStorage): Restore the values of the <options> UI elements from the local storage.
(window.optionsManager.updateLocalStorageFromUI): Saves the values of the <options> UI elements to the local storage.

(window.suitesManager): Responsible of managing the user suites and streaming them to/form the localStorage.
(window.suitesManager._treeElement): Returns the suites tree container element.
(window.suitesManager._suitesElements): Returns a list of the suites elements.
(window.suitesManager._checkboxElement): Returns the checkbox element of a given suite.
(window.suitesManager._localStorageNameForTest): Generates a string for the tuple <suite, test> to be saved in the localStorage.
(window.suitesManager._updateSuiteCheckboxState): Updates the state of a suite checkbox from the state of its tests' checkboxes.
(window.suitesManager._updateStartButtonState): Updates the state of the start button from the state of the suites' checkboxes.
(window.suitesManager._onChangeSuiteCheckbox): Called when a suite checkbox is clicked.
(window.suitesManager._onChangeTestCheckbox): Called when a test checkbox is clicked.
(window.suitesManager._createSuiteElement): Creates suite node in the suites tree.
(window.suitesManager._createTestElement): Creates test node in the suites tree.
(window.suitesManager.createElements): Creates the suites tree dynamically from the array Suites.
(window.suitesManager.updateUIFromLocalStorage): Restore the values of the <suites> UI elements from the local storage.
(window.suitesManager.updateLocalStorageFromUI): aves the values of the <suites> UI elements to the local storage.

(window.benchmarkController): This is the UI controller of the animometer.html page.
(window.benchmarkController.initialize): Called when the animometer.html page is loaded.
(window.benchmarkController._runBenchmark): Starts a benchmark run.
(window.benchmarkController.startTest): Called when the "Start Test" button is clicked.
(window.benchmarkController.showResults): Called at the end of the test to show the final results.
(window.benchmarkController.showJson): Called from the results page to show the JSON of the last benchmark run.
(window.benchmarkController.showTestGraph): Called from the results the table to show a graph for the samples of a specific test.
(window.benchmarkController.showTestJSON): Called from the results the table to show a JSON for the samples of a specific test.

(showSection): Deleted.
(startTest): Deleted.
(showResults): Deleted.
(showJson): Deleted.
(showTestGraph): Deleted.
(showTestJSON): Deleted.
(initialize.toggleTestsCheckbox.onchange): Deleted.
(initialize): Deleted.
(updateSuiteSelection): Deleted.
(updateTestSelection): Deleted.
(updateSuiteCheckbox): Deleted.
(localStorageNameForTest): Deleted.
(populateSettings.): Deleted.
(populateSettings): Deleted.

* Animometer/runner/resources/benchmark-runner.js:
(BenchmarkRunner): Pass the frameContainer element to the BenchmarkRunner.
(BenchmarkRunner.prototype._appendFrame): Remove unused parameter unwanted styling code.
(BenchmarkRunner.prototype.runMultipleIterations):  Use the this._client.iterationCount instead of passing it as a parameter also.

* Animometer/runner/resources/graph.js:
(graph): Calculate the size of the chart from the container element.

* Animometer/tests/bouncing-particles/resources/bouncing-svg-images.js:
(BouncingSvgImage):
* Animometer/tests/bouncing-particles/resources/bouncing-svg-particles.js:
(BouncingSvgParticlesStage.prototype._createDefs):
(BouncingSvgParticlesStage.prototype._createClipStar):
* Animometer/tests/bouncing-particles/resources/bouncing-svg-shapes.js:
(BouncingSvgShape.prototype._createShape):
(BouncingSvgShapesStage.prototype.createGradient):
Call DocumentExtension.createSvgElement() instead of calling Utilities.createSvgElement().

* Animometer/tests/resources/main.js:
(Animator.prototype.animate):
(Benchmark):
(Benchmark.prototype.update):
* Animometer/tests/resources/stage.js:
(StageBenchmark.prototype.showResults):
Rename the options to match the <input> ids in animometer.html.

* Animometer/tests/resources/utilities.js:
(window.Utilities.createSvgElement): Deleted.

* Animometer/tests/text/layering-text.html:
* Animometer/tests/text/resources/layering-text.js:
(LayeringTextStage):
(LayeringTextStage.prototype._setFontSize): Sets the size of the text dynamically such that they all fit in one stage.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@191853 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
PerformanceTests/Animometer/resources/extensions.js
PerformanceTests/Animometer/runner/animometer.html
PerformanceTests/Animometer/runner/resources/animometer.css
PerformanceTests/Animometer/runner/resources/animometer.js
PerformanceTests/Animometer/runner/resources/benchmark-runner.js
PerformanceTests/Animometer/runner/resources/graph.js
PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-svg-images.js
PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-svg-particles.js
PerformanceTests/Animometer/tests/bouncing-particles/resources/bouncing-svg-shapes.js
PerformanceTests/Animometer/tests/resources/main.js
PerformanceTests/Animometer/tests/resources/stage.js
PerformanceTests/Animometer/tests/resources/utilities.js
PerformanceTests/Animometer/tests/text/layering-text.html
PerformanceTests/Animometer/tests/text/resources/layering-text.js
PerformanceTests/ChangeLog

index c700d146234864a805b480ca6a577d47a220d8e3..35673bf410843eb466079c50c21c7493d10f98a6 100644 (file)
@@ -14,6 +14,11 @@ Point.pointOnEllipse = function(angle, radiuses)
     return new Point(radiuses.x * Math.cos(angle), radiuses.y * Math.sin(angle));
 }
 
+Point.elementClientSize = function(element)
+{
+    return new Point(element.clientWidth, element.clientHeight);
+}
+
 Point.prototype =
 {
     // Used when the point object is used as a size object.
@@ -65,12 +70,19 @@ function Insets(top, right, bottom, left)
 
 Insets.prototype =
 {
-    get width() {
+    get width()
+    {
         return this.left + this.right;
     },
 
-    get height() {
+    get height()
+    {
         return this.top + this.bottom;
+    },
+    
+    get size()
+    {
+        return new Point(this.width, this.height);
     }
 }
 
@@ -110,10 +122,51 @@ SimplePromise.prototype.resolve = function (value)
         this._chainedPromise.resolve(result);
 }
 
-function Options(testInterval, frameRate)
+window.DocumentExtension =
 {
-    this.testInterval = testInterval;
-    this.frameRate = frameRate;
+    createElement : function(name, attrs, parentElement)
+    {
+        var element = document.createElement(name);
+
+        for (var key in attrs)
+            element.setAttribute(key, attrs[key]);
+
+        parentElement.appendChild(element);
+        return element;
+    },
+
+    createSvgElement: function(name, attrs, xlinkAttrs, parentElement)
+    {
+        const svgNamespace = "http://www.w3.org/2000/svg";
+        const xlinkNamespace = "http://www.w3.org/1999/xlink";
+
+        var element = document.createElementNS(svgNamespace, name);
+        
+        for (var key in attrs)
+            element.setAttribute(key, attrs[key]);
+            
+        for (var key in xlinkAttrs)
+            element.setAttributeNS(xlinkNamespace, key, xlinkAttrs[key]);
+            
+        parentElement.appendChild(element);
+        return element;
+    },
+    
+    insertCssRuleAfter: function(newRule, referenceRule)
+    {
+        var styleSheets = document.styleSheets;
+
+        for (var i = 0; i < styleSheets.length; ++i) {       
+            for (var j = 0; j < styleSheets[i].cssRules.length; ++j) {
+                if (styleSheets[i].cssRules[j].selectorText == referenceRule) {
+                    styleSheets[i].insertRule(newRule, j + 1);
+                    return true;
+                }
+            }
+        }
+        
+        return false;
+    }
 }
 
 function ProgressBar(element, ranges)
@@ -227,11 +280,11 @@ ResultsTable.prototype =
 
     _showHeader: function(message)
     {
-        var row = document.createElement("tr");
+        var thead = DocumentExtension.createElement("thead", {}, this.element);
+        var row = DocumentExtension.createElement("tr", {}, thead);
 
         var queue = [];
         this._showHeaderRow(row, queue, this._headers, message);
-        this.element.appendChild(row);
 
         while (queue.length) {
             var row = null;
@@ -246,14 +299,13 @@ ResultsTable.prototype =
                 }
 
                 if (!row)
-                    var row = document.createElement("tr");
+                    row = DocumentExtension.createElement("tr", {}, thead);
 
                 this._showHeaderRow(row, queue, entry.headers, "");
                 entry.element.colSpan = entry.headers.length;
             }
 
             if (row) {
-                this.element.appendChild(row);
                 entries.forEach(function(entry) {
                     ++entry.rowSpan;
                 });
@@ -290,14 +342,14 @@ ResultsTable.prototype =
         }
         
         var td = document.createElement("td");
-        var button = document.createElement("div");
+        var button = document.createElement("button");
         button.className = "small-button";
 
         button.addEventListener("click", function() {
             var samples = data[Strings["JSON_GRAPH"][0]];
             var samplingTimeOffset = data[Strings["JSON_GRAPH"][1]];
             var axes = Strings["TEXT_EXPERIMENTS"];
-            window.showTestGraph(testName, axes, samples, samplingTimeOffset);
+            benchmarkController.showTestGraph(testName, axes, samples, samplingTimeOffset);
         });
             
         button.textContent = Strings["TEXT_RESULTS"][1] + "...";
@@ -314,11 +366,11 @@ ResultsTable.prototype =
         }
         
         var td = document.createElement("td");
-        var button = document.createElement("div");
+        var button = document.createElement("button");
         button.className = "small-button";
 
         button.addEventListener("click", function() {
-            window.showTestJSON(testName, testResults);
+            benchmarkController.showTestJSON(testName, testResults);
         });
             
         button.textContent = Strings["TEXT_RESULTS"][2] + "...";
@@ -390,3 +442,4 @@ ResultsTable.prototype =
         }, this);
     }
 }
+
index 2a93ba62b168038347a726dcf9ebbec5d0803929..0aa24672f6bfa7155d2408fbcac9371b5a6b0956 100644 (file)
 <body>
     <main>
         <section id="home" class="selected">
-            <p>
-                Animometer is a browser benchmark that measures the complexity of an animation for
-                which a browser can achieve 50 FPS (frame per second). It uses adaptive animations
-                to tune their complexities to stay close to 50 FPS.
-            </p>
-            <div class="options">
-                <div id="suites" class="column">
-                <p>
-                    <label><input type="checkbox" id="toggleTests"> Show individual tests</label>
-                </p>
-                Suites:<br>
-                </div>
-                <div>
-                    <label>Test interval: <input id="test-interval" type="number" value="30"> seconds</label><br>
-                    <label>Frame rate: <input id="frame-rate" type="number" value="50"> fps</label><br>
-                    <label><input id="estimated-frame-rate" type="checkbox" checked> Estimated Frame Rate</label><br>
-                    <label><input id="fix-test-complexity" type="checkbox"> Fix test complexity after warmup</label><br>
-                    <label><input id="show-running-results" type="checkbox"> Show running results</label>
-                </div>
-            </div>
-            <div class="buttons">
-                <button onclick="startTest()">Start Test</button>
-            </div>
+            <header>
+                <h2>
+                    Animometer is a browser benchmark that measures the complexity of an animation for
+                    which a browser can achieve 50 FPS (frame per second). It uses adaptive animations
+                    to tune their complexities to stay close to 50 FPS.
+                </h2>
+            </header>
+            <suites>
+                <h2>Suites:</h2>
+                <ul class="tree"></ul>
+            </suites>
+            <options>
+                <h2>Options:</h2>
+                <label>Test interval: <input id="test-interval" type="number" value="30"> seconds</label><br>
+                <label>Frame rate: <input id="frame-rate" type="number" value="50"> fps</label><br>
+                <label><input id="estimated-frame-rate" type="checkbox" checked> Estimated Frame Rate</label><br>
+                <label><input id="fix-test-complexity" type="checkbox"> Fix test complexity after warmup</label><br>
+                <label><input id="show-running-results" type="checkbox"> Show running results</label><br>
+                <label><input id="normalize-for-device-scale-factor" type="checkbox"> Normalize for device scale factor</label>
+            </options>
+            <footer>
+                <button class="large-button" onclick="benchmarkController.startTest()">Start Test</button>
+            </footer>
         </section>
         <section id="running">
-            <div id="testContainer"></div>
+            <div id="running-test"></div>
             <div id="progress">
                 <div id="progress-completed"></div>
             </div>
             <div id="record">
-                <table class="record-table"></table>
+                <table class="results-table"></table>
             </div>
         </section>
         <section id="results">
-            <h1>Results:</h1>
-            <table class="results-table"></table>
-            <div class="buttons">
-                <button onclick="showJson()">JSON</button>
-                <button onclick="startTest()">Test Again</button>
-            </div>
+            <header>
+                <h1>Results:</h1>
+            </header>
+            <data>
+                <table class="results-table"></table>
+            </data>
+            <footer>
+                <button class="large-button" onclick="benchmarkController.showJson()">JSON</button>
+                <button class="large-button" onclick="benchmarkController.startTest()">Test Again</button>
+            </footer>
         </section>  
         <section id="json">
-            <h1>JSON:</h1>
-            <textarea class="results-json"></textarea>
-            <div class="buttons">
-                <button onclick="showResults()">Results</button>
-                <button onclick="startTest()">Test Again</button>
-            </div>
-        </section>  
-        <section id="test-graph">
-            <h1>Graph:</h1>
-            <div id="graphContainer"></div>
-            <div class="buttons">
-                <button onclick="showResults()">Results</button>
-                <button onclick="startTest()">Test Again</button>        
-            </div>
+            <header>
+                <h1>JSON:</h1>
+            </header>
+            <data>
+                <div class="results-json" contentEditable="true"></div>
+            </data>
+            <footer>
+                <button class="large-button" onclick="benchmarkController.showResults()">Results</button>
+                <button class="large-button" onclick="benchmarkController.startTest()">Test Again</button>
+            </footer>
         </section>  
         <section id="test-json">
-            <h1>JSON:</h1>
-            <textarea class="results-json"></textarea>
-            <div class="buttons">
-                <button onclick="showResults()">Results</button>
-                <button onclick="startTest()">Test Again</button>        
-            </div>
+            <header>
+                <h1>JSON:</h1>
+            </header>
+            <data>
+                <div class="results-json" contentEditable="true"></div>
+            </data>
+            <footer>
+                <button class="large-button" onclick="benchmarkController.showResults()">Results</button>
+                <button class="large-button" onclick="benchmarkController.startTest()">Test Again</button>        
+            </footer>
+        </section>
+        <section id="test-graph">
+            <header>
+                <h1>Graph:</h1>
+            </header>
+            <data></data>
+            <footer>
+                <button class="large-button" onclick="benchmarkController.showResults()">Results</button>
+                <button class="large-button" onclick="benchmarkController.startTest()">Test Again</button>        
+            </footer>
         </section>  
     </main>
 </body>
index 4b3e6a0202168b3e76ec6f8ef6e8429125bb93ca..e08b4c407f7f94f2da7c9007806d30eb74d58f2d 100644 (file)
+/* -------------------------------------------------------------------------- */
+/*                                HTML and Body                               */
+/* -------------------------------------------------------------------------- */
+
+html,body {
+    height: 100%;
+    margin: 0px;
+    padding: 0px;
+}
+
 body {
     background-color: rgb(96, 96, 96);
     color: rgb(235, 235, 235);
     font-family: "Helvetica Neue", Helvetica, Verdana, sans-serif;
 }
 
-main {
-    display: block;
-    position: absolute;
-    width: 800px;
-    height: 600px;
-    top: 50%;
-    left: 50%;
-    margin-top: -321px;
-    margin-left: -421px;
-    padding: 15px;
-    border: 6px solid rgb(235, 235, 235);
-    border-radius: 20px;
+/* -------------------------------------------------------------------------- */
+/*                              Buttons                                       */
+/* -------------------------------------------------------------------------- */
+
+button {
+    -webkit-appearance: none;
+    -webkit-user-select: none;
+    background-color: transparent;
 }
 
-iframe {
-    width: 800px;
-    height: 600px;
-    border: 0px none;
-    position: absolute;
+button.large-button {
+    border: 3px solid rgb(235, 235, 235);
+    border-radius: 10px;
+    min-width: 200px;
+    padding: .5em 2em;
+    margin: 0 1em;
+    font-size: 25px;
+    color: rgb(235, 235, 235);
 }
 
-label, p {
-    font-size: 16px;
-    line-height: 21px;
+button.large-button:active {
+    background-color: rgb(235, 235, 235);
+    color: rgb(46, 51, 55);
+    border-color: rgb(235, 235, 235) !important;
 }
 
-section {
-    display: none;
+button.large-button:disabled {
+    background-color: rgb(96, 96, 96);
+    color: rgb(128, 128, 128);
+}
+
+button.small-button {
+    border: 1px solid DarkCyan;
+    border-radius: 2px;
+    padding: 1px 4px;
+    margin: 0 4px;
+    font-size: 9px;
+    color: black;
 }
 
-section > p {
-    margin: 10px 20px;
+button.small-button:active {
+    background-color: DarkCyan;
+    color: rgb(46, 51, 55);
+    border-color: DarkCyan !important;
 }
 
-section#home > p {
-    margin: 0 auto;
-    width: 70%;
-    text-align: center;
+/* -------------------------------------------------------------------------- */
+/*                               Tree                                         */
+/* -------------------------------------------------------------------------- */
+
+.tree {
+    margin: 1em;
+    overflow-y: scroll;
+    height: 80%;
 }
 
-section#home > p:first-child {
-    margin-top: 160px;
-    text-align: center;
+.tree .expand-button {
+    position: absolute;
+    clip: rect(0, 0, 0, 0);
 }
 
-section.selected {
+.tree .expand-button ~ ul {
+    display: none;
+}
+
+.tree .expand-button:checked ~ ul {
     display: block;
 }
 
-#testContainer {
-    position: absolute;
-    top: 15px;
-    left: 15px;
-    width: 800px;
-    height: 600px;
+.tree ul {
+    list-style-type:none;
 }
 
-section#running > #progress {
-    position: absolute;
-    bottom: -6px;
-    left: 60px;
-    right: 60px;
-    height: 6px;
-    background-color: rgb(128, 128, 128);
-    border-left: 6px solid rgb(46, 51, 55);
-    border-right: 6px solid rgb(46, 51, 55);
+.tree li {
+    position: relative;
+    padding: 0 0 1em 1em;
 }
 
-section#running #progress-completed {
-    position: absolute;
-    top: 0;
-    left: 0;
-    height: 6px;
-    width: 0;
-    background-color: rgb(235, 235, 235);
+.tree ul li {
+    list-style:none;
+    padding: 1em 0 0 0em;
 }
 
-section#running > #record {
-    position: absolute;
-    bottom: -130px;
-    left: 0px;
-    right: 0px;
-    height: 75px;
-    color: rgb(128, 128, 128);
-    padding: 15px;
-    border: 6px solid rgb(235, 235, 235);
-    border-radius: 20px;
+.tree > li:last-child {
+    padding-bottom: 0;
+}
+
+.tree-label {
+  position: relative;
+  display: inline-block;
+}
+
+label.tree-label {
+    cursor: pointer;
 }
 
-section#running > #record > table,
-section#results > table {
+label.tree-label:before {
+    background: black;
+    color: white;
+    position: relative;
+    z-index: 1;
+    float: left;
+    margin: 0 1em 0 -2.1em;
+    width: 1em;
+    height: 1em;
+    border-radius: 1em;
+    content: '+';
+    text-align: center;
+    line-height: .9em;
+}
+
+:checked ~ label.tree-label:before {
+    content: '\2013';
+}
+
+/* -------------------------------------------------------------------------- */
+/*                              Results Table                                 */
+/* -------------------------------------------------------------------------- */
+
+table.results-table {
     width: 100%;
+    border: 2px solid DarkCyan;
+    border-collapse: collapse;
 }
 
-section#running > #record > table td, th,
-section#results > table td, th {
+.results-table th,
+.results-table td {
     font-size: 11px;
-    border: 1px solid #98bf21;
     padding: 3px 4px 2px 4px;
 }
-                                           
-section#running > #record > table tr.alt, td
-section#results > table tr.alt, td {
-    color: #000000;
-    background-color: #EAF2D3;
+
+.results-table th {
+    background: DarkCyan;
+    border-left: 1px solid LightCyan;
+    border-right: 1px solid LightCyan;
+    border-top: 1px solid LightCyan;
 }
-                                             
-section#running > #record > table th,
-section#results > table th {
-    background-color: #A7C942; 
-    text-align: center;
+
+.results-table td {
+    background: white;
+    border-bottom: none;
+    border-left: none;
+    border-right: 1px solid DarkCyan;
+    border-top: 1px solid DarkCyan;
+    color: black;
 }
 
-section#test-json > textarea,
-section#json > textarea {
-    width: 800px;
-    height: 460px;
+/* -------------------------------------------------------------------------- */
+/*                              Results JSON                                  */
+/* -------------------------------------------------------------------------- */
+
+div.results-json {
+    width: 100%;
     background-color: rgb(128, 128, 128);
-    border: 1px solid rgb(235, 235, 235);
-    color: white;
+    color: rgb(235, 235, 235);
     white-space: pre;
-    overflow: scroll;
+    font-size: 12px;
 }
 
-.options {
-    margin:0 auto;    
-    margin-top: 30px;
-    width: 600px;
-    align: center;
+/* -------------------------------------------------------------------------- */
+/*                                 Main Layout                                */
+/* -------------------------------------------------------------------------- */
+
+main {
+    width: 100%;
+    height: 100%;
+    display: -ms-flexbox;
+    display: -webkit-flex;
+    display: flex;
+    -ms-flex-align: center;
+    -webkit-align-items: center;
+    -webkit-box-align: center;
+    align-items: center;
+    -webkit-justify-content: center;
+    -ms-flex-pack: center;
+    justify-content: center;
+}
+
+section {
+    width: 800px;
+    height: 600px;
+    display: none;
+    padding: 10px;
+    border: 6px solid rgb(235, 235, 235);
+    border-radius: 20px;
 }
 
-.options p {
-    margin-top: 0;
+section.selected {
+    display: -ms-flexbox;
+    display: -webkit-flex;
+    display: flex;
 }
 
-#suites ul {
-    list-style-type: none;
-    margin: 0;
-    padding: 0;
+section > header,
+section > footer {
+    margin: 10px;
+    padding: 10px;
+    -webkit-flex: 0 1 100%;
+    -ms-flex: 0 1 100%;
+    flex: 0 1 100%;
+    max-height: 15%;
 }
 
-#suites ul ul {
-    padding-left: 1.5em;
-    display: none;
+section > footer {
+    text-align: center;
+    clear: both;
+    display: -ms-flexbox;
+    display: -webkit-flex;
+    display: flex;
+    -ms-flex-align: center;
+    -webkit-align-items: center;
+    -webkit-box-align: center;
+    align-items: center;
+    -webkit-justify-content: center;
+    -ms-flex-pack: center;
+    justify-content: center;
 }
 
-#suites ul ul input, #suites ul ul label {
-    font-size: .8em;
+/* -------------------------------------------------------------------------- */
+/*                                 Home Section                               */
+/* -------------------------------------------------------------------------- */
+
+section#home {
+    -webkit-flex-direction: row;
+    -ms-flex-direction: row;
+    flex-direction: row;
+    -webkit-flex-wrap: wrap;
+    -ms-flex-wrap: wrap;
+    flex-wrap: wrap;
+    -webkit-align-content: center;
+    -ms-flex-line-pack: center;
+    align-content: center;
 }
 
-#suites.showTests ul ul {
-    display: block;
+section#home > suites,
+section#home > options {
+    padding: 10px;
+    margin: 10px;
+    height: 50%;
 }
 
-.column {
-    width: 55%;
-    float:left;
+section#home > suites {
+    padding-left: 80px;
+    -webkit-flex: 0 1 40%;
+    -ms-flex: 0 1 40%;
+    flex: 0 1 40%;
 }
 
-input[type="number"] {
-   width:50px;
+section#home > options {
+    -webkit-flex: 1 1 auto;
+    -ms-flex: 1 1 auto;
+    flex: 1 1 auto;
 }
 
-.buttons {
-    margin-top: 10px;
+section#home > options > label {
+    margin: 2em;
+    line-height: 2;
+}
+
+section#home > header > h2 {
+    margin: 0 auto;
+    width: 70%;
     text-align: center;
-    clear: both;
 }
 
-button {
-    -webkit-appearance: none;
-    border: 3px solid rgb(235, 235, 235);
-    border-radius: 10px;
-    min-width: 200px;
-    padding: 5px 20px;
-    margin: 0 40px;
-    font-size: 25px;
-    color: rgb(235, 235, 235);
-    background-color: transparent;
+section#home > options > label > input[type="number"] {
+   width: 50px;
+}
 
-    -webkit-user-select: none;
+/* -------------------------------------------------------------------------- */
+/*                           Running Section                                  */
+/* -------------------------------------------------------------------------- */
+
+section#running {
+    position: relative;
+    -ms-flex-align: center;
+    -webkit-align-items: center;
+    -webkit-box-align: center;
+    align-items: center;
+    -webkit-justify-content: center;
+    -ms-flex-pack: center;
+    justify-content: center;
 }
 
-button:active {
-    background-color: rgb(235, 235, 235);
-    color: rgb(46, 51, 55);
-    border-color: rgb(235, 235, 235) !important;
+section#running > #running-test {
+    width: 100%;
+    height: 100%;
+}
+
+section#running > #running-test > iframe {
+    width: 100%;
+    height: 100%;
+    border: 0px none;
 }
 
-button:focus {
-    outline: none;
-    border-color: rgb(232, 79, 79);
+section#running > #progress {
+    position: absolute;
+    bottom: -6px;
+    left: 60px;
+    right: 60px;
+    height: 6px;
+    background-color: rgb(128, 128, 128);
+    border-left: 6px solid rgb(46, 51, 55);
+    border-right: 6px solid rgb(46, 51, 55);
 }
 
-.small-button {
-    -webkit-appearance: none;
-    border: 1px solid rgb(96, 96, 96);
-    border-radius: 2px;
-    padding: 1px 4px;
-    margin: 0 4px;
-    background-color: transparent;
-    cursor: pointer;
-    text-align: center;
-    -webkit-user-select: none;
+section#running > #progress > #progress-completed {
+    position: absolute;
+    top: 0;
+    left: 0;
+    height: 6px;
+    width: 0;
+    background-color: rgb(235, 235, 235);
 }
 
-#graphContainer {
-  font: 10px sans-serif;
-  color: rgb(235, 235, 235);  
+section#running > #record {
+    position: absolute;
+    bottom: -95px;
+    left: 0px;
+    right: 0px;
+    padding: 16px;
+}
+
+/* -------------------------------------------------------------------------- */
+/*                           Results Section                                  */
+/* -------------------------------------------------------------------------- */
+
+section#results,
+section#json,
+section#test-json,
+section#test-graph {
+    -webkit-flex-direction: row;
+    -ms-flex-direction: row;
+    flex-direction: row;
+    -webkit-flex-wrap: wrap;
+    -ms-flex-wrap: wrap;
+    flex-wrap: wrap;
+    -webkit-justify-content: space-between;
+    -ms-flex-pack: justify;
+    justify-content: space-between;
+    -webkit-align-content: space-between;
+    -ms-flex-line-pack: justify;
+    align-content: space-between;
+    -webkit-align-items: flex-start;
+    -ms-flex-align: start;
+    align-items: flex-start;
+}
+
+section#results > data,
+section#json > data,
+section#test-json > data,
+section#test-graph > data {
+    overflow-y: scroll;
+    height: 70%;
+    -webkit-flex: 0 1 100%;
+    -ms-flex: 0 1 100%;
+    flex: 0 1 100%;
+    -webkit-align-self: stretch;
+    -ms-flex-item-align: stretch;
+    align-self: stretch;
+}
+
+/* -------------------------------------------------------------------------- */
+/*                           Graph Section                                    */
+/* -------------------------------------------------------------------------- */
+
+section#test-graph > data {
+    font: 10px sans-serif;
+    color: rgb(235, 235, 235);
+}
+
+section#test-graph > data > svg {
+    fill: none;
+    shape-rendering: crispEdges;  
 }
 
 .axis path,
 .axis line {
-  fill: none;
-  stroke: #999;
-  shape-rendering: crispEdges;
+    fill: none;
+    stroke: #999999;
+    shape-rendering: crispEdges;
 }
 
 .left-samples {
-  fill: none;
-  stroke: #7ADD49;
-  stroke-width: 1.5px;
+    stroke: #7ADD49;
+    stroke-width: 1.5px;
 }
 
 .right-samples {
-    fill: none;
     stroke: #FA4925;
     stroke-width: 1.5px;
 }
 
 .sample-time {
-    fill: none;
     stroke: #5493D6;
-    stroke-width: 1px;
 }
 
 .left-mean {
-    fill: none;
     stroke: #7ADD49;
-    stroke-width: 1px;
     opacity: .8;
 }
 
 .right-mean {
-    fill: none;
     stroke: #FA4925;
-    stroke-width: 1px;
     opacity: .8;
 }
+
+/* -------------------------------------------------------------------------- */
+/*                           JSON Section                                  */
+/* -------------------------------------------------------------------------- */
+
+section#json > data,
+section#test-json > data {
+    border: 1px solid rgb(235, 235, 235);
+    box-sizing: border-box;
+}
index 0b1b7e83f601b956b7881209979d4d19282e3eca..c14e65b27585c99c9bcf638da2c38ac2dc5f189a 100644 (file)
@@ -3,11 +3,17 @@ window.benchmarkRunnerClient = {
     testsCount: null,
     progressBar: null,
     recordTable: null,
-    options: { testInterval: 30000, frameRate: 50, estimatedFrameRate: true, fixTestComplexity : false },
+    options: null,
     score: 0,
     _resultsDashboard: null,
     _resultsTable: null,
     
+    initialize: function(suites, options)
+    {
+        this.testsCount = this.iterationCount * suites.reduce(function (count, suite) { return count + suite.tests.length; }, 0);
+        this.options = options;
+    },
+
     willAddTestFrame: function (frame)
     {
         var main = document.querySelector("main");
@@ -24,10 +30,10 @@ window.benchmarkRunnerClient = {
     willStartFirstIteration: function ()
     {
         this._resultsDashboard = new ResultsDashboard();
-        this._resultsTable = new ResultsTable(document.querySelector(".results-table"), Headers);
+        this._resultsTable = new ResultsTable(document.querySelector("section#results > data > table"), Headers);
         
         this.progressBar = new ProgressBar(document.getElementById("progress-completed"), this.testsCount);
-        this.recordTable = new ResultsTable(document.querySelector(".record-table"), Headers);
+        this.recordTable = new ResultsTable(document.querySelector("section#running > #record > table"), Headers);
     },
     
     didRunSuites: function (suitesSamplers)
@@ -38,226 +44,365 @@ window.benchmarkRunnerClient = {
     didFinishLastIteration: function ()
     {
         var json = this._resultsDashboard.toJSON(true, true);
-        this._resultsTable.showIterations(json[Strings["JSON_RESULTS"][0]]);
-        
-        var element = document.querySelector("#json > textarea");
-        element.innerHTML = JSON.stringify(json[Strings["JSON_RESULTS"][0]][0], function(key, value) { 
-            if (typeof value == "number")
-                return value.toFixed(2);
-            return value;
-        }, 4);
-        
         this.score = json[Strings["JSON_SCORE"]];
-        showResults();
+        this._resultsTable.showIterations(json[Strings["JSON_RESULTS"][0]]);
+        sectionsManager.showJSON("json", json[Strings["JSON_RESULTS"][0]][0]);
+        benchmarkController.showResults();
     }
 }
 
-function showSection(sectionIdentifier, pushState)
+window.sectionsManager =
 {
-    var currentSectionElement = document.querySelector("section.selected");
-    console.assert(currentSectionElement);
+    _sectionHeaderH1Element: function(sectionIdentifier)
+    {
+        return document.querySelector("#" + sectionIdentifier + " > header > h1");
+    },
+    
+    _sectionDataDivElement: function(sectionIdentifier)
+    {
+        return document.querySelector("#" + sectionIdentifier + " >  data > div");
+    },
+    
+    showScore: function(sectionIdentifier, title)
+    {
+        var element = this._sectionHeaderH1Element(sectionIdentifier);
+        element.textContent = title + ":";
 
-    var newSectionElement = document.getElementById(sectionIdentifier);
-    console.assert(newSectionElement);
+        var score = benchmarkRunnerClient.score.toFixed(2);
+        element.textContent += " [Score = " + score + "]";
+    },
+    
+    showTestName: function(sectionIdentifier, title, testName)
+    {
+        var element = this._sectionHeaderH1Element(sectionIdentifier);
+        element.textContent = title + ":";
 
-    currentSectionElement.classList.remove("selected");
-    newSectionElement.classList.add("selected");
+        if (!testName.length)
+            return;
+            
+        element.textContent += " [test = " + testName + "]";
+    },
+    
+    showJSON: function(sectionIdentifier, json)
+    {
+        var element = this._sectionDataDivElement(sectionIdentifier);
+        element.textContent = JSON.stringify(json, function(key, value) { 
+            if (typeof value == "number")
+                return value.toFixed(2);
+            return value;
+        }, 4);
+    },
+    
+    showSection: function(sectionIdentifier, pushState)
+    {
+        var currentSectionElement = document.querySelector("section.selected");
+        console.assert(currentSectionElement);
 
-    if (pushState)
-        history.pushState({section: sectionIdentifier}, document.title);
-}
+        var newSectionElement = document.getElementById(sectionIdentifier);
+        console.assert(newSectionElement);
 
-function startBenchmark()
-{
-    var enabledSuites = [];
-
-    localStorage.clear();
-    var suiteItems = document.querySelectorAll("#suites > ul > li");
-    for (var i = 0; i < suiteItems.length; ++i) {
-        var suiteItem = suiteItems[i];
-        var suiteCheckbox = suiteItem.querySelector("input");
-
-        if (!suiteCheckbox.checked)
-            continue;
-
-        var enabledTests = [];
-        var testCheckboxes = suiteItem.querySelector("ul").querySelectorAll("input");
-        for (var j = 0; j < testCheckboxes.length; ++j) {
-            var testCheckbox = testCheckboxes[j];
-            if (!testCheckbox.checked)
-                continue;
+        currentSectionElement.classList.remove("selected");
+        newSectionElement.classList.add("selected");
 
-            enabledTests.push(testCheckbox.test);
-            localStorage.setItem(localStorageNameForTest(suiteCheckbox.suite, testCheckbox.test), +testCheckbox.checked);
-        }
+        if (pushState)
+            history.pushState({section: sectionIdentifier}, document.title);
+    },
 
-        enabledSuites.push(new Suite(suiteCheckbox.suite.name, enabledTests));
+    setupSectionStyle: function()
+    {
+        if (screen.width >= 1800 && screen.height >= 1000)
+            DocumentExtension.insertCssRuleAfter(" section { width: 1600px; height: 800px; }", "section");
+        else
+            DocumentExtension.insertCssRuleAfter(" section { width: 800px; height: 600px; }", "section");
+    },
+    
+    setupRunningSectionStyle: function(options)
+    {
+        if (!options["show-running-results"])
+            document.getElementById("record").style.display = "none";
+
+        if (options["normalize-for-device-scale-factor"] && window.devicePixelRatio != 1) {
+            var percentage = window.devicePixelRatio * 100;
+            var rule = "section#running > #running-test > iframe";
+            var newRule = rule;
+            newRule += " { ";
+            newRule += "width: " + percentage + "%; ";
+            newRule += "height: " + percentage + "%; ";
+            newRule += "transform: scale(" + 100 / percentage + ") translate(" + (100 - percentage) / 2 + "%," + (100 - percentage) / 2 + "%);";
+            newRule += " }";
+            DocumentExtension.insertCssRuleAfter(newRule, rule);
+        }
     }
+}
 
-    localStorage.setItem("test-interval", document.getElementById("test-interval").value);
-
-    var testsCount = enabledSuites.reduce(function (testsCount, suite) { return testsCount + suite.tests.length; }, 0);
-    benchmarkRunnerClient.testsCount = benchmarkRunnerClient.iterationCount * testsCount;
-    benchmarkRunnerClient.options["testInterval"] = parseInt(document.getElementById("test-interval").value) * 1000;
-    benchmarkRunnerClient.options["frameRate"] = parseInt(document.getElementById("frame-rate").value);
-    benchmarkRunnerClient.options["estimatedFrameRate"] = document.getElementById("estimated-frame-rate").checked;    
-    benchmarkRunnerClient.options["fixTestComplexity"] = document.getElementById("fix-test-complexity").checked;
-    benchmarkRunnerClient.options["showRunningResults"] = document.getElementById("show-running-results").checked;
+window.optionsManager =
+{
+    _optionsElements : function()
+    {
+        return document.querySelectorAll("section#home > options input");;
+    },
     
-    if (!benchmarkRunnerClient.options["showRunningResults"])
-        document.getElementById("record").style.display = "none";
+    updateUIFromLocalStorage: function()
+    {
+        var optionsElements = this._optionsElements();
 
-    var runner = new BenchmarkRunner(enabledSuites, benchmarkRunnerClient);
-    runner.runMultipleIterations(benchmarkRunnerClient.iterationCount);
-}
+        for (var i = 0; i < optionsElements.length; ++i) {
+            var optionElement = optionsElements[i];
+            
+            var value = localStorage.getItem(optionElement.id);
+            if (value === null)
+                continue;
 
-function startTest()
-{
-    showSection("running");
-    startBenchmark();
-}
+            if (optionElement.getAttribute("type") == "number")
+                optionElement.value = +value;
+            else if (optionElement.getAttribute("type") == "checkbox")
+                optionElement.checked = value == "true";
+        }
+    },
 
-function showResults()
-{
-    var element = document.querySelector("#results > h1");
-    element.textContent = Strings["TEXT_RESULTS"][0] + ":";
+    updateLocalStorageFromUI: function()
+    {
+        var optionsElements = this._optionsElements();
+        var options = {};        
+
+        for (var i = 0; i < optionsElements.length; ++i) {
+            var optionElement = optionsElements[i];
+        
+            if (optionElement.getAttribute("type") == "number")
+                options[optionElement.id] = optionElement.value;
+            else if (optionElement.getAttribute("type") == "checkbox")
+                options[optionElement.id] = optionElement.checked;
     
-    var score = benchmarkRunnerClient.score.toFixed(2);
-    element.textContent += " [Score = " + score + "]";
+            localStorage.setItem(optionElement.id, options[optionElement.id]);
+        }
         
-    showSection("results", true);
+        return options;
+    }
 }
 
-function showJson()
+window.suitesManager =
 {
-    var element = document.querySelector("#json > h1");
-    element.textContent = Strings["TEXT_RESULTS"][2] + ":";
+    _treeElement : function()
+    {
+        return document.querySelector("suites > .tree");
+    },
+    
+    _suitesElements : function()
+    {
+        return document.querySelectorAll("#home > suites > ul > li");
+    },
     
-    var score = benchmarkRunnerClient.score.toFixed(2);
-    element.textContent += " [Score = " + score + "]";
+    _checkboxElement: function(element)
+    {
+        return element.querySelector("input[type='checkbox']:not(.expand-button)");
+    },
 
-    showSection("json", true);
-}
+    _localStorageNameForTest: function(suite, test)
+    {
+        return suite.name + "/" + test.name;
+    },
 
-function showTestGraph(testName, axes, samples, samplingTimeOffset)
-{
-    var element = document.querySelector("#test-graph > h1");
-    element.textContent = Strings["TEXT_RESULTS"][1] + ":";
+    _updateSuiteCheckboxState: function(suiteCheckbox)
+    {
+        var numberEnabledTests = 0;
+        suiteCheckbox.testsElements.forEach(function(testElement) {
+            var testCheckbox = this._checkboxElement(testElement);
+            if (testCheckbox.checked)
+                ++numberEnabledTests;
+        }, this);
+        suiteCheckbox.checked = numberEnabledTests > 0;
+        suiteCheckbox.indeterminate = numberEnabledTests > 0 && numberEnabledTests < suiteCheckbox.testsElements.length;
+    },
 
-    if (testName.length)
-        element.textContent += " [test = " + testName + "]";
+    _updateStartButtonState: function()
+    {
+        var suitesElements = this._suitesElements();
+        var startButton = document.querySelector("#home > footer > button");
+        
+        for (var i = 0; i < suitesElements.length; ++i) {
+            var suiteElement = suitesElements[i];
+            var suiteCheckbox = this._checkboxElement(suiteElement);
             
-    graph("#graphContainer", new Point(700, 400), new Insets(20, 50, 20, 50), axes, samples, samplingTimeOffset);
-    showSection("test-graph", true);    
-}
+            if (suiteCheckbox.checked) {
+                startButton.disabled = false;
+                return;
+            }
+        }
+    
+        startButton.disabled = true;
+    },
 
-function showTestJSON(testName, testResults)
-{
-    var element = document.querySelector("#test-json > h1");
-    element.textContent = Strings["TEXT_RESULTS"][2] + ":";
+    _onChangeSuiteCheckbox: function(event)
+    {
+        var selected = event.target.checked;
+        event.target.testsElements.forEach(function(testElement) {
+            var testCheckbox = this._checkboxElement(testElement);
+            testCheckbox.checked = selected;        
+        }, this);
+        this._updateStartButtonState();
+    },
 
-    if (testName.length)
-        element.textContent += " [test = " + testName + "]";
-            
-    var element = document.querySelector("#test-json > textarea");
-    element.innerHTML = JSON.stringify(testResults, function(key, value) { 
-        if (typeof value == "number")
-            return value.toFixed(2);
-        return value;
-    }, 4);
-
-    showSection("test-json", true);    
-}
+    _onChangeTestCheckbox: function(event)
+    {
+        var suiteCheckbox = event.target.suiteCheckbox;
+        this._updateSuiteCheckboxState(suiteCheckbox);
+        this._updateStartButtonState();
+    },
 
-function initialize() {
-    populateSettings();
+    _createSuiteElement: function(treeElement, suite, id)
+    {
+        var suiteElement = DocumentExtension.createElement("li", {}, treeElement);
+        var expand = DocumentExtension.createElement("input", { type: "checkbox",  class: "expand-button", id: id }, suiteElement);
+        var label = DocumentExtension.createElement("label", { class: "tree-label", for: id }, suiteElement);
 
-    var toggleTestsCheckbox = document.getElementById("toggleTests");
-    toggleTestsCheckbox.onchange = function(event) {
-        if (event.target.checked)
-            document.getElementById("suites").classList.add("showTests");
-        else
-            document.getElementById("suites").classList.remove("showTests");
-    };
-}
+        var suiteCheckbox = DocumentExtension.createElement("input", { type: "checkbox" }, label);
+        suiteCheckbox.suite = suite;
+        suiteCheckbox.onchange = this._onChangeSuiteCheckbox.bind(this);
+        suiteCheckbox.testsElements = [];
 
-function updateSuiteSelection(event) {
-    var selected = event.target.checked;
-    var testCheckboxes = event.target.parentNode.parentNode.querySelector("ul").querySelectorAll("input");
-    for (var i = 0; i < testCheckboxes.length; ++i) {
-        testCheckboxes[i].checked = selected;
-    }
-}
+        label.appendChild(document.createTextNode(" " + suite.name));
+        return suiteElement;
+    },
 
-function updateTestSelection(event) {
-    var testsList = event.target.parentNode.parentNode.parentNode;
-    var suiteCheckbox = testsList.parentNode.querySelector("label > input");
+    _createTestElement: function(listElement, test, suiteCheckbox)
+    {
+        var testElement = DocumentExtension.createElement("li", {}, listElement);
+        var span = DocumentExtension.createElement("span", { class: "tree-label" }, testElement);
 
-    updateSuiteCheckbox(testsList, suiteCheckbox);
-}
+        var testCheckbox = DocumentExtension.createElement("input", { type: "checkbox" }, span);
+        testCheckbox.test = test;
+        testCheckbox.onchange = this._onChangeTestCheckbox.bind(this);
+        testCheckbox.suiteCheckbox = suiteCheckbox;
 
-function updateSuiteCheckbox(testsList, suiteCheckbox) {
-    var numberEnabledTests = 0;
-    var testCheckboxes = testsList.querySelectorAll("input");
-    var totalCheckboxes = testCheckboxes.length;
-    for (var i = 0; i < totalCheckboxes; ++i) {
-        if (testCheckboxes[i].checked)
-            ++numberEnabledTests;
-    }
-    suiteCheckbox.checked = numberEnabledTests > 0;
-    suiteCheckbox.indeterminate = numberEnabledTests > 0 && numberEnabledTests < totalCheckboxes;
-}
+        suiteCheckbox.testsElements.push(testElement);
+        span.appendChild(document.createTextNode(" " + test.name));
+        return testElement;
+    },
 
-function localStorageNameForTest(suite, test) {
-    return suite.name + "/" + test.name;
-}
+    createElements: function()
+    {
+        var treeElement = this._treeElement();
 
-function populateSettings() {
-    var suitesDiv = document.getElementById("suites");
+        Suites.forEach(function(suite, index) {
+            var suiteElement = this._createSuiteElement(treeElement, suite, "suite-" + index);
+            var listElement = DocumentExtension.createElement("ul", {}, suiteElement);
+            var suiteCheckbox = this._checkboxElement(suiteElement);
 
-    var suitesList = document.createElement("ul");
-    suitesDiv.appendChild(suitesList);
+            suite.tests.forEach(function(test) {
+                var testElement = this._createTestElement(listElement, test, suiteCheckbox);
+            }, this);
+        }, this);
+    },
+    
+    updateUIFromLocalStorage: function()
+    {
+        var suitesElements = this._suitesElements();
+        
+        for (var i = 0; i < suitesElements.length; ++i) {
+            var suiteElement = suitesElements[i];
+            var suiteCheckbox = this._checkboxElement(suiteElement);
+            var suite = suiteCheckbox.suite;
+            
+            suiteCheckbox.testsElements.forEach(function(testElement) {
+                var testCheckbox = this._checkboxElement(testElement);
+                var test = testCheckbox.test;
+                
+                var str = localStorage.getItem(this._localStorageNameForTest(suite, test));
+                if (str === null)
+                    return;
+
+                var value = JSON.parse(str);
+                testCheckbox.checked = value.checked;
+            }, this);
+
+            this._updateSuiteCheckboxState(suiteCheckbox);
+        }
+        
+        this._updateStartButtonState();
+    },
+
+    updateLocalStorageFromUI: function()
+    {
+        var suitesElements = this._suitesElements();
+        var suites = [];
+        
+        for (var i = 0; i < suitesElements.length; ++i) {
+            var suiteElement = suitesElements[i];
+            var suiteCheckbox = this._checkboxElement(suiteElement);
+            var suite = suiteCheckbox.suite;
+
+            var tests = [];
+            suiteCheckbox.testsElements.forEach(function(testElement) {
+                var testCheckbox = this._checkboxElement(testElement);
+                var test = testCheckbox.test;
+                
+                if (testCheckbox.checked)
+                    tests.push(test);
+
+                var value = { checked: testCheckbox.checked }; 
+                localStorage.setItem(this._localStorageNameForTest(suite, test), JSON.stringify(value));
+            }, this);
+
+            if (tests.length)
+                suites.push(new Suite(suiteCheckbox.suite.name, tests));
+        }
 
-    Suites.forEach(function(suite) {
-        var suiteItem = document.createElement("li");
-        suitesList.appendChild(suiteItem);
+        return suites;
+    }
+}
 
-        var suiteLabel = document.createElement("label");
-        suiteItem.appendChild(suiteLabel);
+window.benchmarkController =
+{
+    initialize: function()
+    {
+        sectionsManager.setupSectionStyle();
+        optionsManager.updateUIFromLocalStorage();
+        suitesManager.createElements();
+        suitesManager.updateUIFromLocalStorage();
+    },
 
-        var suiteCheckbox = document.createElement("input");
-        suiteCheckbox.setAttribute("type", "checkbox");
-        suiteCheckbox.suite = suite;
-        suiteCheckbox.onchange = updateSuiteSelection;
-        suiteLabel.appendChild(suiteCheckbox);
-        suiteLabel.appendChild(document.createTextNode(" " + suite.name));
-
-        var testsList = document.createElement("ul");
-        suiteItem.appendChild(testsList);
-
-        suite.tests.forEach(function(test) {
-            var testItem = document.createElement("li");
-            testsList.appendChild(testItem);
-
-            var testLabel = document.createElement("label");
-            testItem.appendChild(testLabel);
-
-            var testCheckbox = document.createElement("input");
-            testCheckbox.setAttribute("type", "checkbox");
-            testCheckbox.test = test;
-            testCheckbox.onchange = updateTestSelection;
-            if (+localStorage.getItem(localStorageNameForTest(suite, test)))
-                testCheckbox.checked = true;
-            testLabel.appendChild(testCheckbox);
-            testLabel.appendChild(document.createTextNode(" " + test.name));
-        });
-
-        updateSuiteCheckbox(testsList, suiteCheckbox);
-    });
-
-    var interval = localStorage.getItem("test-interval");
-    if (interval) {
-        document.getElementById("test-interval").value = interval;
+    _runBenchmark: function(suites, options)
+    {
+        benchmarkRunnerClient.initialize(suites, options);
+        var frameContainer = document.querySelector("#running > #running-test");
+        var runner = new BenchmarkRunner(suites, frameContainer || document.body, benchmarkRunnerClient);
+        runner.runMultipleIterations();
+    },
+
+    startTest: function()
+    {
+        var options = optionsManager.updateLocalStorageFromUI();
+        var suites = suitesManager.updateLocalStorageFromUI();
+        sectionsManager.setupRunningSectionStyle(options);
+        this._runBenchmark(suites, options);
+        sectionsManager.showSection("running");
+    },
+    
+    showResults: function()
+    {
+        sectionsManager.showScore("results", Strings["TEXT_RESULTS"][0]);
+        sectionsManager.showSection("results", true);
+    },
+    
+    showJson: function()
+    {
+        sectionsManager.showScore("json", Strings["TEXT_RESULTS"][0]);
+        sectionsManager.showSection("json", true);
+    },
+    
+    showTestGraph: function(testName, axes, samples, samplingTimeOffset)
+    {
+        sectionsManager.showTestName("test-graph", Strings["TEXT_RESULTS"][1], testName);
+        sectionsManager.showSection("test-graph", true);
+        graph("section#test-graph > data", new Insets(20, 50, 20, 50), axes, samples, samplingTimeOffset);
+    },
+
+    showTestJSON: function(testName, json)
+    {
+        sectionsManager.showTestName("test-json", Strings["TEXT_RESULTS"][1], testName);
+        sectionsManager.showJSON("test-json", json);
+        sectionsManager.showSection("test-json", true);
     }
 }
-document.addEventListener("DOMContentLoaded", initialize);
+
+document.addEventListener("DOMContentLoaded", benchmarkController.initialize());
index 1273964e2a9764a2ad8383547847bae189bac301..4f4834524335b758076dea4068e617861957813e 100644 (file)
@@ -50,10 +50,11 @@ BenchmarkRunnerState.prototype.prepareCurrentTest = function(runner, frame)
     return promise;
 }
 
-function BenchmarkRunner(suites, client)
+function BenchmarkRunner(suites, frameContainer, client)
 {
     this._suites = suites;
     this._client = client;
+    this._frameContainer = frameContainer;
 }
 
 BenchmarkRunner.prototype.waitForElement = function(selector)
@@ -72,25 +73,15 @@ BenchmarkRunner.prototype.waitForElement = function(selector)
     return promise;
 }
 
-BenchmarkRunner.prototype._appendFrame = function(src)
+BenchmarkRunner.prototype._appendFrame = function()
 {
     var frame = document.createElement("iframe");
     frame.setAttribute("scrolling", "no");
 
-    var marginLeft = parseInt(getComputedStyle(document.body).marginLeft);
-    var marginTop = parseInt(getComputedStyle(document.body).marginTop);
-    if (window.innerWidth > 800 + marginLeft && window.innerHeight > 600 + marginTop) {
-        frame.style.left = marginLeft + "px";
-        frame.style.top = marginTop + "px";
-    } else {
-        frame.style.left = "0px";
-        frame.style.top = "0px";
-    }
-
     if (this._client && this._client.willAddTestFrame)
         this._client.willAddTestFrame(frame);
 
-    document.body.insertBefore(frame, document.body.firstChild);
+    this._frameContainer.insertBefore(frame, this._frameContainer.firstChild);
     this._frame = frame;
     return frame;
 }
@@ -167,21 +158,21 @@ BenchmarkRunner.prototype.runAllSteps = function(startingState)
     });
 }
 
-BenchmarkRunner.prototype.runMultipleIterations = function(iterationCount)
+BenchmarkRunner.prototype.runMultipleIterations = function()
 {
     var self = this;
     var currentIteration = 0;
 
     this._runNextIteration = function() {
         currentIteration++;
-        if (currentIteration < iterationCount)
+        if (currentIteration < self._client.iterationCount)
             self.runAllSteps();
         else if (this._client && this._client.didFinishLastIteration)
             self._client.didFinishLastIteration();
     }
 
     if (self._client && self._client.willStartFirstIteration)
-        self._client.willStartFirstIteration(iterationCount);
+        self._client.willStartFirstIteration();
 
     self.runAllSteps();
 }
index d0b103411463759c14f67f37c8dbb3bfde0fcf80..021f5f5d669de00fef39d2e92ccb39bc04655c1c 100644 (file)
@@ -1,7 +1,9 @@
-function graph(selector, size, margins, axes, samples, samplingTimeOffset)
+function graph(selector, margins, axes, samples, samplingTimeOffset)
 {
     var element = document.querySelector(selector);
-    element.textContent = null;
+    element.innerHTML = '';
+
+    var size = Point.elementClientSize(element).subtract(margins.size);
 
     var x = d3.scale.linear()
             .range([0, size.width])
index cfe2c40275f5fbf34d262ce7089fb802dd18d784..c68bce0a1bfccf407f91b11d8a0346161827aca7 100644 (file)
@@ -5,7 +5,7 @@ function BouncingSvgImage(stage)
     
     var attrs = { x: 0, y: 0, width: this._size.x, height: this._size.y };
     var xlinkAttrs = { href: stage.imageSrc };
-    this.element = Utilities.createSvgElement("image", attrs, xlinkAttrs, stage.element);
+    this.element = DocumentExtension.createSvgElement("image", attrs, xlinkAttrs, stage.element);
     this._move();
 }
 
index 2d29bf40a5a5a7f00ac2cfc7e00f5e64a50863a1..69c675415b89bb392ae5aabd5511d9efd20646ad 100644 (file)
@@ -39,7 +39,7 @@ BouncingSvgParticlesStage.prototype.constructor = BouncingSvgParticlesStage;
 
 BouncingSvgParticlesStage.prototype._createDefs = function()
 {
-    return Utilities.createSvgElement("defs", {}, {}, this.element);
+    return DocumentExtension.createSvgElement("defs", {}, {}, this.element);
 }
                                                                
 BouncingSvgParticlesStage.prototype._ensureDefsIsCreated = function()
@@ -50,10 +50,10 @@ BouncingSvgParticlesStage.prototype._ensureDefsIsCreated = function()
 BouncingSvgParticlesStage.prototype._createClipStar = function()
 {
     var attrs = { id: "star-clip", clipPathUnits: "objectBoundingBox" };
-    var clipPath  = Utilities.createSvgElement("clipPath", attrs, {}, this._ensureDefsIsCreated());
+    var clipPath  = DocumentExtension.createSvgElement("clipPath", attrs, {}, this._ensureDefsIsCreated());
 
     attrs = { d: "M.50,0L.38,.38L0,.38L.30,.60L.18,1L.50,.75L.82,1L.70,.60L1,.38L.62,.38z" };
-    Utilities.createSvgElement("path", attrs, {}, clipPath);
+    DocumentExtension.createSvgElement("path", attrs, {}, clipPath);
     return clipPath;
 }
 
index 1980110a25dbf2adb4bb385f554cca0524e68c85..76dc9ee553709476317ec95444490b48489726f1 100644 (file)
@@ -19,13 +19,13 @@ BouncingSvgShape.prototype._createShape = function(stage)
     switch (this._shape) {
     case "rect":
         var attrs = { x: 0, y: 0, width: this._size.x, height: this._size.y };
-        this.element = Utilities.createSvgElement("rect", attrs, {}, stage.element);
+        this.element = DocumentExtension.createSvgElement("rect", attrs, {}, stage.element);
         break;
     
     case "circle":
     default:
         var attrs = { cx: this._size.x / 2, cy: this._size.y / 2, r: Math.min(this._size.x, this._size.y) / 2 };
-        this.element = Utilities.createSvgElement("circle", attrs, {}, stage.element);
+        this.element = DocumentExtension.createSvgElement("circle", attrs, {}, stage.element);
         break;        
     }
 }
@@ -58,11 +58,11 @@ BouncingSvgShapesStage.prototype.constructor = BouncingSvgShapesStage;
 BouncingSvgShapesStage.prototype.createGradient = function(stops)
 {
     var attrs = { id: "gadient-" + ++this._gradientsCount };
-    var gradient = Utilities.createSvgElement("linearGradient", attrs, {}, this._ensureDefsIsCreated());    
+    var gradient = DocumentExtension.createSvgElement("linearGradient", attrs, {}, this._ensureDefsIsCreated());    
     
     for (var i = 0; i < stops; ++i) {
         attrs = { offset: i * 100 / stops + "%", 'stop-color': this.randomColor() };
-        Utilities.createSvgElement("stop", attrs, {}, gradient);
+        DocumentExtension.createSvgElement("stop", attrs, {}, gradient);
     }
 
     return gradient;
index ea77ceb9397f77a29f7f8dce505300bceb71920d..d645610b8d2a7e0c08f0818c0f4c72c0956dc640 100644 (file)
@@ -109,7 +109,7 @@ Animator.prototype =
         var currentFrameRate = Math.floor(1000 / (measureTimeDelta / this._measureFrameCount));
          
         // Use Kalman filter to get a more non-fluctuating frame rate.
-        if (this._benchmark.options.estimatedFrameRate)
+        if (this._benchmark.options["estimated-frame-rate"])
             currentFrameRate = this._estimator.estimate(measureTimeDelta, currentFrameRate);
         
         // Adjust the test to reach the desired FPS.
@@ -146,9 +146,9 @@ function Benchmark(options)
     var lowValue = -parseInt(this._options["addLimit"]) || 1;
     var highValue = parseInt(this._options["removeLimit"]) || 1;
     
-    this._controller = new PIDController(gain, options.frameRate, lowValue, highValue);
+    this._controller = new PIDController(gain, options["frame-rate"], lowValue, highValue);
     this._sampler = new Sampler(2);
-    this._state = new BenchmarkState(this.options.testInterval);    
+    this._state = new BenchmarkState(this.options["test-interval"] * 1000);
 }
 
 Benchmark.prototype =
@@ -179,7 +179,7 @@ Benchmark.prototype =
         }
 
         var tuneValue = 0;
-        if (!(this._isSampling && this.options.fixTestComplexity)) {
+        if (!(this._isSampling && this.options["fix-test-complexity"])) {
             // The relationship between frameRate and test complexity is inverse-proportional so we
             // need to use the negative of PIDController.tune() to change the complexity of the test.
             tuneValue = -this._controller.tune(currentFrameRate, timeDelta / 1000);
index 07fa6617d6fb55058fa0bb077f6ac0aaa3ce781f..2bb49d5fddb203dd895912efb6e91b1ec8e9f42e 100644 (file)
@@ -177,7 +177,7 @@ StageBenchmark.prototype.showResults = function(message, progress)
     if (!this._recordTable || !this._progressBar || !this._test)
         return;
 
-    if (this.options.showRunningResults)
+    if (this.options["show-running-results"])
         this._recordTable.showRecord(this._test.name, message, this._sampler.toJSON(true, false));
 
     this._progressBar.setPos(progress);
index 0726001f56c8a01e834d1dc54de860562c0c03d7..a6c7649dc77a42a452a6acc35b69868e18b732dc 100644 (file)
@@ -39,22 +39,5 @@ window.Utilities =
     mergeObjects: function(obj1, obj2)
     {
         return this.extendObject(this.copyObject(obj1), obj2);
-    },
-    
-    createSvgElement: function(name, attrs, xlinkAttrs, parent)
-    {
-        const svgNamespace = "http://www.w3.org/2000/svg";
-        const xlinkNamespace = "http://www.w3.org/1999/xlink";
-
-        var element = document.createElementNS(svgNamespace, name);
-        
-        for (var key in attrs)
-            element.setAttribute(key, attrs[key]);
-            
-        for (var key in xlinkAttrs)
-            element.setAttributeNS(xlinkNamespace, key, xlinkAttrs[key]);
-            
-        parent.appendChild(element);
-        return element;
     }
 }
index f58ef4ca6fdf4cc612aa97db9f3d19fa8b6e14c5..1f2a33e341edae2cd72ff018eca8427608546ef5 100644 (file)
@@ -3,12 +3,12 @@
 <head>
     <style>
         .text-layer {
-            font-size: 5.5px;
             position: absolute;
             left: 0px;
             top: 0px;
             padding: 3px;
             background-color: transparent;
+            line-height: 1.2;
         }
     </style>  
     <link rel="stylesheet" type="text/css" href="../resources/stage.css">
index 385e1976bce92804ac76c2b482b3d5b02ce6bbbc..1d64654a345d1c56b9e19aefb4ebd2b64346c808 100644 (file)
@@ -6,6 +6,7 @@ function LayeringTextStage(element, options)
     this._textItemIndex = 0;
     this._colorIndex = 0;
     this._animateCounts = 0;
+    this._setFontSize();
 }
 
 LayeringTextStage.textItems = [
@@ -137,6 +138,14 @@ LayeringTextStage.colorIndexToTextElementIndex = function(colorIndex)
 LayeringTextStage.prototype = Object.create(Stage.prototype);
 LayeringTextStage.prototype.constructor = LayeringTextStage;
 
+LayeringTextStage.prototype._setFontSize = function()
+{
+    var lineHeight = this.size.height / LayeringTextStage.colorableTextItems;
+    var fontHeight = lineHeight / 1.5;
+    var fontSize = fontHeight * 72.0 / 96.0;
+    DocumentExtension.insertCssRuleAfter(".text-layer", ".text-layer { font-size: " + fontSize.toFixed(2) + "px; }");
+}
+
 LayeringTextStage.prototype._nextTextItem = function(textItemFlags)
 {
     var textItem = LayeringTextStage.textItems[this._textItemIndex];
index 3b9cae1bcfc77b5140d2aa7c0083cd1feebd439a..deba5a8f0ebbed6b0104cdab0223244a4aa42031 100644 (file)
@@ -1,3 +1,226 @@
+2015-11-01  Said Abou-Hallawa  <sabouhallawa@apple,com>
+
+        Make the size of the benchmark canvas adaptive to the screen size and screen resolution
+        https://bugs.webkit.org/show_bug.cgi?id=150530
+
+        Reviewed by Darin Adler.
+        
+        We want to set the size of the benchmark stage dynamically such that it
+        depends on the screen resolution and the device scale factor. This patch 
+        does more than that because the home page css was not done properly. To
+        use the flex box layout, the animometer.css has to be rewritten almost from
+        scratch. The suites tree has to be rewritten also because it was not collapsing
+        and with the flex box layout it was going outside of the window area. The 
+        options handling and the local storage handling had to be rewritten to
+        allow more flexibility with this patch and the future patches. The code
+        in animometer.js was reorganized into objects to allow distributing the code
+        nicely among separate entities.
+
+        * Animometer/resources/extensions.js:
+        (Point.elementClientSize): Returns the client size of an HTMLElement as a Point object.
+        (Insets.prototype.get width): Follow the function opening brace style guidelines.
+        (Insets.prototype.get height):
+        (Insets.prototype.get size): Returns the size of an Insets as a Point object.
+        
+        (window.DocumentExtension): Provides document helper functions. It should be assailable from the runner and the tests.
+        (window.DocumentExtension.createElement): Creates an HTMLElement given its name, attributes and parentElement. 
+        (window.DocumentExtension.createSvgElement): Creates an SVGElement given its name, attributes and parentElement (moved from utilities.js). 
+        (window.DocumentExtension.insertCssRuleAfter): Inserts a CSS rule after an exiting rule given its text.
+        
+        (ResultsTable.prototype._showHeader): Use DocumentExtension functions.
+        (ResultsTable.prototype._showGraph): Use DocumentExtension functions and create a real button for "Graph..." option.
+        (ResultsTable.prototype._showJSON): Use DocumentExtension functions and create a real button for "JSON..." option.
+        (Options): Deleted.
+        
+        * Animometer/runner/animometer.html: Restructure the page to use the flex box layout.
+        
+        * Animometer/runner/resources/animometer.css:
+        (html,body):
+        (button):
+        
+        (button.large-button):The large button appears in the animometer.html.
+        (button.large-button:active):
+        (button.large-button:disabled):
+        
+        (button.small-button): The small button appears in the results table.
+        (button.small-button:active):
+        
+        (.tree): The tree class is used to list the suites and their tests.
+        (.tree .expand-button): This button expands a tree element.
+        (.tree .expand-button ~ ul): Hide the children (<ul>...</ul>) of a parent node by default.
+        (.tree .expand-button:checked ~ ul): Show the children of a parent node only when checked.
+        (.tree ul): Hide the list bullets.
+        (.tree li): Indent every node in the tree relative to its parent.
+        (.tree ul li): Indent all the non top level nodes only (the tests nodes in our case).
+        (.tree > li:last-child): Do not indent the bottom of the last child node.
+        (.tree-label): Style for all the labels in the tree.
+        (label.tree-label): Style for the labels in the top level only (the suites nodes in our case).
+        (label.tree-label:before): Style the unchecked case of the expand-button.
+        (:checked ~ label.tree-label:before): Style the checked case of the expand-button.
+        
+        (table.results-table): The results table appears while running the test and at the end.
+        (.results-table td):
+        (.results-table th):
+        
+        (div.results-json): The JSON div appears per test or for the whole run.
+        
+        (main): This is the flex box container.
+        
+        (section): A section is displayed exclusively inside the <main>. It is hidden by default.
+        (section.selected): When it is selected, its layout is flex layout.
+        (section > footer): The header or the footer of a section should not take more than 15% of the container.
+        
+        (section#home): The home section has <suites> and <options> parts to be laid out in the middle.
+        (section#home > options): 
+        (section#home > suites): The <suites> should not take more than 40% of the width.
+        (section#home > options > label): The benchmark title.
+        (section#home > header > h2): The benchmark title.
+        (section#home > options > label > input[type="number"]): Sets the width of the option edit control.
+        
+        (section#running): The running section contain the runner <iframe> which takes the whole area of the <main>.
+        (section#running > #running-test): This is the <iframe> container.
+        (section#running > #running-test > iframe): The <iframe> is created by the runner for each test.
+        (section#running > #progress): This is the progress bar.
+        (section#running > #progress > #progress-completed): This is another element which grows while the runner is progressing.
+        (section#running > #record): This the container of the record results table which is shown while running a test.
+        
+        (section#results):
+        (section#json):
+        (section#test-json):
+        (section#test-graph): All these sections have the same layout. A <data> element is laid out between <header> and <footer>.
+
+        (section#results > data):
+        (section#json > data):
+        (section#test-json > data):
+        (section#test-graph > data): The <data> element should take 70% of the <section>.
+        
+        (section#test-graph > data > svg):
+        (.axis line):
+        (.left-samples): These styles are for the d3 graph.
+        
+        (section#test-json > data): This is the style of the JSON <data> element.
+        
+        (iframe): Deleted.
+        (label, p): Deleted.
+        (section > p): Deleted.
+        (section#home > p): Deleted.
+        (section#home > p:first-child): Deleted.
+        (#testContainer): Deleted.
+        (section#running #progress-completed): Deleted.
+        (section#results > table): Deleted.
+        (section#results > table td, th): Deleted.
+        (section#results > table tr.alt, td): Deleted.
+        (section#results > table th): Deleted.
+        (section#json > textarea): Deleted.
+        (.options): Deleted.
+        (.options p): Deleted.
+        (#suites ul): Deleted.
+        (#suites ul ul): Deleted.
+        (#suites ul ul input, #suites ul ul label): Deleted.
+        (#suites.showTests ul ul): Deleted.
+        (.column): Deleted.
+        (input[type="number"]): Deleted.
+        (.buttons): Deleted.
+        (.small-button): Deleted.
+        (#graphContainer): Deleted.
+        (.right-samples): Deleted.
+        (.sample-time): Deleted.
+        (.left-mean): Deleted.
+        (.right-mean): Deleted.
+        
+        * Animometer/runner/resources/animometer.js:
+        (window.benchmarkRunnerClient.initialize): Initialize the client object with the options and the suites.
+        (window.benchmarkRunnerClient.willStartFirstIteration): Use new css selectors for results and the record table.
+        (window.benchmarkRunnerClient.didFinishLastIteration): Move the code which sets the JSON text to sectionsManager.showJSON().
+        
+        (window.sectionsManager): Responsible of managing the <section>s elements inside animometer.html.
+        (window.sectionsManager._sectionHeaderH1Element): Return the <h1> inside the <header> of a given section.
+        (window.sectionsManager._sectionDataDivElement): Return the <div> inside the <data> of a given section.
+        (window.sectionsManager.showScore): Show the score of the last benchmark run.
+        (window.sectionsManager.showTestName): Show the test name for detailed results <section>.
+        (window.sectionsManager.showJSON): Shows the JSON text of the last benchmark or for a specific test.
+        (window.sectionsManager.showSection): Shows a specific <section> in the <main> container.
+        (window.sectionsManager.setupSectionStyle): Sets css attributes for all the <section>s.
+        (window.sectionsManager.setupRunningSectionStyle): Sets the css attributes for the running <section> only.
+        
+        (window.optionsManager): Responsible of managing the user options and streaming them to/form the localStorage.
+        (window.optionsManager._optionsElements): Returns the children <input> elements of the <options>.
+        (window.optionsManager.updateUIFromLocalStorage): Restore the values of the <options> UI elements from the local storage.
+        (window.optionsManager.updateLocalStorageFromUI): Saves the values of the <options> UI elements to the local storage.
+        
+        (window.suitesManager): Responsible of managing the user suites and streaming them to/form the localStorage.
+        (window.suitesManager._treeElement): Returns the suites tree container element.
+        (window.suitesManager._suitesElements): Returns a list of the suites elements.
+        (window.suitesManager._checkboxElement): Returns the checkbox element of a given suite.
+        (window.suitesManager._localStorageNameForTest): Generates a string for the tuple <suite, test> to be saved in the localStorage.
+        (window.suitesManager._updateSuiteCheckboxState): Updates the state of a suite checkbox from the state of its tests' checkboxes.
+        (window.suitesManager._updateStartButtonState): Updates the state of the start button from the state of the suites' checkboxes.
+        (window.suitesManager._onChangeSuiteCheckbox): Called when a suite checkbox is clicked.
+        (window.suitesManager._onChangeTestCheckbox): Called when a test checkbox is clicked.
+        (window.suitesManager._createSuiteElement): Creates suite node in the suites tree.
+        (window.suitesManager._createTestElement): Creates test node in the suites tree.
+        (window.suitesManager.createElements): Creates the suites tree dynamically from the array Suites.
+        (window.suitesManager.updateUIFromLocalStorage): Restore the values of the <suites> UI elements from the local storage.
+        (window.suitesManager.updateLocalStorageFromUI): aves the values of the <suites> UI elements to the local storage.
+        
+        (window.benchmarkController): This is the UI controller of the animometer.html page.
+        (window.benchmarkController.initialize): Called when the animometer.html page is loaded.
+        (window.benchmarkController._runBenchmark): Starts a benchmark run.
+        (window.benchmarkController.startTest): Called when the "Start Test" button is clicked.
+        (window.benchmarkController.showResults): Called at the end of the test to show the final results.
+        (window.benchmarkController.showJson): Called from the results page to show the JSON of the last benchmark run.
+        (window.benchmarkController.showTestGraph): Called from the results the table to show a graph for the samples of a specific test.
+        (window.benchmarkController.showTestJSON): Called from the results the table to show a JSON for the samples of a specific test.
+        
+        (showSection): Deleted.
+        (startTest): Deleted.
+        (showResults): Deleted.
+        (showJson): Deleted.
+        (showTestGraph): Deleted.
+        (showTestJSON): Deleted.
+        (initialize.toggleTestsCheckbox.onchange): Deleted.
+        (initialize): Deleted.
+        (updateSuiteSelection): Deleted.
+        (updateTestSelection): Deleted.
+        (updateSuiteCheckbox): Deleted.
+        (localStorageNameForTest): Deleted.
+        (populateSettings.): Deleted.
+        (populateSettings): Deleted.
+        
+        * Animometer/runner/resources/benchmark-runner.js:
+        (BenchmarkRunner): Pass the frameContainer element to the BenchmarkRunner.
+        (BenchmarkRunner.prototype._appendFrame): Remove unused parameter unwanted styling code.
+        (BenchmarkRunner.prototype.runMultipleIterations):  Use the this._client.iterationCount instead of passing it as a parameter also.
+
+        * Animometer/runner/resources/graph.js:
+        (graph): Calculate the size of the chart from the container element.
+        
+        * Animometer/tests/bouncing-particles/resources/bouncing-svg-images.js:
+        (BouncingSvgImage):
+        * Animometer/tests/bouncing-particles/resources/bouncing-svg-particles.js:
+        (BouncingSvgParticlesStage.prototype._createDefs):
+        (BouncingSvgParticlesStage.prototype._createClipStar):        
+        * Animometer/tests/bouncing-particles/resources/bouncing-svg-shapes.js:
+        (BouncingSvgShape.prototype._createShape):
+        (BouncingSvgShapesStage.prototype.createGradient):
+        Call DocumentExtension.createSvgElement() instead of calling Utilities.createSvgElement().
+        
+        * Animometer/tests/resources/main.js:
+        (Animator.prototype.animate):
+        (Benchmark):
+        (Benchmark.prototype.update):
+        * Animometer/tests/resources/stage.js:
+        (StageBenchmark.prototype.showResults):
+        Rename the options to match the <input> ids in animometer.html.
+        
+        * Animometer/tests/resources/utilities.js:
+        (window.Utilities.createSvgElement): Deleted.
+        
+        * Animometer/tests/text/layering-text.html:
+        * Animometer/tests/text/resources/layering-text.js:
+        (LayeringTextStage):
+        (LayeringTextStage.prototype._setFontSize): Sets the size of the text dynamically such that they all fit in one stage.
+
 2015-10-29  Simon Fraser  <simon.fraser@apple.com>
 
         Animometer computes frame rate incorrectly