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          &nbsp;{{tab}} ({{numResultsPerTab[tab]}})&nbsp;
48        </div>
49        <div class="tab-spacer">
50          &nbsp;
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              &ndash;none&ndash;
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              &ndash;none&ndash;
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              &ndash;none&ndash;
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              &ndash;none&ndash;
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