1 /*
  2  * Copyright (c) 2012-2016 LabKey Corporation
  3  *
  4  * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
  5  */
  6 
  7 // Contains helpers that aren't specific to plot, layer, geom, etc. and are used throughout the API.
  8 
  9 if(!LABKEY){
 10 	var LABKEY = {};
 11 }
 12 
 13 if(!LABKEY.vis){
 14     /**
 15      * @namespace The namespace for the internal LabKey visualization library. Contains classes within
 16      * {@link LABKEY.vis.Plot}, {@link LABKEY.vis.Layer}, and {@link LABKEY.vis.Geom}.
 17      */
 18 	LABKEY.vis = {};
 19 }
 20 
 21 LABKEY.vis.makeLine = function(x1, y1, x2, y2){
 22     //Generates a path between two coordinates.
 23     return "M " + x1 + " " + y1 + " L " + x2 + " " + y2;
 24 };
 25 
 26 LABKEY.vis.makePath = function(data, xAccessor, yAccessor){
 27     var pathString = '';
 28 
 29     for(var i = 0; i < data.length; i++){
 30         var x = xAccessor(data[i]);
 31         var y = yAccessor(data[i]);
 32         if(!LABKEY.vis.isValid(x) || !LABKEY.vis.isValid(y)){
 33             continue;
 34         }
 35         
 36         if(pathString == ''){
 37             pathString = pathString + 'M' + x + ' ' + y;
 38         } else {
 39             pathString = pathString + ' L' + x + ' ' + y;
 40         }
 41     }
 42     return pathString;
 43 };
 44 
 45 LABKEY.vis.createGetter = function(aes){
 46     if(typeof aes.value === 'function'){
 47         aes.getValue = aes.value;
 48     } else {
 49         aes.getValue = function(row){
 50             if(row instanceof Array) {
 51                 /*
 52                  * For Path geoms we pass in the entire array of values for the path to the aesthetic. So if the user
 53                  * provides only a string for an Aes value we'll assume they want the first object in the path array to
 54                  * determing the value.
 55                 */
 56                 if(row.length > 0) {
 57                     row = row[0];
 58                 } else {
 59                     return null;
 60                 }
 61             }
 62             return row[aes.value];
 63         };
 64     }
 65 };
 66 
 67 LABKEY.vis.convertAes = function(aes){
 68     var newAes= {};
 69     for(var aesthetic in aes){
 70         var newAesName = (aesthetic == 'y') ? 'yLeft' : aesthetic;
 71         newAes[newAesName] = {};
 72         newAes[newAesName].value = aes[aesthetic];
 73     }
 74     return newAes;
 75 };
 76 
 77 LABKEY.vis.mergeAes = function(oldAes, newAes) {
 78     newAes = LABKEY.vis.convertAes(newAes);
 79     for(var attr in newAes) {
 80         if(newAes.hasOwnProperty(attr)) {
 81             if (newAes[attr].value != null) {
 82                 LABKEY.vis.createGetter(newAes[attr]);
 83                 oldAes[attr] = newAes[attr];
 84             } else {
 85                 delete oldAes[attr];
 86             }
 87         }
 88     }
 89 };
 90 
 91 LABKEY.vis.groupData = function(data, groupAccessor){
 92     /*
 93         Groups data by the groupAccessor passed in.
 94         Ex: A set of rows with participantIds in them, would return an object that has one attribute
 95          per participant id. Each attribute will be an array of all of the rows the participant is in.
 96      */
 97     var groupedData = {};
 98     for(var i = 0; i < data.length; i++){
 99         var value = groupAccessor(data[i]);
100         if(!groupedData[value]){
101             groupedData[value] = [];
102         }
103         groupedData[value].push(data[i]);
104     }
105     return groupedData;
106 };
107 
108 LABKEY.vis.groupCountData = function(data, groupAccessor, propNameMap){
109     /*
110         Groups data by the groupAccessor passed in and returns the number of occurances for that group.
111         Most commonly used for processing data for a bar plot.
112      */
113     var groupName, groupedData, count, counts = [], total = 0;
114 
115     groupedData = LABKEY.vis.groupData(data, groupAccessor);
116 
117     for (groupName in groupedData)
118     {
119         if (groupedData.hasOwnProperty(groupName))
120         {
121             count = groupedData[groupName].length;
122             total += count;
123 
124             var row = {rawData: groupedData[groupName]};
125             row[propNameMap && propNameMap.name ? propNameMap.name : 'name'] = groupName;
126             row[propNameMap && propNameMap.count ? propNameMap.count : 'count'] = count;
127             row[propNameMap && propNameMap.total ? propNameMap.total : 'total'] = total;
128             counts.push(row);
129         }
130     }
131 
132     return counts;
133 };
134 
135 LABKEY.vis.getColumnAlias = function(aliasArray, measureInfo) {
136     /*
137      Lookup the column alias (from the getData response) by the specified measure information
138      aliasArray: columnAlias array from the getData API response
139      measureInfo: 1. a string with the name of the column to lookup
140                   2. an object with a measure alias OR measureName
141                  3. an object with both measureName AND pivotValue
142     */
143     if (!aliasArray)
144         aliasArray = [];
145 
146     if (typeof measureInfo != "object")
147         measureInfo = {measureName: measureInfo};
148     for (var i = 0; i < aliasArray.length; i++)
149     {
150         var arrVal = aliasArray[i];
151 
152         if (measureInfo.measureName && measureInfo.pivotValue)
153         {
154             if (arrVal.measureName == measureInfo.measureName && arrVal.pivotValue == measureInfo.pivotValue)
155                 return arrVal.columnName;
156         }
157         else if (measureInfo.alias)
158         {
159             if (arrVal.alias == measureInfo.alias)
160                 return arrVal.columnName;
161         }
162         else if (measureInfo.measureName && arrVal.measureName == measureInfo.measureName)
163             return arrVal.columnName;
164     }
165     return null;
166 };
167 
168 LABKEY.vis.isValid = function(value) {
169     return !(value == undefined || value == null || (typeof value == "number" && !isFinite(value)));
170 };
171 
172 LABKEY.vis.arrayObjectIndexOf = function(myArray, searchTerm, property) {
173     for (var i = 0; i < myArray.length; i++) {
174         if (myArray[i][property] === searchTerm) return i;
175     }
176     return -1;
177 };
178 
179 LABKEY.vis.discreteSortFn = function(a,b) {
180     // Issue 23015: sort categorical x-axis alphabetically with special case for "Not in X" and "[Blank]"
181     var aIsEmptyCategory = a && (a.indexOf("Not in ") == 0 || a == '[Blank]'),
182         bIsEmptyCategory = b && (b.indexOf("Not in ") == 0 || b == '[Blank]');
183 
184     if (aIsEmptyCategory)
185         return 1;
186     else if (bIsEmptyCategory)
187         return -1;
188     else if (a != b)
189         return a < b ? -1 : 1;
190 
191     return 0;
192 };
193