1 <!DOCTYPE html> 2 3 <html ng-app="Loader" ng-controller="Loader.Controller"> 4 5 <head> 6 <title ng-bind="windowTitle"></title> 7 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js"></script> 8 <script src="loader.js"></script> 9 <link rel="stylesheet" href="view.css"> 10 </head> 11 12 <body> 13 <h2> 14 Instructions, roadmap, etc. are at 15 <a href="http://tinyurl.com/SkiaRebaselineServer"> 16 http://tinyurl.com/SkiaRebaselineServer 17 </a> 18 </h2> 19 20 <em> 21 {{loadingMessage}} 22 </em> 23 24 <div ng-hide="!categories"><!-- everything: hide until data is loaded --> 25 26 <div class="warning-div" 27 ng-hide="!(header.isEditable && header.isExported)"> 28 WARNING! These results are editable and exported, so any user 29 who can connect to this server over the network can modify them. 30 </div> 31 32 <div ng-hide="!(header.timeUpdated)"> 33 Results current as of {{localTimeString(header.timeUpdated)}} 34 </div> 35 36 <div><!-- tabs --> 37 <div class="tab-spacer" ng-repeat="tab in tabs"> 38 <div class="tab-{{tab == viewingTab}}" 39 ng-click="setViewingTab(tab)"> 40 {{tab}} ({{numResultsPerTab[tab]}}) 41 </div> 42 <div class="tab-spacer"> 43 44 </div> 45 </div> 46 </div><!-- tabs --> 47 48 <div class="tab-main"><!-- main display area of selected tab --> 49 50 <br> 51 <!-- We only show the filters/settings table on the Unfiled tab. --> 52 <table ng-hide="viewingTab != defaultTab" border="1"> 53 <tr> 54 <th colspan="4"> 55 Filters 56 </th> 57 <th> 58 Settings 59 </th> 60 </tr> 61 <tr valign="top"> 62 <!-- TODO(epoger): make this an ng-repeat over resultType, config, etc? --> 63 <td> 64 resultType<br> 65 <label ng-repeat="(resultType, count) in categories['resultType'] track by $index"> 66 <input type="checkbox" 67 name="resultTypes" 68 value="{{resultType}}" 69 ng-checked="!isValueInSet(resultType, hiddenResultTypes)" 70 ng-click="toggleValueInSet(resultType, hiddenResultTypes); setUpdatesPending(true)"> 71 {{resultType}} ({{count}})<br> 72 </label> 73 <button ng-click="hiddenResultTypes = {}; updateResults()"> 74 all 75 </button> 76 <button ng-click="hiddenResultTypes = {}; toggleValuesInSet(allResultTypes, hiddenResultTypes); updateResults()"> 77 none 78 </button> 79 <button ng-click="toggleValuesInSet(allResultTypes, hiddenResultTypes); updateResults()"> 80 toggle 81 </button> 82 </td> 83 <td ng-repeat="category in ['builder', 'test']"> 84 {{category}} 85 <br> 86 <input type="text" 87 ng-model="categoryValueMatch[category]" 88 ng-change="setUpdatesPending(true)"/> 89 <br> 90 <button ng-click="setCategoryValueMatch(category, '')" 91 ng-disabled="('' == categoryValueMatch[category])"> 92 clear (show all) 93 </button> 94 </td> 95 <td> 96 config<br> 97 <label ng-repeat="(config, count) in categories['config'] track by $index"> 98 <input type="checkbox" 99 name="configs" 100 value="{{config}}" 101 ng-checked="!isValueInSet(config, hiddenConfigs)" 102 ng-click="toggleValueInSet(config, hiddenConfigs); setUpdatesPending(true)"> 103 {{config}} ({{count}})<br> 104 </label> 105 <button ng-click="hiddenConfigs = {}; updateResults()"> 106 all 107 </button> 108 <button ng-click="hiddenConfigs = {}; toggleValuesInSet(allConfigs, hiddenConfigs); updateResults()"> 109 none 110 </button> 111 <button ng-click="toggleValuesInSet(allConfigs, hiddenConfigs); updateResults()"> 112 toggle 113 </button> 114 </td> 115 <td><table> 116 <tr><td> 117 Image size 118 <input type="text" ng-model="imageSizePending" 119 ng-init="imageSizePending=100" 120 ng-change="areUpdatesPending = true" 121 maxlength="4"/> 122 </td></tr> 123 <tr><td> 124 Max records to display 125 <input type="text" ng-model="displayLimitPending" 126 ng-init="displayLimitPending=50" 127 ng-change="areUpdatesPending = true" 128 maxlength="4"/> 129 </td></tr> 130 <tr><td> 131 <button class="update-results-button" 132 ng-click="updateResults()" 133 ng-disabled="!areUpdatesPending"> 134 Update Results 135 </button> 136 </td></tr> 137 </tr></table></td> 138 </tr> 139 </table> 140 141 <p> 142 143 <!-- Submission UI that we only show in the Pending Approval tab. --> 144 <div ng-hide="'Pending Approval' != viewingTab"> 145 <div style="display:inline-block"> 146 <button style="font-size:20px" 147 ng-click="submitApprovals(filteredTestData)" 148 ng-disabled="submitPending || (filteredTestData.length == 0)"> 149 Update these {{filteredTestData.length}} expectations on the server 150 </button> 151 </div> 152 <div style="display:inline-block"> 153 <div style="font-size:20px" 154 ng-hide="!submitPending"> 155 Submitting, please wait... 156 </div> 157 </div> 158 <div> 159 Advanced settings... 160 <input type="checkbox" ng-model="showSubmitAdvancedSettings"> 161 show 162 <ul ng-hide="!showSubmitAdvancedSettings"> 163 <li ng-repeat="setting in ['reviewed-by-human', 'ignore-failure']"> 164 {{setting}} 165 <input type="checkbox" ng-model="submitAdvancedSettings[setting]"> 166 </li> 167 <li ng-repeat="setting in ['bug']"> 168 {{setting}} 169 <input type="text" ng-model="submitAdvancedSettings[setting]"> 170 </li> 171 </ul> 172 </div> 173 </div> 174 175 <p> 176 177 <table border="0"><tr><td> <!-- table holding results header + results table --> 178 <table border="0" width="100%"> <!-- results header --> 179 <tr> 180 <td> 181 Found {{filteredTestData.length}} matches; 182 <span ng-hide="filteredTestData.length <= limitedTestData.length"> 183 displaying the first {{limitedTestData.length}} 184 </span> 185 <span ng-hide="filteredTestData.length > limitedTestData.length"> 186 displaying them all 187 </span> 188 <br> 189 (click on the column header radio buttons to re-sort by that column) 190 </td> 191 <td align="right"> 192 <div> 193 all tests shown: 194 <button ng-click="selectAllItems()"> 195 select 196 </button> 197 <button ng-click="clearAllItems()"> 198 clear 199 </button> 200 <button ng-click="toggleAllItems()"> 201 toggle 202 </button> 203 </div> 204 <div ng-repeat="otherTab in tabs"> 205 <button ng-click="moveSelectedItemsToTab(otherTab)" 206 ng-disabled="selectedItems.length == 0" 207 ng-hide="otherTab == viewingTab"> 208 move {{selectedItems.length}} selected tests to {{otherTab}} tab 209 </button> 210 </div> 211 </td> 212 </tr> 213 </table> <!-- results header --> 214 </td></tr><tr><td> 215 <table border="1"> <!-- results --> 216 <tr> 217 <!-- Most column headers are displayed in a common fashion... --> 218 <th ng-repeat="categoryName in ['resultType', 'builder', 'test', 'config']"> 219 <input type="radio" 220 name="sortColumnRadio" 221 value="{{categoryName}}" 222 ng-checked="(sortColumn == categoryName)" 223 ng-click="sortResultsBy(categoryName)"> 224 {{categoryName}} 225 </th> 226 <!-- ... but there are a few columns where we display things differently. --> 227 <th> 228 <input type="radio" 229 name="sortColumnRadio" 230 value="bugs" 231 ng-checked="(sortColumn == 'bugs')" 232 ng-click="sortResultsBy('bugs')"> 233 bugs 234 </th> 235 <th width="{{imageSize}}"> 236 <input type="radio" 237 name="sortColumnRadio" 238 value="expectedHashDigest" 239 ng-checked="(sortColumn == 'expectedHashDigest')" 240 ng-click="sortResultsBy('expectedHashDigest')"> 241 expected image 242 </th> 243 <th width="{{imageSize}}"> 244 <input type="radio" 245 name="sortColumnRadio" 246 value="actualHashDigest" 247 ng-checked="(sortColumn == 'actualHashDigest')" 248 ng-click="sortResultsBy('actualHashDigest')"> 249 actual image 250 </th> 251 <th width="{{imageSize}}"> 252 <input type="radio" 253 name="sortColumnRadio" 254 value="percentDifferingPixels" 255 ng-checked="(sortColumn == 'percentDifferingPixels')" 256 ng-click="sortResultsBy('percentDifferingPixels')"> 257 differing pixels in white 258 </th> 259 <th width="{{imageSize}}"> 260 <input type="radio" 261 name="sortColumnRadio" 262 value="weightedDiffMeasure" 263 ng-checked="(sortColumn == 'weightedDiffMeasure')" 264 ng-click="sortResultsBy('weightedDiffMeasure')"> 265 difference per pixel 266 </th> 267 <th> 268 <!-- item-selection checkbox column --> 269 </th> 270 </tr> 271 272 <!-- For most columns... if the user clicks on the cell, and we are on 273 the default tab, update the filter to only show results with the 274 same value for this category. 275 This is made a bit tricky by the fact that AngularJS expressions 276 do not allow control flow statements. See 277 http://docs.angularjs.org/guide/expression --> 278 <tr ng-repeat="result in limitedTestData"> 279 <td ng-click="(viewingTab != defaultTab) || showOnlyResultType(result.resultType)"> 280 {{result.resultType}} 281 </td> 282 <td ng-repeat="categoryName in ['builder', 'test']" 283 ng-click="(viewingTab != defaultTab) || setCategoryValueMatch(categoryName, result[categoryName])"> 284 {{result[categoryName]}} 285 </td> 286 <td ng-click="(viewingTab != defaultTab) || showOnlyConfig(result.config)"> 287 {{result.config}} 288 </td> 289 <td> 290 <a ng-repeat="bug in result['bugs']" 291 href="https://code.google.com/p/skia/issues/detail?id={{bug}}" 292 target="_blank"> 293 {{bug}} 294 </a> 295 </td> 296 297 <!-- expected image --> 298 <td valign="top" width="{{imageSize}}"> 299 <a class="image-link" target="_blank" href="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.expectedHashType}}/{{result.test}}/{{result.expectedHashDigest}}.png"> 300 <img width="{{imageSize}}" src="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.expectedHashType}}/{{result.test}}/{{result.expectedHashDigest}}.png"/> 301 </a> 302 </td> 303 304 <!-- actual image --> 305 <td valign="top" width="{{imageSize}}"> 306 <a class="image-link" target="_blank" href="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.actualHashType}}/{{result.test}}/{{result.actualHashDigest}}.png"> 307 <img width="{{imageSize}}" src="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.actualHashType}}/{{result.test}}/{{result.actualHashDigest}}.png"/> 308 </a> 309 </td> 310 311 <!-- whitediffs: every differing pixel shown in white --> 312 <td valign="top" width="{{imageSize}}"> 313 <div ng-hide="result.expectedHashDigest == result.actualHashDigest" 314 title="{{result.numDifferingPixels | number:0}} of {{(100 * result.numDifferingPixels / result.percentDifferingPixels) | number:0}} pixels ({{result.percentDifferingPixels.toFixed(4)}}%) differ from expectation."> 315 <a class="image-link" target="_blank" href="/static/generated-images/whitediffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png"> 316 <img width="{{imageSize}}" src="/static/generated-images/whitediffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png"/> 317 </a><br> 318 {{result.percentDifferingPixels.toFixed(4)}}% 319 ({{result.numDifferingPixels}}) 320 </div> 321 <div ng-hide="result.expectedHashDigest != result.actualHashDigest" 322 style="text-align:center"> 323 –none– 324 </div> 325 </td> 326 327 <!-- diffs: per-channel RGB deltas --> 328 <td valign="top" width="{{imageSize}}"> 329 <div ng-hide="result.expectedHashDigest == result.actualHashDigest" 330 title="Weighted difference measure is {{result.weightedDiffMeasure.toFixed(4)}}%. Maximum difference per channel: R={{result.maxDiffPerChannel[0]}}, G={{result.maxDiffPerChannel[1]}}, B={{result.maxDiffPerChannel[2]}}"> 331 <a class="image-link" target="_blank" href="/static/generated-images/diffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png"> 332 <img width="{{imageSize}}" src="/static/generated-images/diffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png"/> 333 </a><br> 334 {{result.weightedDiffMeasure.toFixed(4)}}% 335 {{result.maxDiffPerChannel}} 336 </div> 337 <div ng-hide="result.expectedHashDigest != result.actualHashDigest" 338 style="text-align:center"> 339 –none– 340 </div> 341 </td> 342 343 <td> 344 <input type="checkbox" 345 name="rowSelect" 346 value="{{result.index}}" 347 ng-checked="isValueInArray(result.index, selectedItems)" 348 ng-click="toggleValueInArray(result.index, selectedItems)"> 349 </tr> 350 </table> <!-- results --> 351 </td></tr></table> <!-- table holding results header + results table --> 352 353 </div><!-- main display area of selected tab --> 354 </div><!-- everything: hide until data is loaded --> 355 356 <!-- TODO(epoger): Can we get the base URLs (commondatastorage and 357 issues list) from 358 http://skia.googlecode.com/svn/buildbot/site_config/global_variables.json 359 ? I tried importing the 360 http://skia.googlecode.com/svn/buildbot/skia_tools.js script and using 361 that to do so, but I got Access-Control-Allow-Origin errors. 362 --> 363 364 </body> 365 </html> 366