Index: gm/rebaseline_server/static/loader.js |
=================================================================== |
--- gm/rebaseline_server/static/loader.js (revision 11924) |
+++ gm/rebaseline_server/static/loader.js (working copy) |
@@ -8,6 +8,7 @@ |
[] |
); |
+ |
// TODO(epoger): Combine ALL of our filtering operations (including |
// truncation) into this one filter, so that runs most efficiently? |
// (We would have to make sure truncation still took place after |
@@ -20,6 +21,8 @@ |
var filteredItems = []; |
for (var i = 0; i < unfilteredItems.length; i++) { |
var item = unfilteredItems[i]; |
+ // For performance, we examine the "set" objects directly rather |
+ // than calling $scope.isValueInSet(). |
if (!(true == hiddenResultTypes[item.resultType]) && |
!(true == hiddenConfigs[item.config]) && |
(viewingTab == item.tab)) { |
@@ -31,6 +34,7 @@ |
} |
); |
+ |
Loader.controller( |
'Loader.Controller', |
function($scope, $http, $filter, $location) { |
@@ -39,6 +43,11 @@ |
$scope.loadingMessage = "Loading results of type '" + resultsToLoad + |
"', please wait..."; |
+ /** |
+ * On initial page load, load a full dictionary of results. |
+ * Once the dictionary is loaded, unhide the page elements so they can |
+ * render the data. |
+ */ |
$http.get("/results/" + resultsToLoad).success( |
function(data, status, header, config) { |
$scope.loadingMessage = "Processing data, please wait..."; |
@@ -74,13 +83,16 @@ |
$scope.testData[i].tab = $scope.defaultTab; |
} |
+ // Arrays within which the user can toggle individual elements. |
+ $scope.selectedItems = []; |
+ |
+ // Sets within which the user can toggle individual elements. |
$scope.hiddenResultTypes = { |
'failure-ignored': true, |
'no-comparison': true, |
'succeeded': true, |
}; |
$scope.hiddenConfigs = {}; |
- $scope.selectedItems = []; |
$scope.updateResults(); |
$scope.loadingMessage = ""; |
@@ -94,59 +106,21 @@ |
} |
); |
- $scope.isItemSelected = function(index) { |
- return (-1 != $scope.selectedItems.indexOf(index)); |
- } |
- $scope.toggleItemSelected = function(index) { |
- var i = $scope.selectedItems.indexOf(index); |
- if (-1 == i) { |
- $scope.selectedItems.push(index); |
- } else { |
- $scope.selectedItems.splice(i, 1); |
- } |
- // unlike other toggle methods below, does not set |
- // $scope.areUpdatesPending = true; |
- } |
- $scope.isHiddenResultType = function(thisResultType) { |
- return (true == $scope.hiddenResultTypes[thisResultType]); |
- } |
- $scope.toggleHiddenResultType = function(thisResultType) { |
- if (true == $scope.hiddenResultTypes[thisResultType]) { |
- delete $scope.hiddenResultTypes[thisResultType]; |
- } else { |
- $scope.hiddenResultTypes[thisResultType] = true; |
- } |
- $scope.areUpdatesPending = true; |
- } |
+ // |
+ // Tab operations. |
+ // |
- // TODO(epoger): Rather than maintaining these as hard-coded |
- // variants of isHiddenResultType and toggleHiddenResultType, we |
- // should create general-purpose functions that can work with ANY |
- // category. |
- // But for now, I wanted to see this working. :-) |
- $scope.isHiddenConfig = function(thisConfig) { |
- return (true == $scope.hiddenConfigs[thisConfig]); |
- } |
- $scope.toggleHiddenConfig = function(thisConfig) { |
- if (true == $scope.hiddenConfigs[thisConfig]) { |
- delete $scope.hiddenConfigs[thisConfig]; |
- } else { |
- $scope.hiddenConfigs[thisConfig] = true; |
- } |
- $scope.areUpdatesPending = true; |
- } |
- |
+ /** |
+ * Change the selected tab. |
+ * |
+ * @param tab (string): name of the tab to select |
+ */ |
$scope.setViewingTab = function(tab) { |
$scope.viewingTab = tab; |
$scope.updateResults(); |
} |
- $scope.localTimeString = function(secondsPastEpoch) { |
- var d = new Date(secondsPastEpoch * 1000); |
- return d.toString(); |
- } |
- |
/** |
* Move the items in $scope.selectedItems to a different tab, |
* and then clear $scope.selectedItems. |
@@ -177,6 +151,31 @@ |
$scope.numResultsPerTab[newTab] += numItems; |
} |
+ |
+ // |
+ // updateResults() and friends. |
+ // |
+ |
+ /** |
+ * Set $scope.areUpdatesPending (to enable/disable the Update Results |
+ * button). |
+ * |
+ * TODO(epoger): We could reduce the amount of code by just setting the |
+ * variable directly (from, e.g., a button's ng-click handler). But when |
+ * I tried that, the HTML elements depending on the variable did not get |
+ * updated. |
+ * It turns out that this is due to variable scoping within an ng-repeat |
+ * element; see http://stackoverflow.com/questions/15388344/behavior-of-assignment-expression-invoked-by-ng-click-within-ng-repeat |
+ * |
+ * @param val boolean value to set $scope.areUpdatesPending to |
+ */ |
+ $scope.setUpdatesPending = function(val) { |
+ $scope.areUpdatesPending = val; |
+ } |
+ |
+ /** |
+ * Update the displayed results, based on filters/settings. |
+ */ |
$scope.updateResults = function() { |
$scope.displayLimit = $scope.displayLimitPending; |
// TODO(epoger): Every time we apply a filter, AngularJS creates |
@@ -209,14 +208,24 @@ |
$scope.filteredTestData, $scope.displayLimit); |
} |
$scope.imageSize = $scope.imageSizePending; |
- $scope.areUpdatesPending = false; |
+ $scope.setUpdatesPending(false); |
} |
+ /** |
+ * Re-sort the displayed results. |
+ * |
+ * @param sortColumn (string): name of the column to sort on |
+ */ |
$scope.sortResultsBy = function(sortColumn) { |
$scope.sortColumn = sortColumn; |
$scope.updateResults(); |
} |
+ |
+ // |
+ // Operations for sending info back to the server. |
+ // |
+ |
/** |
* Tell the server that the actual results of these particular tests |
* are acceptable. |
@@ -266,5 +275,90 @@ |
$scope.submitPending = false; |
}); |
} |
+ |
+ |
+ // |
+ // Operations we use to mimic Set semantics, in such a way that |
+ // checking for presence within the Set is as fast as possible. |
+ // But getting a list of all values within the Set is not necessarily |
+ // possible. |
+ // TODO(epoger): move into a separate .js file? |
+ // |
+ |
+ /** |
+ * Returns true if value "value" is present within set "set". |
+ * |
+ * @param value a value of any type |
+ * @param set an Object which we use to mimic set semantics |
+ * (this should make isValueInSet faster than if we used an Array) |
+ */ |
+ $scope.isValueInSet = function(value, set) { |
+ return (true == set[value]); |
+ } |
+ |
+ /** |
+ * If value "value" is already in set "set", remove it; otherwise, add it. |
+ * |
+ * @param value a value of any type |
+ * @param set an Object which we use to mimic set semantics |
+ */ |
+ $scope.toggleValueInSet = function(value, set) { |
+ if (true == set[value]) { |
+ delete set[value]; |
+ } else { |
+ set[value] = true; |
+ } |
+ } |
+ |
+ |
+ // |
+ // Array operations; similar to our Set operations, but operate on a |
+ // Javascript Array so we *can* easily get a list of all values in the Set. |
+ // TODO(epoger): move into a separate .js file? |
+ // |
+ |
+ /** |
+ * Returns true if value "value" is present within array "array". |
+ * |
+ * @param value a value of any type |
+ * @param array a Javascript Array |
+ */ |
+ $scope.isValueInArray = function(value, array) { |
+ return (-1 != array.indexOf(value)); |
+ } |
+ |
+ /** |
+ * If value "value" is already in array "array", remove it; otherwise, |
+ * add it. |
+ * |
+ * @param value a value of any type |
+ * @param array a Javascript Array |
+ */ |
+ $scope.toggleValueInArray = function(value, array) { |
+ var i = array.indexOf(value); |
+ if (-1 == i) { |
+ array.push(value); |
+ } else { |
+ array.splice(i, 1); |
+ } |
+ } |
+ |
+ |
+ // |
+ // Miscellaneous utility functions. |
+ // TODO(epoger): move into a separate .js file? |
+ // |
+ |
+ /** |
+ * Returns a human-readable (in local time zone) time string for a |
+ * particular moment in time. |
+ * |
+ * @param secondsPastEpoch (numeric): seconds past epoch in UTC |
+ */ |
+ $scope.localTimeString = function(secondsPastEpoch) { |
+ var d = new Date(secondsPastEpoch * 1000); |
+ return d.toString(); |
+ } |
+ |
} |
); |