| <!DOCTYPE html> |
| |
| <html ng-app="Loader" ng-controller="Loader.Controller"> |
| |
| <head> |
| <title ng-bind="windowTitle"></title> |
| <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js"></script> |
| <script src="loader.js"></script> |
| <script src="diff_viewer.js"></script> |
| <link rel="stylesheet" href="view.css"> |
| </head> |
| |
| <body> |
| <h2> |
| Instructions, roadmap, etc. are at |
| <a href="http://tinyurl.com/SkiaRebaselineServer"> |
| http://tinyurl.com/SkiaRebaselineServer |
| </a> |
| </h2> |
| |
| <em> |
| {{loadingMessage}} |
| </em> |
| |
| <div ng-hide="!categories"><!-- everything: hide until data is loaded --> |
| |
| <div class="warning-div" |
| ng-hide="!(header.isEditable && header.isExported)"> |
| WARNING! These results are editable and exported, so any user |
| who can connect to this server over the network can modify them. |
| </div> |
| |
| <div ng-hide="!(header.timeUpdated)"> |
| Results current as of {{localTimeString(header.timeUpdated)}} |
| </div> |
| |
| <div><!-- tabs --> |
| <div class="tab-spacer" ng-repeat="tab in tabs"> |
| <div class="tab-{{tab == viewingTab}}" |
| ng-click="setViewingTab(tab)"> |
| {{tab}} ({{numResultsPerTab[tab]}}) |
| </div> |
| <div class="tab-spacer"> |
| |
| </div> |
| </div> |
| </div><!-- tabs --> |
| |
| <div class="tab-main"><!-- main display area of selected tab --> |
| |
| <br> |
| <!-- We only show the filters/settings table on the Unfiled tab. --> |
| <table ng-hide="viewingTab != defaultTab" border="1"> |
| <tr> |
| <th colspan="4"> |
| Filters |
| </th> |
| <th> |
| Settings |
| </th> |
| </tr> |
| <tr valign="top"> |
| <!-- TODO(epoger): make this an ng-repeat over resultType, config, etc? --> |
| <td> |
| resultType<br> |
| <label ng-repeat="(resultType, count) in categories['resultType'] track by $index"> |
| <input type="checkbox" |
| name="resultTypes" |
| value="{{resultType}}" |
| ng-checked="!isValueInSet(resultType, hiddenResultTypes)" |
| ng-click="toggleValueInSet(resultType, hiddenResultTypes); setUpdatesPending(true)"> |
| {{resultType}} ({{count}})<br> |
| </label> |
| <button ng-click="hiddenResultTypes = {}; updateResults()"> |
| all |
| </button> |
| <button ng-click="hiddenResultTypes = {}; toggleValuesInSet(allResultTypes, hiddenResultTypes); updateResults()"> |
| none |
| </button> |
| <button ng-click="toggleValuesInSet(allResultTypes, hiddenResultTypes); updateResults()"> |
| toggle |
| </button> |
| </td> |
| <td ng-repeat="category in ['builder', 'test']"> |
| {{category}} |
| <br> |
| <input type="text" |
| ng-model="categoryValueMatch[category]" |
| ng-change="setUpdatesPending(true)"/> |
| <br> |
| <button ng-click="setCategoryValueMatch(category, '')" |
| ng-disabled="('' == categoryValueMatch[category])"> |
| clear (show all) |
| </button> |
| </td> |
| <td> |
| config<br> |
| <label ng-repeat="(config, count) in categories['config'] track by $index"> |
| <input type="checkbox" |
| name="configs" |
| value="{{config}}" |
| ng-checked="!isValueInSet(config, hiddenConfigs)" |
| ng-click="toggleValueInSet(config, hiddenConfigs); setUpdatesPending(true)"> |
| {{config}} ({{count}})<br> |
| </label> |
| <button ng-click="hiddenConfigs = {}; updateResults()"> |
| all |
| </button> |
| <button ng-click="hiddenConfigs = {}; toggleValuesInSet(allConfigs, hiddenConfigs); updateResults()"> |
| none |
| </button> |
| <button ng-click="toggleValuesInSet(allConfigs, hiddenConfigs); updateResults()"> |
| toggle |
| </button> |
| </td> |
| <td><table> |
| <tr><td> |
| Image width |
| <input type="text" ng-model="imageSizePending" |
| ng-init="imageSizePending=100" |
| ng-change="areUpdatesPending = true" |
| maxlength="4"/> |
| </td></tr> |
| <tr><td> |
| Max records to display |
| <input type="text" ng-model="displayLimitPending" |
| ng-init="displayLimitPending=50" |
| ng-change="areUpdatesPending = true" |
| maxlength="4"/> |
| </td></tr> |
| <tr><td> |
| <button class="update-results-button" |
| ng-click="updateResults()" |
| ng-disabled="!areUpdatesPending"> |
| Update Results |
| </button> |
| </td></tr> |
| </tr></table></td> |
| </tr> |
| </table> |
| |
| <p> |
| |
| <!-- Submission UI that we only show in the Pending Approval tab. --> |
| <div ng-hide="'Pending Approval' != viewingTab"> |
| <div style="display:inline-block"> |
| <button style="font-size:20px" |
| ng-click="submitApprovals(filteredTestData)" |
| ng-disabled="submitPending || (filteredTestData.length == 0)"> |
| Update these {{filteredTestData.length}} expectations on the server |
| </button> |
| </div> |
| <div style="display:inline-block"> |
| <div style="font-size:20px" |
| ng-hide="!submitPending"> |
| Submitting, please wait... |
| </div> |
| </div> |
| <div> |
| Advanced settings... |
| <input type="checkbox" ng-model="showSubmitAdvancedSettings"> |
| show |
| <ul ng-hide="!showSubmitAdvancedSettings"> |
| <li ng-repeat="setting in ['reviewed-by-human', 'ignore-failure']"> |
| {{setting}} |
| <input type="checkbox" ng-model="submitAdvancedSettings[setting]"> |
| </li> |
| <li ng-repeat="setting in ['bug']"> |
| {{setting}} |
| <input type="text" ng-model="submitAdvancedSettings[setting]"> |
| </li> |
| </ul> |
| </div> |
| </div> |
| |
| <p> |
| |
| <table border="0"><tr><td> <!-- table holding results header + results table --> |
| <table border="0" width="100%"> <!-- results header --> |
| <tr> |
| <td> |
| Found {{filteredTestData.length}} matches; |
| <span ng-hide="filteredTestData.length <= limitedTestData.length"> |
| displaying the first {{limitedTestData.length}} |
| </span> |
| <span ng-hide="filteredTestData.length > limitedTestData.length"> |
| displaying them all |
| </span> |
| <br> |
| (click on the column header radio buttons to re-sort by that column) |
| </td> |
| <td align="right"> |
| <div> |
| all tests shown: |
| <button ng-click="selectAllItems()"> |
| select |
| </button> |
| <button ng-click="clearAllItems()"> |
| clear |
| </button> |
| <button ng-click="toggleAllItems()"> |
| toggle |
| </button> |
| </div> |
| <div ng-repeat="otherTab in tabs"> |
| <button ng-click="moveSelectedItemsToTab(otherTab)" |
| ng-disabled="selectedItems.length == 0" |
| ng-hide="otherTab == viewingTab"> |
| move {{selectedItems.length}} selected tests to {{otherTab}} tab |
| </button> |
| </div> |
| </td> |
| </tr> |
| </table> <!-- results header --> |
| </td></tr><tr><td> |
| <table border="1" ng-app="diff_viewer"> <!-- results --> |
| <tr> |
| <!-- Most column headers are displayed in a common fashion... --> |
| <th ng-repeat="categoryName in ['resultType', 'builder', 'test', 'config']"> |
| <input type="radio" |
| name="sortColumnRadio" |
| value="{{categoryName}}" |
| ng-checked="(sortColumn == categoryName)" |
| ng-click="sortResultsBy(categoryName)"> |
| {{categoryName}} |
| </th> |
| <!-- ... but there are a few columns where we display things differently. --> |
| <th> |
| <input type="radio" |
| name="sortColumnRadio" |
| value="bugs" |
| ng-checked="(sortColumn == 'bugs')" |
| ng-click="sortResultsBy('bugs')"> |
| bugs |
| </th> |
| <th width="{{imageSize}}"> |
| <input type="radio" |
| name="sortColumnRadio" |
| value="expectedHashDigest" |
| ng-checked="(sortColumn == 'expectedHashDigest')" |
| ng-click="sortResultsBy('expectedHashDigest')"> |
| expected image |
| </th> |
| <th width="{{imageSize}}"> |
| <input type="radio" |
| name="sortColumnRadio" |
| value="actualHashDigest" |
| ng-checked="(sortColumn == 'actualHashDigest')" |
| ng-click="sortResultsBy('actualHashDigest')"> |
| actual image |
| </th> |
| <th width="{{imageSize}}"> |
| <input type="radio" |
| name="sortColumnRadio" |
| value="percentDifferingPixels" |
| ng-checked="(sortColumn == 'percentDifferingPixels')" |
| ng-click="sortResultsBy('percentDifferingPixels')"> |
| differing pixels in white |
| </th> |
| <th width="{{imageSize}}"> |
| <input type="radio" |
| name="sortColumnRadio" |
| value="weightedDiffMeasure" |
| ng-checked="(sortColumn == 'weightedDiffMeasure')" |
| ng-click="sortResultsBy('weightedDiffMeasure')"> |
| perceptual difference |
| <br> |
| <input type="range" ng-model="pixelDiffBgColorBrightness" |
| ng-init="pixelDiffBgColorBrightness=64; pixelDiffBgColor=brightnessStringToHexColor(pixelDiffBgColorBrightness)" |
| ng-change="pixelDiffBgColor=brightnessStringToHexColor(pixelDiffBgColorBrightness)" |
| title="image background brightness" |
| min="0" max="255"/> |
| </th> |
| <th> |
| <!-- item-selection checkbox column --> |
| </th> |
| </tr> |
| |
| <tr ng-repeat="result in limitedTestData" ng-controller="ImageController"> |
| <td> |
| {{result.resultType}} |
| <br> |
| <button class="show-only-button" |
| ng-hide="viewingTab != defaultTab" |
| ng-click="showOnlyResultType(result.resultType)" |
| title="show only results of type '{{result.resultType}}'"> |
| show only |
| </button> |
| <br> |
| <button class="show-all-button" |
| ng-hide="viewingTab != defaultTab" |
| ng-disabled="0 == setSize(hiddenResultTypes)" |
| ng-click="showAllResultTypes()" |
| title="show results of all types"> |
| show all |
| </button> |
| </td> |
| <td ng-repeat="categoryName in ['builder', 'test']"> |
| {{result[categoryName]}} |
| <br> |
| <button class="show-only-button" |
| ng-hide="viewingTab != defaultTab" |
| ng-disabled="result[categoryName] == categoryValueMatch[categoryName]" |
| ng-click="setCategoryValueMatch(categoryName, result[categoryName])" |
| title="show only results of {{categoryName}} '{{result[categoryName]}}'"> |
| show only |
| </button> |
| <br> |
| <button class="show-all-button" |
| ng-hide="viewingTab != defaultTab" |
| ng-disabled="'' == categoryValueMatch[categoryName]" |
| ng-click="setCategoryValueMatch(categoryName, '')" |
| title="show results of all {{categoryName}}s"> |
| show all |
| </button> |
| </td> |
| <td> |
| {{result.config}} |
| <br> |
| <button class="show-only-button" |
| ng-hide="viewingTab != defaultTab" |
| ng-click="showOnlyConfig(result.config)" |
| title="show only results of config '{{result.config}}'"> |
| show only |
| </button> |
| <br> |
| <button class="show-all-button" |
| ng-hide="viewingTab != defaultTab" |
| ng-disabled="0 == setSize(hiddenConfigs)" |
| ng-click="showAllConfigs()" |
| title="show results of all configs"> |
| show all |
| </button> |
| </td> |
| <td> |
| <a ng-repeat="bug in result['bugs']" |
| href="https://code.google.com/p/skia/issues/detail?id={{bug}}" |
| target="_blank"> |
| {{bug}} |
| </a> |
| </td> |
| |
| <!-- expected image --> |
| <td valign="bottom" width="{{imageSize}}"> |
| <a href="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.expectedHashType}}/{{result.test}}/{{result.expectedHashDigest}}.png" target="_blank">View Image</a><br/> |
| <img-compare type="baseline" width="{{imageSize}}" |
| src="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.expectedHashType}}/{{result.test}}/{{result.expectedHashDigest}}.png" /> |
| |
| </td> |
| |
| <!-- actual image --> |
| <td valign="bottom" width="{{imageSize}}"> |
| <a href="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.actualHashType}}/{{result.test}}/{{result.actualHashDigest}}.png" target="_blank">View Image</a><br/> |
| <img-compare type="test" width="{{imageSize}}" |
| src="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.actualHashType}}/{{result.test}}/{{result.actualHashDigest}}.png" /> |
| |
| </td> |
| |
| <!-- whitediffs: every differing pixel shown in white --> |
| <td valign="bottom" width="{{imageSize}}"> |
| <div ng-hide="result.expectedHashDigest == result.actualHashDigest" |
| title="{{result.numDifferingPixels | number:0}} of {{(100 * result.numDifferingPixels / result.percentDifferingPixels) | number:0}} pixels ({{result.percentDifferingPixels.toFixed(4)}}%) differ from expectation."> |
| |
| {{result.percentDifferingPixels.toFixed(4)}}% |
| ({{result.numDifferingPixels}}) |
| <br/> |
| <a href="/static/generated-images/whitediffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png" target="_blank">View Image</a><br/> |
| <img-compare type="differingPixelsInWhite" width="{{imageSize}}" |
| src="/static/generated-images/whitediffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png" /> |
| |
| </div> |
| <div ng-hide="result.expectedHashDigest != result.actualHashDigest" |
| style="text-align:center"> |
| –none– |
| </div> |
| </td> |
| |
| <!-- diffs: per-channel RGB deltas --> |
| <td valign="bottom" width="{{imageSize}}"> |
| <div ng-hide="result.expectedHashDigest == result.actualHashDigest" |
| title="Perceptual difference measure is {{result.perceptualDifference.toFixed(4)}}%. Maximum difference per channel: R={{result.maxDiffPerChannel[0]}}, G={{result.maxDiffPerChannel[1]}}, B={{result.maxDiffPerChannel[2]}}"> |
| |
| {{result.perceptualDifference.toFixed(4)}}% |
| {{result.maxDiffPerChannel}} |
| <br/> |
| <a href="/static/generated-images/diffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png" target="_blank">View Image</a><br/> |
| <img-compare ng-style="{backgroundColor: pixelDiffBgColor}" |
| type="differencePerPixel" width="{{imageSize}}" |
| src="/static/generated-images/diffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png" |
| ng-mousedown="MagnifyDraw($event, true)" |
| ng-mousemove="MagnifyDraw($event, false)" |
| ng-mouseup="MagnifyEnd($event)" |
| ng-mouseleave="MagnifyEnd($event)" /> |
| |
| </div> |
| <div ng-hide="result.expectedHashDigest != result.actualHashDigest" |
| style="text-align:center"> |
| –none– |
| </div> |
| </td> |
| |
| <td> |
| <input type="checkbox" |
| name="rowSelect" |
| value="{{result.index}}" |
| ng-checked="isValueInArray(result.index, selectedItems)" |
| ng-click="toggleValueInArray(result.index, selectedItems)"> |
| </tr> |
| </table> <!-- results --> |
| </td></tr></table> <!-- table holding results header + results table --> |
| |
| </div><!-- main display area of selected tab --> |
| </div><!-- everything: hide until data is loaded --> |
| |
| <!-- TODO(epoger): Can we get the base URLs (commondatastorage and |
| issues list) from |
| https://skia.googlesource.com/buildbot/+/master/site_config/global_variables.json ? |
| I tried importing the |
| http://skia.googlecode.com/svn/buildbot/skia_tools.js script and using |
| that to do so, but I got Access-Control-Allow-Origin errors. |
| --> |
| |
| </body> |
| </html> |