<!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="constants.js"></script> <script src="loader.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 ng-show="!extraColumnHeaders"><!-- show until data is loaded --> Loading results from <a href="{{resultsToLoad}}">{{resultsToLoad}}</a> ... {{loadingMessage}} </em> <div ng-show="extraColumnHeaders"><!-- everything: hide until data is loaded --> <div class="warning-div" ng-show="header[constants.KEY__HEADER__IS_EDITABLE] && header[constants.KEY__HEADER__IS_EXPORTED]"> 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-show="header[constants.KEY__HEADER__TIME_UPDATED]"> These results, from raw JSON file <a href="{{resultsToLoad}}">{{resultsToLoad}}</a>, current as of {{localTimeString(header[constants.KEY__HEADER__TIME_UPDATED])}} <br> To see other sets of results (all results, failures only, etc.), <a href="/">click here</a> </div> <div class="tab-wrapper"><!-- tabs --> <div class="tab-spacer" ng-repeat="tab in tabs"> <div class="tab 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-show="viewingTab == defaultTab" border="1"> <tr> <th colspan="4"> Filters </th> <th> Settings </th> </tr> <tr valign="top"> <td> resultType<br> <label ng-repeat="valueAndCount in extraColumnHeaders[constants.KEY__EXTRACOLUMNS__RESULT_TYPE][constants.KEY__EXTRACOLUMNHEADERS__VALUES_AND_COUNTS]"> <input type="checkbox" name="resultTypes" value="{{valueAndCount[0]}}" ng-checked="!isValueInSet(valueAndCount[0], hiddenResultTypes)" ng-click="toggleValueInSet(valueAndCount[0], hiddenResultTypes); setUpdatesPending(true)"> {{valueAndCount[0]}} ({{valueAndCount[1]}})<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 [constants.KEY__EXTRACOLUMNS__BUILDER, constants.KEY__EXTRACOLUMNS__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="valueAndCount in extraColumnHeaders[constants.KEY__EXTRACOLUMNS__CONFIG][constants.KEY__EXTRACOLUMNHEADERS__VALUES_AND_COUNTS]"> <input type="checkbox" name="configs" value="{{valueAndCount[0]}}" ng-checked="!isValueInSet(valueAndCount[0], hiddenConfigs)" ng-click="toggleValueInSet(valueAndCount[0], hiddenConfigs); setUpdatesPending(true)"> {{valueAndCount[0]}} ({{valueAndCount[1]}})<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> <input type="checkbox" ng-model="showThumbnailsPending" ng-init="showThumbnailsPending = true" ng-change="areUpdatesPending = true"/> Show thumbnails </td></tr> <tr><td> <input type="checkbox" ng-model="mergeIdenticalRowsPending" ng-init="mergeIdenticalRowsPending = true" ng-change="areUpdatesPending = true"/> Merge identical rows </td></tr> <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-show="'Pending Approval' == viewingTab"> <div style="display:inline-block"> <button style="font-size:20px" ng-click="submitApprovals(filteredImagePairs)" ng-disabled="submitPending || (filteredImagePairs.length == 0)"> Update these {{filteredImagePairs.length}} expectations on the server </button> </div> <div style="display:inline-block"> <div style="font-size:20px" ng-show="submitPending"> Submitting, please wait... </div> </div> <div> Advanced settings... <input type="checkbox" ng-model="showSubmitAdvancedSettings"> show <ul ng-show="showSubmitAdvancedSettings"> <li ng-repeat="setting in [constants.KEY__EXPECTATIONS__REVIEWED, constants.KEY__EXPECTATIONS__IGNOREFAILURE]"> {{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 {{filteredImagePairs.length}} matches; <span ng-show="filteredImagePairs.length > limitedImagePairs.length"> displaying the first {{limitedImagePairs.length}}. </span> <span ng-show="filteredImagePairs.length <= limitedImagePairs.length"> displaying them all. </span> <span ng-show="renderEndTime > renderStartTime"> Rendered in {{(renderEndTime - renderStartTime).toFixed(0)}} ms. </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="selectAllImagePairs()"> select </button> <button ng-click="clearAllImagePairs()"> clear </button> <button ng-click="toggleAllImagePairs()"> toggle </button> </div> <div ng-repeat="otherTab in tabs"> <button ng-click="moveSelectedImagePairsToTab(otherTab)" ng-disabled="selectedImagePairs.length == 0" ng-show="otherTab != viewingTab"> move {{selectedImagePairs.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 [constants.KEY__EXTRACOLUMNS__RESULT_TYPE, constants.KEY__EXTRACOLUMNS__BUILDER, constants.KEY__EXTRACOLUMNS__TEST, constants.KEY__EXTRACOLUMNS__CONFIG]"> <input type="radio" name="sortColumnRadio" value="{{categoryName}}" ng-checked="(sortColumnKey == categoryName)" ng-click="sortResultsBy(constants.KEY__IMAGEPAIRS__EXTRACOLUMNS, categoryName)"> {{categoryName}} </th> <!-- ... but there are a few columns where we display things differently. --> <th> <input type="radio" name="sortColumnRadio" value="bugs" ng-checked="(sortColumnKey == constants.KEY__EXPECTATIONS__BUGS)" ng-click="sortResultsBy(constants.KEY__IMAGEPAIRS__EXPECTATIONS, constants.KEY__EXPECTATIONS__BUGS)"> bugs </th> <th width="{{imageSize}}"> <input type="radio" name="sortColumnRadio" value="imageA" ng-checked="(sortColumnKey == constants.KEY__IMAGEPAIRS__IMAGE_A_URL)" ng-click="sortResultsBy('none', constants.KEY__IMAGEPAIRS__IMAGE_A_URL)"> {{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_A][constants.KEY__IMAGESETS__FIELD__DESCRIPTION]}} </th> <th width="{{imageSize}}"> <input type="radio" name="sortColumnRadio" value="imageB" ng-checked="(sortColumnKey == constants.KEY__IMAGEPAIRS__IMAGE_B_URL)" ng-click="sortResultsBy('none', constants.KEY__IMAGEPAIRS__IMAGE_B_URL)"> {{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_B][constants.KEY__IMAGESETS__FIELD__DESCRIPTION]}} </th> <th width="{{imageSize}}"> <input type="radio" name="sortColumnRadio" value="percentDifferingPixels" ng-checked="(sortColumnKey == constants.KEY__DIFFERENCES__PERCENT_DIFF_PIXELS)" ng-click="sortResultsBy(constants.KEY__IMAGEPAIRS__DIFFERENCES, constants.KEY__DIFFERENCES__PERCENT_DIFF_PIXELS)"> differing pixels in white </th> <th width="{{imageSize}}"> <input type="radio" name="sortColumnRadio" value="perceptualDiff" ng-checked="(sortColumnKey == constants.KEY__DIFFERENCES__PERCEPTUAL_DIFF)" ng-click="sortResultsBy(constants.KEY__IMAGEPAIRS__DIFFERENCES, constants.KEY__DIFFERENCES__PERCEPTUAL_DIFF)"> 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> <!-- imagepair-selection checkbox column --> </th> </tr> <tr ng-repeat="imagePair in limitedImagePairs" valign="top" ng-class-odd="'results-odd'" ng-class-even="'results-even'" results-updated-callback-directive> <td> {{imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][constants.KEY__EXTRACOLUMNS__RESULT_TYPE]}} <br> <button class="show-only-button" ng-show="viewingTab == defaultTab" ng-click="showOnlyResultType(imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][constants.KEY__EXTRACOLUMNS__RESULT_TYPE])" title="show only results of type {{imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][constants.KEY__EXTRACOLUMNS__RESULT_TYPE]}}"> show only </button> <br> <button class="show-all-button" ng-show="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 [constants.KEY__EXTRACOLUMNS__BUILDER, constants.KEY__EXTRACOLUMNS__TEST]"> {{imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][categoryName]}} <br> <button class="show-only-button" ng-show="viewingTab == defaultTab" ng-disabled="imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][categoryName] == categoryValueMatch[categoryName]" ng-click="setCategoryValueMatch(categoryName, imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][categoryName])" title="show only results of {{categoryName}} {{imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][categoryName]}}"> show only </button> <br> <button class="show-all-button" ng-show="viewingTab == defaultTab" ng-disabled="'' == categoryValueMatch[categoryName]" ng-click="setCategoryValueMatch(categoryName, '')" title="show results of all {{categoryName}}s"> show all </button> </td> <td> {{imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][constants.KEY__EXTRACOLUMNS__CONFIG]}} <br> <button class="show-only-button" ng-show="viewingTab == defaultTab" ng-click="showOnlyConfig(imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][constants.KEY__EXTRACOLUMNS__CONFIG])" title="show only results of config {{imagePair[constants.KEY__IMAGEPAIRS__EXTRACOLUMNS][constants.KEY__EXTRACOLUMNS__CONFIG]}}"> show only </button> <br> <button class="show-all-button" ng-show="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 imagePair[constants.KEY__IMAGEPAIRS__EXPECTATIONS][constants.KEY__EXPECTATIONS__BUGS]" href="https://code.google.com/p/skia/issues/detail?id={{bug}}" target="_blank"> {{bug}} </a> </td> <!-- image A --> <td width="{{imageSize}}" ng-if="imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] > 0" rowspan="{{imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN]}}"> <div ng-if="imagePair[constants.KEY__IMAGEPAIRS__IMAGE_A_URL] != null"> <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/> <img ng-if="showThumbnails" width="{{imageSize}}" ng-src="{{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_A][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{imagePair[constants.KEY__IMAGEPAIRS__IMAGE_A_URL]}}" /> </div> <div ng-show="imagePair[constants.KEY__IMAGEPAIRS__IMAGE_A_URL] == null" style="text-align:center"> –none– </div> </td> <!-- image B --> <td width="{{imageSize}}" ng-if="imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] > 0" rowspan="{{imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN]}}"> <div ng-if="imagePair[constants.KEY__IMAGEPAIRS__IMAGE_B_URL] != null"> <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/> <img ng-if="showThumbnails" width="{{imageSize}}" ng-src="{{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_B][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{imagePair[constants.KEY__IMAGEPAIRS__IMAGE_B_URL]}}" /> </div> <div ng-show="imagePair[constants.KEY__IMAGEPAIRS__IMAGE_B_URL] == null" style="text-align:center"> –none– </div> </td> <!-- whitediffs: every differing pixel shown in white --> <td width="{{imageSize}}" ng-if="imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] > 0" rowspan="{{imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN]}}"> <div ng-if="imagePair[constants.KEY__IMAGEPAIRS__IS_DIFFERENT]" 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."> <a href="{{imageSets[constants.KEY__IMAGESETS__SET__WHITEDIFFS][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{getImageDiffRelativeUrl(imagePair)}}" target="_blank">View Image</a><br/> <img ng-if="showThumbnails" width="{{imageSize}}" ng-src="{{imageSets[constants.KEY__IMAGESETS__SET__WHITEDIFFS][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{getImageDiffRelativeUrl(imagePair)}}" /> <br/> {{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__PERCENT_DIFF_PIXELS].toFixed(4)}}% ({{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__NUM_DIFF_PIXELS]}}) </div> <div ng-show="!imagePair[constants.KEY__IMAGEPAIRS__IS_DIFFERENT]" style="text-align:center"> –none– </div> </td> <!-- diffs: per-channel RGB deltas --> <td width="{{imageSize}}" ng-if="imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] > 0" rowspan="{{imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN]}}"> <div ng-if="imagePair[constants.KEY__IMAGEPAIRS__IS_DIFFERENT]" 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]}}"> <a href="{{imageSets[constants.KEY__IMAGESETS__SET__DIFFS][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{getImageDiffRelativeUrl(imagePair)}}" target="_blank">View Image</a><br/> <img ng-if="showThumbnails" ng-style="{backgroundColor: pixelDiffBgColor}" width="{{imageSize}}" ng-src="{{imageSets[constants.KEY__IMAGESETS__SET__DIFFS][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{getImageDiffRelativeUrl(imagePair)}}" /> <br/> {{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__PERCEPTUAL_DIFF].toFixed(4)}}% {{imagePair[constants.KEY__IMAGEPAIRS__DIFFERENCES][constants.KEY__DIFFERENCES__MAX_DIFF_PER_CHANNEL]}} </div> <div ng-show="!imagePair[constants.KEY__IMAGEPAIRS__IS_DIFFERENT]" style="text-align:center"> –none– </div> </td> <td ng-if="imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] > 0" rowspan="{{imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN]}}"> <br/> <input type="checkbox" name="rowSelect" value="{{imagePair.index}}" ng-checked="isValueInArray(imagePair.index, selectedImagePairs)" ng-click="toggleSomeImagePairs($index, imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN])"> </tr> </table> <!-- imagePairs --> </td></tr></table> <!-- table holding results header + imagePairs table --> </div><!-- main display area of selected tab --> </div><!-- everything: hide until data is loaded --> </body> </html>