Index: gm/rebaseline_server/static/new/js/app.js |
diff --git a/gm/rebaseline_server/static/new/js/app.js b/gm/rebaseline_server/static/new/js/app.js |
deleted file mode 100644 |
index 0a1fac0a450f848083742c51e9ab3a159cab6ed9..0000000000000000000000000000000000000000 |
--- a/gm/rebaseline_server/static/new/js/app.js |
+++ /dev/null |
@@ -1,1130 +0,0 @@ |
-'use strict'; |
- |
-/** |
- * TODO (stephana@): This is still work in progress. |
- * It does not offer the same functionality as the current version, but |
- * will serve as the starting point for a new backend. |
- * It works with the current backend, but does not support rebaselining. |
- */ |
- |
-/* |
- * Wrap everything into an IIFE to not polute the global namespace. |
- */ |
-(function () { |
- |
- // Declare app level module which contains everything of the current app. |
- // ui.bootstrap refers to directives defined in the AngularJS Bootstrap |
- // UI package (http://angular-ui.github.io/bootstrap/). |
- var app = angular.module('rbtApp', ['ngRoute', 'ui.bootstrap']); |
- |
- // Configure the different within app views. |
- app.config(['$routeProvider', function($routeProvider) { |
- $routeProvider.when('/', {templateUrl: 'partials/index-view.html', |
- controller: 'IndexCtrl'}); |
- $routeProvider.when('/view', {templateUrl: 'partials/rebaseline-view.html', |
- controller: 'RebaselineCrtrl'}); |
- $routeProvider.otherwise({redirectTo: '/'}); |
- }]); |
- |
- |
- // TODO (stephana): Some of these constants are 'gm' specific. In the |
- // next iteration we need to remove those as we move the more generic |
- // 'dm' testing tool. |
- // |
- // Shared constants used here and in the markup. These are exported when |
- // when used by a controller. |
- var c = { |
- // Define different view states as we load the data. |
- ST_LOADING: 1, |
- ST_STILL_LOADING: 2, |
- ST_READY: 3, |
- |
- // These column types are used by the Column class. |
- COL_T_FILTER: 'filter', |
- COL_T_IMAGE: 'image', |
- COL_T_REGULAR: 'regular', |
- |
- // Request parameters used to select between subsets of results. |
- RESULTS_ALL: 'all', |
- RESULTS_FAILURES: 'failures', |
- |
- // Filter types are used by the Column class. |
- FILTER_FREE_FORM: 'free_form', |
- FILTER_CHECK_BOX: 'checkbox', |
- |
- // Columns either provided by the backend response or added in code. |
- // TODO (stephana): This should go away once we switch to 'dm'. |
- COL_BUGS: 'bugs', |
- COL_IGNORE_FAILURE: 'ignore-failure', |
- COL_REVIEWED_BY_HUMANS: 'reviewed-by-human', |
- |
- // Defines the order in which image columns appear. |
- // TODO (stephana@): needs to be driven by backend data. |
- IMG_COL_ORDER: [ |
- { |
- key: 'imageA', |
- urlField: ['imageAUrl'] |
- }, |
- { |
- key: 'imageB', |
- urlField: ['imageBUrl'] |
- }, |
- { |
- key: 'whiteDiffs', |
- urlField: ['differenceData', 'whiteDiffUrl'], |
- percentField: ['differenceData', 'percentDifferingPixels'], |
- valueField: ['differenceData', 'numDifferingPixels'] |
- }, |
- { |
- key: 'diffs', |
- urlField: ['differenceData', 'diffUrl'], |
- percentField: ['differenceData', 'perceptualDifference'], |
- valueField: ['differenceData', 'maxDiffPerChannel'] |
- } |
- ], |
- |
- // Choice of availabe image size selection. |
- IMAGE_SIZES: [ |
- 100, |
- 200, |
- 400 |
- ], |
- |
- // Choice of available number of records selection. |
- MAX_RECORDS: [ |
- '100', |
- '200', |
- '300' |
- ] |
- }; // end constants |
- |
- /* |
- * Index Controller |
- */ |
- // TODO (stephana): Remove $timeout since it only simulates loading delay. |
- app.controller('IndexCtrl', ['$scope', '$timeout', 'dataService', |
- function($scope, $timeout, dataService) { |
- // init the scope |
- $scope.c = c; |
- $scope.state = c.ST_LOADING; |
- $scope.qStr = dataService.getQueryString; |
- |
- // TODO (stephana): Remove and replace with index data generated by the |
- // backend to reflect the current "known" image sets to compare. |
- $scope.allSKPs = [ |
- { |
- params: { |
- setBSection: 'actual-results', |
- setASection: 'expected-results', |
- setBDir: 'gs://chromium-skia-skp-summaries/' + |
- 'Test-Mac10.8-MacMini4.1-GeForce320M-x86_64-Debug', |
- setADir: 'repo:expectations/skp/' + |
- 'Test-Mac10.8-MacMini4.1-GeForce320M-x86_64-Debug' |
- }, |
- title: 'expected vs actuals on ' + |
- 'Test-Mac10.8-MacMini4.1-GeForce320M-x86_64-Debug' |
- }, |
- { |
- params: { |
- setBSection: 'actual-results', |
- setASection: 'expected-results', |
- setBDir: 'gs://chromium-skia-skp-summaries/' + |
- 'Test-Ubuntu12-ShuttleA-GTX660-x86-Release', |
- setADir: 'repo:expectations/skp/'+ |
- 'Test-Ubuntu12-ShuttleA-GTX660-x86-Release' |
- }, |
- title: 'expected vs actuals on Test-Ubuntu12-ShuttleA-GTX660-x86-Release' |
- }, |
- { |
- params: { |
- setBSection: 'actual-results', |
- setASection: 'actual-results', |
- setBDir: 'gs://chromium-skia-skp-summaries/' + |
- 'Test-Ubuntu12-ShuttleA-GTX660-x86-Release', |
- setADir: 'gs://chromium-skia-skp-summaries/' + |
- 'Test-Mac10.8-MacMini4.1-GeForce320M-x86_64-Debug' |
- }, |
- title: 'Actuals on Test-Mac10.8-MacMini4.1-GeForce320M-x86_64-Debug ' + |
- 'vs Test-Ubuntu12-ShuttleA-GTX660-x86-Release' |
- } |
- ]; |
- |
- // TODO (stephana): Remove this once we load index data from the server. |
- $timeout(function () { |
- $scope.state = c.ST_READY; |
- }); |
- }]); |
- |
- /* |
- * RebaselineCtrl |
- * Controls the main comparison view. |
- * |
- * @param {service} dataService Service that encapsulates functions to |
- * retrieve data from the backend. |
- * |
- */ |
- app.controller('RebaselineCrtrl', ['$scope', '$timeout', 'dataService', |
- function($scope, $timeout, dataService) { |
- // determine which to request |
- // TODO (stephana): This should be extracted from the query parameters. |
- var target = c.TARGET_GM; |
- |
- // process the rquest arguments |
- // TODO (stephana): This should be determined from the query parameters. |
- var loadFn = dataService.loadAll; |
- |
- // controller state variables |
- var allData = null; |
- var filterFuncs = null; |
- var currentData = null; |
- var selectedData = null; |
- |
- // Index of the column that should provide the sort key |
- var sortByIdx = 0; |
- |
- // Sort in asending (true) or descending (false) order |
- var sortOrderAsc = true; |
- |
- // Array of functions for each column used for comparison during sort. |
- var compareFunctions = null; |
- |
- // Variables to track load and render times |
- var startTime; |
- var loadStartTime; |
- |
- |
- /** Load the data from the backend **/ |
- loadStartTime = Date.now(); |
- function loadData() { |
- loadFn().then( |
- function (serverData) { |
- $scope.header = serverData.header; |
- $scope.loadTime = (Date.now() - loadStartTime)/1000; |
- |
- // keep polling if the data are not ready yet |
- if ($scope.header.resultsStillLoading) { |
- $scope.state = c.ST_STILL_LOADING; |
- $timeout(loadData, 5000); |
- return; |
- } |
- |
- // get the filter colunms and an array to hold filter data by user |
- var fcol = getFilterColumns(serverData); |
- $scope.filterCols = fcol[0]; |
- $scope.filterVals = fcol[1]; |
- |
- // Add extra columns and retrieve the image columns |
- var otherCols = [ Column.regular(c.COL_BUGS) ]; |
- var imageCols = getImageColumns(serverData); |
- |
- // Concat to get all columns |
- // NOTE: The order is important since filters are rendered first, |
- // followed by regular columns and images |
- $scope.allCols = $scope.filterCols.concat(otherCols, imageCols); |
- |
- // Pre-process the data and get the filter functions. |
- var dataFilters = getDataAndFilters(serverData, $scope.filterCols, |
- otherCols, imageCols); |
- allData = dataFilters[0]; |
- filterFuncs = dataFilters[1]; |
- |
- // Get regular columns (== not image columns) |
- var regularCols = $scope.filterCols.concat(otherCols); |
- |
- // Get the compare functions for regular and image columns. These |
- // are then used to sort by the respective columns. |
- compareFunctions = DataRow.getCompareFunctions(regularCols, |
- imageCols); |
- |
- // Filter and sort the results to get them ready for rendering |
- updateResults(); |
- |
- // Data are ready for display |
- $scope.state = c.ST_READY; |
- }, |
- function (httpErrResponse) { |
- console.log(httpErrResponse); |
- }); |
- }; |
- |
- /* |
- * updateResults |
- * Central render function. Everytime settings/filters/etc. changed |
- * this function is called to filter, sort and splice the data. |
- * |
- * NOTE (stephana): There is room for improvement here: before filtering |
- * and sorting we could check if this is necessary. But this has not been |
- * a bottleneck so far. |
- */ |
- function updateResults () { |
- // run digest before we update the results. This allows |
- // updateResults to be called from functions trigger by ngChange |
- $scope.updating = true; |
- startTime = Date.now(); |
- |
- // delay by one render cycle so it can be called via ng-change |
- $timeout(function() { |
- // filter data |
- selectedData = filterData(allData, filterFuncs, $scope.filterVals); |
- |
- // sort the selected data. |
- sortData(selectedData, compareFunctions, sortByIdx, sortOrderAsc); |
- |
- // only conside the elements that we really need |
- var nRecords = $scope.settings.nRecords; |
- currentData = selectedData.slice(0, parseInt(nRecords)); |
- |
- DataRow.setRowspanValues(currentData, $scope.mergeIdenticalRows); |
- |
- // update the scope with relevant data for rendering. |
- $scope.data = currentData; |
- $scope.totalRecords = allData.length; |
- $scope.showingRecords = currentData.length; |
- $scope.selectedRecords = selectedData.length; |
- $scope.updating = false; |
- |
- // measure the filter time and total render time (via timeout). |
- $scope.filterTime = Date.now() - startTime; |
- $timeout(function() { |
- $scope.renderTime = Date.now() - startTime; |
- }); |
- }); |
- }; |
- |
- /** |
- * Generate the style value to set the width of images. |
- * |
- * @param {Column} col Column that we are trying to render. |
- * @param {int} paddingPx Number of padding pixels. |
- * @param {string} defaultVal Default value if not an image column. |
- * |
- * @return {string} Value to be used in ng-style element to set the width |
- * of a image column. |
- **/ |
- $scope.getImageWidthStyle = function (col, paddingPx, defaultVal) { |
- var result = (col.ctype === c.COL_T_IMAGE) ? |
- ($scope.imageSize + paddingPx + 'px') : defaultVal; |
- return result; |
- }; |
- |
- /** |
- * Sets the column by which to sort the data. If called for the |
- * currently sorted column it will cause the sort to toggle between |
- * ascending and descending. |
- * |
- * @param {int} colIdx Index of the column to use for sorting. |
- **/ |
- $scope.sortBy = function (colIdx) { |
- if (sortByIdx === colIdx) { |
- sortOrderAsc = !sortOrderAsc; |
- } else { |
- sortByIdx = colIdx; |
- sortOrderAsc = true; |
- } |
- updateResults(); |
- }; |
- |
- /** |
- * Helper function to generate a CSS class indicating whether this column |
- * is the sort key. If it is a class name with the sort direction (Asc/Desc) is |
- * return otherwise the default value is returned. In markup we use this |
- * to display (or not display) an arrow next to the column name. |
- * |
- * @param {string} prefix Prefix of the classname to be generated. |
- * @param {int} idx Index of the target column. |
- * @param {string} defaultVal Value to return if current column is not used |
- * for sorting. |
- * |
- * @return {string} CSS class name that a combination of the prefix and |
- * direction indicator ('Asc' or 'Desc') if the column is |
- * used for sorting. Otherwise the defaultVal is returned. |
- **/ |
- $scope.getSortedClass = function (prefix, idx, defaultVal) { |
- if (idx === sortByIdx) { |
- return prefix + ((sortOrderAsc) ? 'Asc' : 'Desc'); |
- } |
- |
- return defaultVal; |
- }; |
- |
- /** |
- * Checkbox to merge identical records has change. Force an update. |
- **/ |
- $scope.mergeRowsChanged = function () { |
- updateResults(); |
- } |
- |
- /** |
- * Max number of records to display has changed. Force an update. |
- **/ |
- $scope.maxRecordsChanged = function () { |
- updateResults(); |
- }; |
- |
- /** |
- * Filter settings changed. Force an update. |
- **/ |
- $scope.filtersChanged = function () { |
- updateResults(); |
- }; |
- |
- /** |
- * Sets all possible values of the specified values to the given value. |
- * That means all checkboxes are eiter selected or unselected. |
- * Then force an update. |
- * |
- * @param {int} idx Index of the target filter column. |
- * @param {boolean} val Value to set the filter values to. |
- * |
- **/ |
- $scope.setFilterAll = function (idx, val) { |
- for(var i=0, len=$scope.filterVals[idx].length; i<len; i++) { |
- $scope.filterVals[idx][i] = val; |
- } |
- updateResults(); |
- }; |
- |
- /** |
- * Toggle the values of a filter. This toggles all values in a |
- * filter. |
- * |
- * @param {int} idx Index of the target filter column. |
- **/ |
- $scope.setFilterToggle = function (idx) { |
- for(var i=0, len=$scope.filterVals[idx].length; i<len; i++) { |
- $scope.filterVals[idx][i] = !$scope.filterVals[idx][i]; |
- } |
- updateResults(); |
- }; |
- |
- // **************************************** |
- // Initialize the scope. |
- // **************************************** |
- |
- // Inject the constants into the scope and set the initial state. |
- $scope.c = c; |
- $scope.state = c.ST_LOADING; |
- |
- // Initial settings |
- $scope.settings = { |
- showThumbnails: true, |
- imageSize: c.IMAGE_SIZES[0], |
- nRecords: c.MAX_RECORDS[0], |
- mergeIdenticalRows: true |
- }; |
- |
- // Initial values for filters set in loadData() |
- $scope.filterVals = []; |
- |
- // Information about records - set in loadData() |
- $scope.totalRecords = 0; |
- $scope.showingRecords = 0; |
- $scope.updating = false; |
- |
- // Trigger the data loading. |
- loadData(); |
- |
- }]); |
- |
- // data structs to interface with markup and backend |
- /** |
- * Models a column. It aggregates attributes of all |
- * columns types. Some might be empty. See convenience |
- * factory methods below for different column types. |
- * |
- * @param {string} key Uniquely identifies this columns |
- * @param {string} ctype Type of columns. Use COL_* constants. |
- * @param {string} ctitle Human readable title of the column. |
- * @param {string} ftype Filter type. Use FILTER_* constants. |
- * @param {FilterOpt[]} foptions Filter options. For 'checkbox' filters this |
- is used to render all the checkboxes. |
- For freeform filters this is a list of all |
- available values. |
- * @param {string} baseUrl Baseurl for image columns. All URLs are relative |
- to this. |
- * |
- * @return {Column} Instance of the Column class. |
- **/ |
- function Column(key, ctype, ctitle, ftype, foptions, baseUrl) { |
- this.key = key; |
- this.ctype = ctype; |
- this.ctitle = ctitle; |
- this.ftype = ftype; |
- this.foptions = foptions; |
- this.baseUrl = baseUrl; |
- this.foptionsArr = []; |
- |
- // get the array of filter options for lookup in indexOfOptVal |
- if (this.foptions) { |
- for(var i=0, len=foptions.length; i<len; i++) { |
- this.foptionsArr.push(this.foptions[i].value); |
- } |
- } |
- } |
- |
- /** |
- * Find the index of an value in a column with a fixed set |
- * of options. |
- * |
- * @param {string} optVal Value of the column. |
- * |
- * @return {int} Index of optVal in this column. |
- **/ |
- Column.prototype.indexOfOptVal = function (optVal) { |
- return this.foptionsArr.indexOf(optVal); |
- }; |
- |
- /** |
- * Set filter options for this column. |
- * |
- * @param {FilterOpt[]} foptions Possible values for this column. |
- **/ |
- Column.prototype.setFilterOptions = function (foptions) { |
- this.foptions = foptions; |
- }; |
- |
- /** |
- * Factory function to create a filter column. Same args as Column() |
- **/ |
- Column.filter = function(key, ctitle, ftype, foptions) { |
- return new Column(key, c.COL_T_FILTER, ctitle || key, ftype, foptions); |
- } |
- |
- /** |
- * Factory function to create an image column. Same args as Column() |
- **/ |
- Column.image = function (key, ctitle, baseUrl) { |
- return new Column(key, c.COL_T_IMAGE, ctitle || key, null, null, baseUrl); |
- }; |
- |
- /** |
- * Factory function to create a regular column. Same args as Column() |
- **/ |
- Column.regular = function (key, ctitle) { |
- return new Column(key, c.COL_T_REGULAR, ctitle || key); |
- }; |
- |
- /** |
- * Helper class to wrap a single option in a filter. |
- * |
- * @param {string} value Option value. |
- * @param {int} count Number of instances of this option in the dataset. |
- * |
- * @return {} Instance of FiltertOpt |
- **/ |
- function FilterOpt(value, count) { |
- this.value = value; |
- this.count = count; |
- } |
- |
- /** |
- * Container for a single row in the dataset. |
- * |
- * @param {int} rowspan Number of rows (including this and following rows) |
- that have identical values. |
- * @param {string[]} dataCols Values of the respective columns (combination |
- of filter and regular columns) |
- * @param {ImgVal[]} imageCols Image meta data for the image columns. |
- * |
- * @return {DataRow} Instance of DataRow. |
- **/ |
- function DataRow(rowspan, dataCols, imageCols) { |
- this.rowspan = rowspan; |
- this.dataCols = dataCols; |
- this.imageCols = imageCols; |
- } |
- |
- /** |
- * Gets the comparator functions for the columns in this dataset. |
- * The comparators are then used to sort the dataset by the respective |
- * column. |
- * |
- * @param {Column[]} dataCols Data columns (= non-image columns) |
- * @param {Column[]} imgCols Image columns. |
- * |
- * @return {Function[]} Array of functions that can be used to sort by the |
- * respective column. |
- **/ |
- DataRow.getCompareFunctions = function (dataCols, imgCols) { |
- var result = []; |
- for(var i=0, len=dataCols.length; i<len; i++) { |
- result.push(( function (col, idx) { |
- return function (a, b) { |
- return (a.dataCols[idx] < b.dataCols[idx]) ? -1 : |
- ((a.dataCols[idx] === b.dataCols[idx]) ? 0 : 1); |
- }; |
- }(dataCols[i], i) )); |
- } |
- |
- for(var i=0, len=imgCols.length; i<len; i++) { |
- result.push((function (col, idx) { |
- return function (a,b) { |
- var aVal = a.imageCols[idx].percent; |
- var bVal = b.imageCols[idx].percent; |
- |
- return (aVal < bVal) ? -1 : ((aVal === bVal) ? 0 : 1); |
- }; |
- }(imgCols[i], i) )); |
- } |
- |
- return result; |
- }; |
- |
- /** |
- * Set the rowspan values of a given array of DataRow instances. |
- * |
- * @param {DataRow[]} data Dataset in desired order (after sorting). |
- * @param {mergeRows} mergeRows Indicate whether to sort |
- **/ |
- DataRow.setRowspanValues = function (data, mergeRows) { |
- var curIdx, rowspan, cur; |
- if (mergeRows) { |
- for(var i=0, len=data.length; i<len;) { |
- curIdx = i; |
- cur = data[i]; |
- rowspan = 1; |
- for(i++; ((i<len) && (data[i].dataCols === cur.dataCols)); i++) { |
- rowspan++; |
- data[i].rowspan=0; |
- } |
- data[curIdx].rowspan = rowspan; |
- } |
- } else { |
- for(var i=0, len=data.length; i<len; i++) { |
- data[i].rowspan = 1; |
- } |
- } |
- }; |
- |
- /** |
- * Wrapper class for image related data. |
- * |
- * @param {string} url Relative Url of the image or null if not available. |
- * @param {float} percent Percent of pixels that are differing. |
- * @param {int} value Absolute number of pixes differing. |
- * |
- * @return {ImgVal} Instance of ImgVal. |
- **/ |
- function ImgVal(url, percent, value) { |
- this.url = url; |
- this.percent = percent; |
- this.value = value; |
- } |
- |
- /** |
- * Extracts the filter columns from the JSON response of the server. |
- * |
- * @param {object} data Server response. |
- * |
- * @return {Column[]} List of filter columns as described in 'header' field. |
- **/ |
- function getFilterColumns(data) { |
- var result = []; |
- var vals = []; |
- var colOrder = data.extraColumnOrder; |
- var colHeaders = data.extraColumnHeaders; |
- var fopts, optVals, val; |
- |
- for(var i=0, len=colOrder.length; i<len; i++) { |
- if (colHeaders[colOrder[i]].isFilterable) { |
- if (colHeaders[colOrder[i]].useFreeformFilter) { |
- result.push(Column.filter(colOrder[i], |
- colHeaders[colOrder[i]].headerText, |
- c.FILTER_FREE_FORM)); |
- vals.push(''); |
- } |
- else { |
- fopts = []; |
- optVals = []; |
- |
- // extract the different options for this column |
- for(var j=0, jlen=colHeaders[colOrder[i]].valuesAndCounts.length; |
- j<jlen; j++) { |
- val = colHeaders[colOrder[i]].valuesAndCounts[j]; |
- fopts.push(new FilterOpt(val[0], val[1])); |
- optVals.push(false); |
- } |
- |
- // ad the column and values |
- result.push(Column.filter(colOrder[i], |
- colHeaders[colOrder[i]].headerText, |
- c.FILTER_CHECK_BOX, |
- fopts)); |
- vals.push(optVals); |
- } |
- } |
- } |
- |
- return [result, vals]; |
- } |
- |
- /** |
- * Extracts the image columns from the JSON response of the server. |
- * |
- * @param {object} data Server response. |
- * |
- * @return {Column[]} List of images columns as described in 'header' field. |
- **/ |
- function getImageColumns(data) { |
- var CO = c.IMG_COL_ORDER; |
- var imgSet; |
- var result = []; |
- for(var i=0, len=CO.length; i<len; i++) { |
- imgSet = data.imageSets[CO[i].key]; |
- result.push(Column.image(CO[i].key, |
- imgSet.description, |
- ensureTrailingSlash(imgSet.baseUrl))); |
- } |
- return result; |
- } |
- |
- /** |
- * Make sure Url has a trailing '/'. |
- * |
- * @param {string} url Base url. |
- * @return {string} Same url with a trailing '/' or same as input if it |
- already contained '/'. |
- **/ |
- function ensureTrailingSlash(url) { |
- var result = url.trim(); |
- |
- // TODO: remove !!! |
- result = fixUrl(url); |
- if (result[result.length-1] !== '/') { |
- result += '/'; |
- } |
- return result; |
- } |
- |
- // TODO: remove. The backend should provide absoute URLs |
- function fixUrl(url) { |
- url = url.trim(); |
- if ('http' === url.substr(0, 4)) { |
- return url; |
- } |
- |
- var idx = url.indexOf('static'); |
- if (idx != -1) { |
- return '/' + url.substr(idx); |
- } |
- |
- return url; |
- }; |
- |
- /** |
- * Processes that data and returns filter functions. |
- * |
- * @param {object} Server response. |
- * @param {Column[]} filterCols Filter columns. |
- * @param {Column[]} otherCols Columns that are neither filters nor images. |
- * @param {Column[]} imageCols Image columns. |
- * |
- * @return {[]} Returns a pair [dataRows, filterFunctions] where: |
- * - dataRows is an array of DataRow instances. |
- * - filterFunctions is an array of functions that can be used to |
- * filter the column at the corresponding index. |
- * |
- **/ |
- function getDataAndFilters(data, filterCols, otherCols, imageCols) { |
- var el; |
- var result = []; |
- var lookupIndices = []; |
- var indexerFuncs = []; |
- var temp; |
- |
- // initialize the lookupIndices |
- var filterFuncs = initIndices(filterCols, lookupIndices, indexerFuncs); |
- |
- // iterate over the data and get the rows |
- for(var i=0, len=data.imagePairs.length; i<len; i++) { |
- el = data.imagePairs[i]; |
- temp = new DataRow(1, getColValues(el, filterCols, otherCols), |
- getImageValues(el, imageCols)); |
- result.push(temp); |
- |
- // index the row |
- for(var j=0, jlen=filterCols.length; j < jlen; j++) { |
- indexerFuncs[j](lookupIndices[j], filterCols[j], temp.dataCols[j], i); |
- } |
- } |
- |
- setFreeFormFilterOptions(filterCols, lookupIndices); |
- return [result, filterFuncs]; |
- } |
- |
- /** |
- * Initiazile the lookup indices and indexer functions for the filter |
- * columns. |
- * |
- * @param {Column} filterCols Filter columns |
- * @param {[]} lookupIndices Will be filled with datastructures for |
- fast lookup (output parameter) |
- * @param {[]} lookupIndices Will be filled with functions to index data |
- of the column with the corresponding column. |
- * |
- * @return {[]} Returns an array of filter functions that can be used to |
- filter the respective column. |
- **/ |
- function initIndices(filterCols, lookupIndices, indexerFuncs) { |
- var filterFuncs = []; |
- var temp; |
- |
- for(var i=0, len=filterCols.length; i<len; i++) { |
- if (filterCols[i].ftype === c.FILTER_FREE_FORM) { |
- lookupIndices.push({}); |
- indexerFuncs.push(indexFreeFormValue); |
- filterFuncs.push( |
- getFreeFormFilterFunc(lookupIndices[lookupIndices.length-1])); |
- } |
- else if (filterCols[i].ftype === c.FILTER_CHECK_BOX) { |
- temp = []; |
- for(var j=0, jlen=filterCols[i].foptions.length; j<jlen; j++) { |
- temp.push([]); |
- } |
- lookupIndices.push(temp); |
- indexerFuncs.push(indexDiscreteValue); |
- filterFuncs.push( |
- getDiscreteFilterFunc(lookupIndices[lookupIndices.length-1])); |
- } |
- } |
- |
- return filterFuncs; |
- } |
- |
- /** |
- * Helper function that extracts the values of free form columns from |
- * the lookupIndex and injects them into the Column object as FilterOpt |
- * objects. |
- **/ |
- function setFreeFormFilterOptions(filterCols, lookupIndices) { |
- var temp, k; |
- for(var i=0, len=filterCols.length; i<len; i++) { |
- if (filterCols[i].ftype === c.FILTER_FREE_FORM) { |
- temp = [] |
- for(k in lookupIndices[i]) { |
- if (lookupIndices[i].hasOwnProperty(k)) { |
- temp.push(new FilterOpt(k, lookupIndices[i][k].length)); |
- } |
- } |
- filterCols[i].setFilterOptions(temp); |
- } |
- } |
- } |
- |
- /** |
- * Index a discrete column (column with fixed number of values). |
- * |
- **/ |
- function indexDiscreteValue(lookupIndex, col, dataVal, dataRowIndex) { |
- var i = col.indexOfOptVal(dataVal); |
- lookupIndex[i].push(dataRowIndex); |
- } |
- |
- /** |
- * Index a column with free form text (= not fixed upfront) |
- * |
- **/ |
- function indexFreeFormValue(lookupIndex, col, dataVal, dataRowIndex) { |
- if (!lookupIndex[dataVal]) { |
- lookupIndex[dataVal] = []; |
- } |
- lookupIndex[dataVal].push(dataRowIndex); |
- } |
- |
- |
- /** |
- * Get the function to filter a column with the given lookup index |
- * for discrete (fixed upfront) values. |
- * |
- **/ |
- function getDiscreteFilterFunc(lookupIndex) { |
- return function(filterVal) { |
- var result = []; |
- for(var i=0, len=lookupIndex.length; i < len; i++) { |
- if (filterVal[i]) { |
- // append the indices to the current array |
- result.push.apply(result, lookupIndex[i]); |
- } |
- } |
- return { nofilter: false, records: result }; |
- }; |
- } |
- |
- /** |
- * Get the function to filter a column with the given lookup index |
- * for free form values. |
- * |
- **/ |
- function getFreeFormFilterFunc(lookupIndex) { |
- return function(filterVal) { |
- filterVal = filterVal.trim(); |
- if (filterVal === '') { |
- return { nofilter: true }; |
- } |
- return { |
- nofilter: false, |
- records: lookupIndex[filterVal] || [] |
- }; |
- }; |
- } |
- |
- /** |
- * Filters the data based on the given filterColumns and |
- * corresponding filter values. |
- * |
- * @return {[]} Subset of the input dataset based on the |
- * filter values. |
- **/ |
- function filterData(data, filterFuncs, filterVals) { |
- var recordSets = []; |
- var filterResult; |
- |
- // run through all the filters |
- for(var i=0, len=filterFuncs.length; i<len; i++) { |
- filterResult = filterFuncs[i](filterVals[i]); |
- if (!filterResult.nofilter) { |
- recordSets.push(filterResult.records); |
- } |
- } |
- |
- // If there are no restrictions then return the whole dataset. |
- if (recordSets.length === 0) { |
- return data; |
- } |
- |
- // intersect the records returned by filters. |
- var targets = intersectArrs(recordSets); |
- var result = []; |
- for(var i=0, len=targets.length; i<len; i++) { |
- result.push(data[targets[i]]); |
- } |
- |
- return result; |
- } |
- |
- /** |
- * Creates an object where the keys are the elements of the input array |
- * and the values are true. To be used for set operations with integer. |
- **/ |
- function arrToObj(arr) { |
- var o = {}; |
- var i,len; |
- for(i=0, len=arr.length; i<len; i++) { |
- o[arr[i]] = true; |
- } |
- return o; |
- } |
- |
- /** |
- * Converts the keys of an object to an array after converting |
- * each key to integer. To be used for set operations with integers. |
- **/ |
- function objToArr(obj) { |
- var result = []; |
- for(var k in obj) { |
- if (obj.hasOwnProperty(k)) { |
- result.push(parseInt(k)); |
- } |
- } |
- return result; |
- } |
- |
- /** |
- * Find the intersection of a set of arrays. |
- **/ |
- function intersectArrs(sets) { |
- var temp, obj; |
- |
- if (sets.length === 1) { |
- return sets[0]; |
- } |
- |
- // sort by size and load the smallest into the object |
- sets.sort(function(a,b) { return a.length - b.length; }); |
- obj = arrToObj(sets[0]); |
- |
- // shrink the hash as we fail to find elements in the other sets |
- for(var i=1, len=sets.length; i<len; i++) { |
- temp = arrToObj(sets[i]); |
- for(var k in obj) { |
- if (obj.hasOwnProperty(k) && !temp[k]) { |
- delete obj[k]; |
- } |
- } |
- } |
- |
- return objToArr(obj); |
- } |
- |
- /** |
- * Extract the column values from an ImagePair (contained in the server |
- * response) into filter and data columns. |
- * |
- * @return {[]} Array of data contained in one data row. |
- **/ |
- function getColValues(imagePair, filterCols, otherCols) { |
- var result = []; |
- for(var i=0, len=filterCols.length; i<len; i++) { |
- result.push(imagePair.extraColumns[filterCols[i].key]); |
- } |
- |
- for(var i=0, len=otherCols.length; i<len; i++) { |
- result.push(get_robust(imagePair, ['expectations', otherCols[i].key])); |
- } |
- |
- return result; |
- } |
- |
- /** |
- * Extract the image meta data from an Image pair returned by the server. |
- **/ |
- function getImageValues(imagePair, imageCols) { |
- var result=[]; |
- var url, value, percent, diff; |
- var CO = c.IMG_COL_ORDER; |
- |
- for(var i=0, len=imageCols.length; i<len; i++) { |
- percent = get_robust(imagePair, CO[i].percentField); |
- value = get_robust(imagePair, CO[i].valueField); |
- url = get_robust(imagePair, CO[i].urlField); |
- if (url) { |
- url = imageCols[i].baseUrl + url; |
- } |
- result.push(new ImgVal(url, percent, value)); |
- } |
- |
- return result; |
- } |
- |
- /** |
- * Given an object find sub objects for the given index without |
- * throwing an error if any of the sub objects do not exist. |
- **/ |
- function get_robust(obj, idx) { |
- if (!idx) { |
- return; |
- } |
- |
- for(var i=0, len=idx.length; i<len; i++) { |
- if ((typeof obj === 'undefined') || (!idx[i])) { |
- return; // returns 'undefined' |
- } |
- |
- obj = obj[idx[i]]; |
- } |
- |
- return obj; |
- } |
- |
- /** |
- * Set all elements in the array to the given value. |
- **/ |
- function setArrVals(arr, newVal) { |
- for(var i=0, len=arr.length; i<len; i++) { |
- arr[i] = newVal; |
- } |
- } |
- |
- /** |
- * Toggle the elements of a boolean array. |
- * |
- **/ |
- function toggleArrVals(arr) { |
- for(var i=0, len=arr.length; i<len; i++) { |
- arr[i] = !arr[i]; |
- } |
- } |
- |
- /** |
- * Sort the array of DataRow instances with the given compare functions |
- * and the column at the given index either in ascending or descending order. |
- **/ |
- function sortData (allData, compareFunctions, sortByIdx, sortOrderAsc) { |
- var cmpFn = compareFunctions[sortByIdx]; |
- var useCmp = cmpFn; |
- if (!sortOrderAsc) { |
- useCmp = function ( _ ) { |
- return -cmpFn.apply(this, arguments); |
- }; |
- } |
- allData.sort(useCmp); |
- } |
- |
- |
- // ***************************** Services ********************************* |
- |
- /** |
- * Encapsulates all interactions with the backend by handling |
- * Urls and HTTP requests. Also exposes some utility functions |
- * related to processing Urls. |
- */ |
- app.factory('dataService', [ '$http', function ($http) { |
- /** Backend related constants **/ |
- var c = { |
- /** Url to retrieve failures */ |
- FAILURES: '/results/failures', |
- |
- /** Url to retrieve all GM results */ |
- ALL: '/results/all' |
- }; |
- |
- /** |
- * Convenience function to retrieve all results. |
- * |
- * @return {Promise} Will resolve to either the data (success) or to |
- * the HTTP response (error). |
- **/ |
- function loadAll() { |
- return httpGetData(c.ALL); |
- } |
- |
- /** |
- * Make a HTTP get request with the given query parameters. |
- * |
- * @param {} |
- * @param {} |
- * |
- * @return {} |
- **/ |
- function httpGetData(url, queryParams) { |
- var reqConfig = { |
- method: 'GET', |
- url: url, |
- params: queryParams |
- }; |
- |
- return $http(reqConfig).then( |
- function(successResp) { |
- return successResp.data; |
- }); |
- } |
- |
- /** |
- * Takes an arbitrary number of objects and generates a Url encoded |
- * query string. |
- * |
- **/ |
- function getQueryString( _params_ ) { |
- var result = []; |
- for(var i=0, len=arguments.length; i < len; i++) { |
- if (arguments[i]) { |
- for(var k in arguments[i]) { |
- if (arguments[i].hasOwnProperty(k)) { |
- result.push(encodeURIComponent(k) + '=' + |
- encodeURIComponent(arguments[i][k])); |
- } |
- } |
- } |
- } |
- return result.join("&"); |
- } |
- |
- // Interface of the service: |
- return { |
- getQueryString: getQueryString, |
- loadAll: loadAll |
- }; |
- |
- }]); |
- |
-})(); |