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