Index: gm/rebaseline_server/static/loader.js |
diff --git a/gm/rebaseline_server/static/loader.js b/gm/rebaseline_server/static/loader.js |
index e05f1c8f863f0684f3387c21734f4d73c46f6b33..296689bde23fff581e364b805b4f77fa8f7ccdd5 100644 |
--- a/gm/rebaseline_server/static/loader.js |
+++ b/gm/rebaseline_server/static/loader.js |
@@ -56,6 +56,65 @@ Loader.filter( |
} |
); |
+/** |
+ * Limit the input imagePairs to some max number, and merge identical rows |
+ * (adjacent rows which have the same (imageA, imageB) pair). |
+ * |
+ * @param unfilteredImagePairs imagePairs to filter |
+ * @param maxPairs maximum number of pairs to output, or <0 for no limit |
+ * @param mergeIdenticalRows if true, merge identical rows by setting |
+ * ROWSPAN>1 on the first merged row, and ROWSPAN=0 for the rest |
+ */ |
+Loader.filter( |
+ 'mergeAndLimit', |
+ function(constants) { |
+ return function(unfilteredImagePairs, maxPairs, mergeIdenticalRows) { |
+ var numPairs = unfilteredImagePairs.length; |
+ if ((maxPairs > 0) && (maxPairs < numPairs)) { |
+ numPairs = maxPairs; |
+ } |
+ var filteredImagePairs = []; |
+ if (!mergeIdenticalRows || (numPairs == 1)) { |
+ // Take a shortcut if we're not merging identical rows. |
+ // We still need to set ROWSPAN to 1 for each row, for the HTML viewer. |
+ for (var i = numPairs-1; i >= 0; i--) { |
+ var imagePair = unfilteredImagePairs[i]; |
+ imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] = 1; |
+ filteredImagePairs[i] = imagePair; |
+ } |
+ } else if (numPairs > 1) { |
+ // General case--there are at least 2 rows, so we may need to merge some. |
+ // Work from the bottom up, so we can keep a running total of how many |
+ // rows should be merged, and set ROWSPAN of the top row accordingly. |
+ var imagePair = unfilteredImagePairs[numPairs-1]; |
+ var nextRowImageAUrl = imagePair[constants.KEY__IMAGEPAIRS__IMAGE_A_URL]; |
+ var nextRowImageBUrl = imagePair[constants.KEY__IMAGEPAIRS__IMAGE_B_URL]; |
+ imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] = 1; |
+ filteredImagePairs[numPairs-1] = imagePair; |
+ for (var i = numPairs-2; i >= 0; i--) { |
+ imagePair = unfilteredImagePairs[i]; |
+ var thisRowImageAUrl = imagePair[constants.KEY__IMAGEPAIRS__IMAGE_A_URL]; |
+ var thisRowImageBUrl = imagePair[constants.KEY__IMAGEPAIRS__IMAGE_B_URL]; |
+ if ((thisRowImageAUrl == nextRowImageAUrl) && |
+ (thisRowImageBUrl == nextRowImageBUrl)) { |
+ imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] = |
+ filteredImagePairs[i+1][constants.KEY__IMAGEPAIRS__ROWSPAN] + 1; |
+ filteredImagePairs[i+1][constants.KEY__IMAGEPAIRS__ROWSPAN] = 0; |
+ } else { |
+ imagePair[constants.KEY__IMAGEPAIRS__ROWSPAN] = 1; |
+ nextRowImageAUrl = thisRowImageAUrl; |
+ nextRowImageBUrl = thisRowImageBUrl; |
+ } |
+ filteredImagePairs[i] = imagePair; |
+ } |
+ } else { |
+ // No results. |
+ } |
+ return filteredImagePairs; |
+ }; |
+ } |
+); |
+ |
Loader.controller( |
'Loader.Controller', |
@@ -234,6 +293,21 @@ Loader.controller( |
} |
} |
+ /** |
+ * Toggle selection state of a subset of the currently showing tests. |
+ * |
+ * @param startIndex index within $scope.limitedImagePairs of the first |
+ * test to toggle selection state of |
+ * @param num number of tests (in a contiguous block) to toggle |
+ */ |
+ $scope.toggleSomeImagePairs = function(startIndex, num) { |
+ var numImagePairsShowing = $scope.limitedImagePairs.length; |
+ for (var i = startIndex; i < startIndex + num; i++) { |
+ var index = $scope.limitedImagePairs[i].index; |
+ $scope.toggleValueInArray(index, $scope.selectedImagePairs); |
+ } |
+ } |
+ |
// |
// Tab operations. |
@@ -335,6 +409,7 @@ Loader.controller( |
'resultsToLoad': $scope.queryParameters.copiers.simple, |
'displayLimitPending': $scope.queryParameters.copiers.simple, |
'showThumbnailsPending': $scope.queryParameters.copiers.simple, |
+ 'mergeIdenticalRowsPending': $scope.queryParameters.copiers.simple, |
'imageSizePending': $scope.queryParameters.copiers.simple, |
'sortColumnSubdict': $scope.queryParameters.copiers.simple, |
'sortColumnKey': $scope.queryParameters.copiers.simple, |
@@ -400,6 +475,7 @@ Loader.controller( |
$scope.renderStartTime = window.performance.now(); |
$log.debug("renderStartTime: " + $scope.renderStartTime); |
$scope.displayLimit = $scope.displayLimitPending; |
+ $scope.mergeIdenticalRows = $scope.mergeIdenticalRowsPending; |
// TODO(epoger): Every time we apply a filter, AngularJS creates |
// another copy of the array. Is there a way we can filter out |
// the imagePairs as they are displayed, rather than storing multiple |
@@ -425,9 +501,10 @@ Loader.controller( |
$scope.categoryValueMatch.test, |
$scope.viewingTab |
), |
- $scope.getSortColumnValue, doReverse); |
- $scope.limitedImagePairs = $filter("limitTo")( |
- $scope.filteredImagePairs, $scope.displayLimit); |
+ [$scope.getSortColumnValue, $scope.getSecondOrderSortValue], |
+ doReverse); |
+ $scope.limitedImagePairs = $filter("mergeAndLimit")( |
+ $scope.filteredImagePairs, $scope.displayLimit, $scope.mergeIdenticalRows); |
} else { |
$scope.filteredImagePairs = |
$filter("orderBy")( |
@@ -436,8 +513,9 @@ Loader.controller( |
{tab: $scope.viewingTab}, |
true |
), |
- $scope.getSortColumnValue); |
- $scope.limitedImagePairs = $scope.filteredImagePairs; |
+ [$scope.getSortColumnValue, $scope.getSecondOrderSortValue]); |
+ $scope.limitedImagePairs = $filter("mergeAndLimit")( |
+ $scope.filteredImagePairs, -1, $scope.mergeIdenticalRows); |
} |
$scope.showThumbnails = $scope.showThumbnailsPending; |
$scope.imageSize = $scope.imageSizePending; |
@@ -458,7 +536,8 @@ Loader.controller( |
* Re-sort the displayed results. |
* |
* @param subdict (string): which KEY__IMAGEPAIRS__* subdictionary |
- * the sort column key is within |
+ * the sort column key is within, or 'none' if the sort column |
+ * key is one of KEY__IMAGEPAIRS__* |
* @param key (string): sort by value associated with this key in subdict |
*/ |
$scope.sortResultsBy = function(subdict, key) { |
@@ -477,12 +556,29 @@ Loader.controller( |
$scope.getSortColumnValue = function(imagePair) { |
if ($scope.sortColumnSubdict in imagePair) { |
return imagePair[$scope.sortColumnSubdict][$scope.sortColumnKey]; |
+ } else if ($scope.sortColumnKey in imagePair) { |
+ return imagePair[$scope.sortColumnKey]; |
} else { |
return undefined; |
} |
} |
/** |
+ * For a particular ImagePair, return the value we use for the |
+ * second-order sort (tiebreaker when multiple rows have |
+ * the same getSortColumnValue()). |
+ * |
+ * We join the imageA and imageB urls for this value, so that we merge |
+ * adjacent rows as much as possible. |
+ * |
+ * @param imagePair: imagePair to get a column value out of. |
+ */ |
+ $scope.getSecondOrderSortValue = function(imagePair) { |
+ return imagePair[constants.KEY__IMAGEPAIRS__IMAGE_A_URL] + "-vs-" + |
+ imagePair[constants.KEY__IMAGEPAIRS__IMAGE_B_URL]; |
+ } |
+ |
+ /** |
* Set $scope.categoryValueMatch[name] = value, and update results. |
* |
* @param name |