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="constants.js"></script> 9 <script src="loader.js"></script> 10 <link rel="stylesheet" href="view.css"> 11 </head> 12 13 <body> 14 <h2> 15 Instructions, roadmap, etc. are at 16 <a href="http://tinyurl.com/SkiaRebaselineServer"> 17 http://tinyurl.com/SkiaRebaselineServer 18 </a> 19 </h2> 20 21 <em ng-show="!extraColumnHeaders"><!-- show until data is loaded --> 22 Loading results from <a href="{{resultsToLoad}}">{{resultsToLoad}}</a> ... 23 {{loadingMessage}} 24 </em> 25 26 <div ng-show="extraColumnHeaders"><!-- everything: hide until data is loaded --> 27 28 <div class="warning-div" 29 ng-show="header[constants.KEY__HEADER__IS_EDITABLE] && header[constants.KEY__HEADER__IS_EXPORTED]"> 30 WARNING! These results are editable and exported, so any user 31 who can connect to this server over the network can modify them. 32 </div> 33 34 <div ng-show="header[constants.KEY__HEADER__TIME_UPDATED]"> 35 These results, from raw JSON file 36 <a href="{{resultsToLoad}}">{{resultsToLoad}}</a>, current as of 37 {{localTimeString(header[constants.KEY__HEADER__TIME_UPDATED])}} 38 <br> 39 To see other sets of results (all results, failures only, etc.), 40 <a href="/">click here</a> 41 </div> 42 43 <div class="tab-wrapper"><!-- tabs --> 44 <div class="tab-spacer" ng-repeat="tab in tabs"> 45 <div class="tab tab-{{tab == viewingTab}}" 46 ng-click="setViewingTab(tab)"> 47 {{tab}} ({{numResultsPerTab[tab]}}) 48 </div> 49 <div class="tab-spacer"> 50 51 </div> 52 </div> 53 </div><!-- tabs --> 54 55 <div class="tab-main"><!-- main display area of selected tab --> 56 57 <br> 58 <!-- We only show the filters/settings table on the Unfiled tab. --> 59 <table ng-show="viewingTab == defaultTab" border="1"> 60 <tr> 61 <th colspan="4"> 62 Filters 63 </th> 64 <th> 65 Settings 66 </th> 67 </tr> 68 <tr valign="top"> 69 <td> 70 resultType<br> 71 <label ng-repeat="valueAndCount in extraColumnHeaders[constants.KEY__EXTRACOLUMNS__RESULT_TYPE][constants.KEY__EXTRACOLUMNHEADERS__VALUES_AND_COUNTS]"> 72 <input type="checkbox" 73 name="resultTypes" 74 value="{{valueAndCount[0]}}" 75 ng-checked="!isValueInSet(valueAndCount[0], hiddenResultTypes)" 76 ng-click="toggleValueInSet(valueAndCount[0], hiddenResultTypes); setUpdatesPending(true)"> 77 {{valueAndCount[0]}} ({{valueAndCount[1]}})<br> 78 </label> 79 <button ng-click="hiddenResultTypes = {}; updateResults()"> 80 all 81 </button> 82 <button ng-click="hiddenResultTypes = {}; toggleValuesInSet(allResultTypes, hiddenResultTypes); updateResults()"> 83 none 84 </button> 85 <button ng-click="toggleValuesInSet(allResultTypes, hiddenResultTypes); updateResults()"> 86 toggle 87 </button> 88 </td> 89 <td ng-repeat="category in [constants.KEY__EXTRACOLUMNS__BUILDER, constants.KEY__EXTRACOLUMNS__TEST]"> 90 {{category}} 91 <br> 92 <input type="text" 93 ng-model="categoryValueMatch[category]" 94 ng-change="setUpdatesPending(true)"/> 95 <br> 96 <button ng-click="setCategoryValueMatch(category, '')" 97 ng-disabled="('' == categoryValueMatch[category])"> 98 clear (show all) 99 </button> 100 </td> 101 <td> 102 config<br> 103 <label ng-repeat="valueAndCount in extraColumnHeaders[constants.KEY__EXTRACOLUMNS__CONFIG][constants.KEY__EXTRACOLUMNHEADERS__VALUES_AND_COUNTS]"> 104 <input type="checkbox" 105 name="configs" 106 value="{{valueAndCount[0]}}" 107 ng-checked="!isValueInSet(valueAndCount[0], hiddenConfigs)" 108 ng-click="toggleValueInSet(valueAndCount[0], hiddenConfigs); setUpdatesPending(true)"> 109 {{valueAndCount[0]}} ({{valueAndCount[1]}})<br> 110 </label> 111 <button ng-click="hiddenConfigs = {}; updateResults()"> 112 all 113 </button> 114 <button ng-click="hiddenConfigs = {}; toggleValuesInSet(allConfigs, hiddenConfigs); updateResults()"> 115 none 116 </button> 117 <button ng-click="toggleValuesInSet(allConfigs, hiddenConfigs); updateResults()"> 118 toggle 119 </button> 120 </td> 121 <td><table> 122 <tr><td> 123 <input type="checkbox" ng-model="showThumbnailsPending" 124 ng-init="showThumbnailsPending = true" 125 ng-change="areUpdatesPending = true"/> 126 Show thumbnails 127 </td></tr> 128 <tr><td> 129 <input type="checkbox" ng-model="mergeIdenticalRowsPending" 130 ng-init="mergeIdenticalRowsPending = true" 131 ng-change="areUpdatesPending = true"/> 132 Merge identical rows 133 </td></tr> 134 <tr><td> 135 Image width 136 <input type="text" ng-model="imageSizePending" 137 ng-init="imageSizePending=100" 138 ng-change="areUpdatesPending = true" 139 maxlength="4"/> 140 </td></tr> 141 <tr><td> 142 Max records to display 143 <input type="text" ng-model="displayLimitPending" 144 ng-init="displayLimitPending=50" 145 ng-change="areUpdatesPending = true" 146 maxlength="4"/> 147 </td></tr> 148 <tr><td> 149 <button class="update-results-button" 150 ng-click="updateResults()" 151 ng-disabled="!areUpdatesPending"> 152 Update Results 153 </button> 154 </td></tr> 155 </tr></table></td> 156 </tr> 157 </table> 158 159 <p> 160 161 <!-- Submission UI that we only show in the Pending Approval tab. --> 162 <div ng-show="'Pending Approval' == viewingTab"> 163 <div style="display:inline-block"> 164 <button style="font-size:20px" 165 ng-click="submitApprovals(filteredImagePairs)" 166 ng-disabled="submitPending || (filteredImagePairs.length == 0)"> 167 Update these {{filteredImagePairs.length}} expectations on the server 168 </button> 169 </div> 170 <div style="display:inline-block"> 171 <div style="font-size:20px" 172 ng-show="submitPending"> 173 Submitting, please wait... 174 </div> 175 </div> 176 <div> 177 Advanced settings... 178 <input type="checkbox" ng-model="showSubmitAdvancedSettings"> 179 show 180 <ul ng-show="showSubmitAdvancedSettings"> 181 <li ng-repeat="setting in [constants.KEY__EXPECTATIONS__REVIEWED, constants.KEY__EXPECTATIONS__IGNOREFAILURE]"> 182 {{setting}} 183 <input type="checkbox" ng-model="submitAdvancedSettings[setting]"> 184 </li> 185 <li ng-repeat="setting in ['bug']"> 186 {{setting}} 187 <input type="text" ng-model="submitAdvancedSettings[setting]"> 188 </li> 189 </ul> 190 </div> 191 </div> 192 193 <p> 194 195 <table border="0"><tr><td> <!-- table holding results header + results table --> 196 <table border="0" width="100%"> <!-- results header --> 197 <tr> 198 <td> 199 Found {{filteredImagePairs.length}} matches; 200 <span ng-show="filteredImagePairs.length > limitedImagePairs.length"> 201 displaying the first {{limitedImagePairs.length}}. 202 </span> 203 <span ng-show="filteredImagePairs.length <= limitedImagePairs.length"> 204 displaying them all. 205 </span> 206 <span ng-show="renderEndTime > renderStartTime"> 207 Rendered in {{(renderEndTime - renderStartTime).toFixed(0)}} ms. 208 </span> 209 <br> 210 (click on the column header radio buttons to re-sort by that column) 211 </td> 212 <td align="right"> 213 <div> 214 all tests shown: 215 <button ng-click="selectAllImagePairs()"> 216 select 217 </button> 218 <button ng-click="clearAllImagePairs()"> 219 clear 220 </button> 221 <button ng-click="toggleAllImagePairs()"> 222 toggle 223 </button> 224 </div> 225 <div ng-repeat="otherTab in tabs"> 226 <button ng-click="moveSelectedImagePairsToTab(otherTab)" 227 ng-disabled="selectedImagePairs.length == 0" 228 ng-show="otherTab != viewingTab"> 229 move {{selectedImagePairs.length}} selected tests to {{otherTab}} tab 230 </button> 231 </div> 232 </td> 233 </tr> 234 </table> <!-- results header --> 235 </td></tr><tr><td> 236 <table border="1" ng-app="diff_viewer"> <!-- results --> 237 <tr> 238 <!-- Most column headers are displayed in a common fashion... --> 239 <th ng-repeat="categoryName in [constants.KEY__EXTRACOLUMNS__RESULT_TYPE, constants.KEY__EXTRACOLUMNS__BUILDER, constants.KEY__EXTRACOLUMNS__TEST, constants.KEY__EXTRACOLUMNS__CONFIG]"> 240 <input type="radio" 241 name="sortColumnRadio" 242 value="{{categoryName}}" 243 ng-checked="(sortColumnKey == categoryName)" 244 ng-click="sortResultsBy(constants.KEY__IMAGEPAIRS__EXTRACOLUMNS, categoryName)"> 245 {{categoryName}} 246 </th> 247 <!-- ... but there are a few columns where we display things differently. --> 248 <th> 249 <input type="radio" 250 name="sortColumnRadio" 251 value="bugs" 252 ng-checked="(sortColumnKey == constants.KEY__EXPECTATIONS__BUGS)" 253 ng-click="sortResultsBy(constants.KEY__IMAGEPAIRS__EXPECTATIONS, constants.KEY__EXPECTATIONS__BUGS)"> 254 bugs 255 </th> 256 <th width="{{imageSize}}"> 257 <input type="radio" 258 name="sortColumnRadio" 259 value="imageA" 260 ng-checked="(sortColumnKey == constants.KEY__IMAGEPAIRS__IMAGE_A_URL)" 261 ng-click="sortResultsBy('none', constants.KEY__IMAGEPAIRS__IMAGE_A_URL)"> 262 {{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_A][constants.KEY__IMAGESETS__FIELD__DESCRIPTION]}} 263 </th> 264 <th width="{{imageSize}}"> 265 <input type="radio" 266 name="sortColumnRadio" 267 value="imageB" 268 ng-checked="(sortColumnKey == constants.KEY__IMAGEPAIRS__IMAGE_B_URL)" 269 ng-click="sortResultsBy('none', constants.KEY__IMAGEPAIRS__IMAGE_B_URL)"> 270 {{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_B][constants.KEY__IMAGESETS__FIELD__DESCRIPTION]}} 271 </th> 272 <th width="{{imageSize}}"> 273 <input type="radio" 274 name="sortColumnRadio" 275 value="percentDifferingPixels" 276 ng-checked="(sortColumnKey == constants.KEY__DIFFERENCES__PERCENT_DIFF_PIXELS)" 277 ng-click="sortResultsBy(constants.KEY__IMAGEPAIRS__DIFFERENCES, constants.KEY__DIFFERENCES__PERCENT_DIFF_PIXELS)"> 278 differing pixels in white 279 </th> 280 <th width="{{imageSize}}"> 281 <input type="radio" 282 name="sortColumnRadio" 283 value="perceptualDiff" 284 ng-checked="(sortColumnKey == constants.KEY__DIFFERENCES__PERCEPTUAL_DIFF)" 285 ng-click="sortResultsBy(constants.KEY__IMAGEPAIRS__DIFFERENCES, constants.KEY__DIFFERENCES__PERCEPTUAL_DIFF)"> 286 perceptual difference 287 <br> 288 <input type="range" ng-model="pixelDiffBgColorBrightness" 289 ng-init="pixelDiffBgColorBrightness=64; pixelDiffBgColor=brightnessStringToHexColor(pixelDiffBgColorBrightness)" 290 ng-change="pixelDiffBgColor=brightnessStringToHexColor(pixelDiffBgColorBrightness)" 291 title="image background brightness" 292 min="0" max="255"/> 293 </th> 294 <th> 295 <!-- imagepair-selection checkbox column --> 296 </th> 297 </tr> 298 299 <tr ng-repeat="imagePair in limitedImagePairs" valign="top" 300 ng-class-odd="'results-odd'" ng-class-even="'results-even'" 301 results-updated-callback-directive> 302 <td> 303 {{imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][constants.KEY__EXTRACOLUMNS__RESULT_TYPE]}} 304 <br> 305 <button class="show-only-button" 306 ng-show="viewingTab == defaultTab" 307 ng-click="showOnlyResultType(imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][constants.KEY__EXTRACOLUMNS__RESULT_TYPE])" 308 title="show only results of type {{imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][constants.KEY__EXTRACOLUMNS__RESULT_TYPE]}}"> 309 show only 310 </button> 311 <br> 312 <button class="show-all-button" 313 ng-show="viewingTab == defaultTab" 314 ng-disabled="0 == setSize(hiddenResultTypes)" 315 ng-click="showAllResultTypes()" 316 title="show results of all types"> 317 show all 318 </button> 319 </td> 320 <td ng-repeat="categoryName in [constants.KEY__EXTRACOLUMNS__BUILDER, constants.KEY__EXTRACOLUMNS__TEST]"> 321 {{imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][categoryName]}} 322 <br> 323 <button class="show-only-button" 324 ng-show="viewingTab == defaultTab" 325 ng-disabled="imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][categoryName] == categoryValueMatch[categoryName]" 326 ng-click="setCategoryValueMatch(categoryName, imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][categoryName])" 327 title="show only results of {{categoryName}} {{imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][categoryName]}}"> 328 show only 329 </button> 330 <br> 331 <button class="show-all-button" 332 ng-show="viewingTab == defaultTab" 333 ng-disabled="'' == categoryValueMatch[categoryName]" 334 ng-click="setCategoryValueMatch(categoryName, '')" 335 title="show results of all {{categoryName}}s"> 336 show all 337 </button> 338 </td> 339 <td> 340 {{imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][constants.KEY__EXTRACOLUMNS__CONFIG]}} 341 <br> 342 <button class="show-only-button" 343 ng-show="viewingTab == defaultTab" 344 ng-click="showOnlyConfig(imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][constants.KEY__EXTRACOLUMNS__CONFIG])" 345 title="show only results of config {{imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][constants.KEY__EXTRACOLUMNS__CONFIG]}}"> 346 show only 347 </button> 348 <br> 349 <button class="show-all-button" 350 ng-show="viewingTab == defaultTab" 351 ng-disabled="0 == setSize(hiddenConfigs)" 352 ng-click="showAllConfigs()" 353 title="show results of all configs"> 354 show all 355 </button> 356 </td> 357 <td> 358 <a ng-repeat="bug in imagePair[constants.KEY__IMAGEPAIRS__EXPECTATIONS][constants.KEY__EXPECTATIONS__BUGS]" 359 href="https://code.google.com/p/skia/issues/detail?id={{bug}}" 360 target="_blank"> 361 {{bug}} 362 </a> 363 </td> 364 365 <!-- image A --> 366 <td width="{{imageSize}}" ng-if="imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] > 0" rowspan="{{imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN]}}"> 367 <div ng-if="imagePair[constants.KEY__IMAGEPAIRS__IMAGE_A_URL] != null"> 368 <a href="{{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_A][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{imagePair[constants.KEY__IMAGEPAIRS__IMAGE_A_URL]}}" target="_blank">View Image</a><br/> 369 <img ng-if="showThumbnails" 370 width="{{imageSize}}" 371 ng-src="{{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_A][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{imagePair[constants.KEY__IMAGEPAIRS__IMAGE_A_URL]}}" /> 372 </div> 373 <div ng-show="imagePair[constants.KEY__IMAGEPAIRS__IMAGE_A_URL] == null" 374 style="text-align:center"> 375 –none– 376 </div> 377 </td> 378 379 <!-- image B --> 380 <td width="{{imageSize}}" ng-if="imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] > 0" rowspan="{{imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN]}}"> 381 <div ng-if="imagePair[constants.KEY__IMAGEPAIRS__IMAGE_B_URL] != null"> 382 <a href="{{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_B][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{imagePair[constants.KEY__IMAGEPAIRS__IMAGE_B_URL]}}" target="_blank">View Image</a><br/> 383 <img ng-if="showThumbnails" 384 width="{{imageSize}}" 385 ng-src="{{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_B][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{imagePair[constants.KEY__IMAGEPAIRS__IMAGE_B_URL]}}" /> 386 </div> 387 <div ng-show="imagePair[constants.KEY__IMAGEPAIRS__IMAGE_B_URL] == null" 388 style="text-align:center"> 389 –none– 390 </div> 391 </td> 392 393 <!-- whitediffs: every differing pixel shown in white --> 394 <td width="{{imageSize}}" ng-if="imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] > 0" rowspan="{{imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN]}}"> 395 <div ng-if="imagePair[constants.KEY__IMAGEPAIRS__IS_DIFFERENT]" 396 title="{{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__NUM_DIFF_PIXELS] | number:0}} of {{(100 * imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__NUM_DIFF_PIXELS] / imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__PERCENT_DIFF_PIXELS]) | number:0}} pixels ({{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__PERCENT_DIFF_PIXELS].toFixed(4)}}%) differ from expectation."> 397 398 <a href="{{imageSets[constants.KEY__IMAGESETS__SET__WHITEDIFFS][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{getImageDiffRelativeUrl(imagePair)}}" target="_blank">View Image</a><br/> 399 <img ng-if="showThumbnails" 400 width="{{imageSize}}" 401 ng-src="{{imageSets[constants.KEY__IMAGESETS__SET__WHITEDIFFS][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{getImageDiffRelativeUrl(imagePair)}}" /> 402 <br/> 403 {{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__PERCENT_DIFF_PIXELS].toFixed(4)}}% 404 ({{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__NUM_DIFF_PIXELS]}}) 405 </div> 406 <div ng-show="!imagePair[constants.KEY__IMAGEPAIRS__IS_DIFFERENT]" 407 style="text-align:center"> 408 –none– 409 </div> 410 </td> 411 412 <!-- diffs: per-channel RGB deltas --> 413 <td width="{{imageSize}}" ng-if="imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] > 0" rowspan="{{imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN]}}"> 414 <div ng-if="imagePair[constants.KEY__IMAGEPAIRS__IS_DIFFERENT]" 415 title="Perceptual difference measure is {{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__PERCEPTUAL_DIFF].toFixed(4)}}%. Maximum difference per channel: R={{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__MAX_DIFF_PER_CHANNEL][0]}}, G={{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__MAX_DIFF_PER_CHANNEL][1]}}, B={{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__MAX_DIFF_PER_CHANNEL][2]}}"> 416 417 <a href="{{imageSets[constants.KEY__IMAGESETS__SET__DIFFS][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{getImageDiffRelativeUrl(imagePair)}}" target="_blank">View Image</a><br/> 418 <img ng-if="showThumbnails" 419 ng-style="{backgroundColor: pixelDiffBgColor}" 420 width="{{imageSize}}" 421 ng-src="{{imageSets[constants.KEY__IMAGESETS__SET__DIFFS][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{getImageDiffRelativeUrl(imagePair)}}" /> 422 <br/> 423 {{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__PERCEPTUAL_DIFF].toFixed(4)}}% 424 {{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__MAX_DIFF_PER_CHANNEL]}} 425 </div> 426 <div ng-show="!imagePair[constants.KEY__IMAGEPAIRS__IS_DIFFERENT]" 427 style="text-align:center"> 428 –none– 429 </div> 430 </td> 431 432 <td ng-if="imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] > 0" rowspan="{{imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN]}}"> 433 <br/> 434 <input type="checkbox" 435 name="rowSelect" 436 value="{{imagePair.index}}" 437 ng-checked="isValueInArray(imagePair.index, selectedImagePairs)" 438 ng-click="toggleSomeImagePairs($index, imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN])"> 439 </tr> 440 </table> <!-- imagePairs --> 441 </td></tr></table> <!-- table holding results header + imagePairs table --> 442 443 </div><!-- main display area of selected tab --> 444 </div><!-- everything: hide until data is loaded --> 445 446 </body> 447 </html> 448