<!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>