|
| 1 | +/** |
| 2 | + * This function creates a new sheet called "Menus_1" that contains the |
| 3 | + * dropdown categories. Caution: running this will overwrite the |
| 4 | + * current version of Menus_1, this should be considered a temporary function |
| 5 | + */ |
| 6 | + |
| 7 | +function createNew() { |
| 8 | + var ss = SpreadsheetApp.getActiveSpreadsheet(); |
| 9 | + var dProps = PropertiesService.getDocumentProperties(); |
| 10 | + |
| 11 | + //TODO: make this array dynamic so that the user can enter as many |
| 12 | + //categories as they need |
| 13 | + var headArr = [ |
| 14 | + ["Cat_1","Cat_2","Cat_3","Cat_4","Cat_5"] |
| 15 | + ]; |
| 16 | + |
| 17 | + ss.insertSheet().setName("Menus_1"); |
| 18 | + |
| 19 | + var menSht = ss.getSheetByName("Menus_1"); |
| 20 | + var menRng = menSht.getRange("Menus_1!$A$1:$E$1"); |
| 21 | + menRng.setValues(headArr); |
| 22 | + |
| 23 | +} |
| 24 | + |
| 25 | +/** |
| 26 | + * This function takes range arguments from the 'createMenu' form as input and returns |
| 27 | + * a nested object using the nestObj() function. |
| 28 | + * |
| 29 | + * @obj - an object containing two strings as values. First string is the range where |
| 30 | + * the dropdown menu options are stored in hierarchical order, the second string is where the |
| 31 | + * |
| 32 | + */ |
| 33 | +function createDDs(obj){ |
| 34 | + var ss = SpreadsheetApp.getActiveSpreadsheet(); |
| 35 | + var dProps = PropertiesService.getDocumentProperties(); |
| 36 | + |
| 37 | + areDDs(); |
| 38 | + var ddObj = JSON.parse(dProps.getProperties()['dropdowns']); |
| 39 | + var count = countProp(ddObj); |
| 40 | + |
| 41 | + //TODO: error handling. If this throws an error, it means that the original objects were |
| 42 | + //misnnamed in the html document. This is probably not as essential since I don't see |
| 43 | + // a condition where this would actually happen. |
| 44 | + |
| 45 | + var optsRng = obj.optsRange; |
| 46 | + var ddsRng = obj.ddsRange; |
| 47 | + |
| 48 | + //TODO:if there are already dropdowns in the target range set in user properties, overwrite |
| 49 | + // the existing menus with the new one being declared. A structured error message might be |
| 50 | + //good to use here at some point, but I think the user can figure out if they made |
| 51 | + //drowpdon menus that overlap each other because it should cause some obvious problems |
| 52 | + //for them right away. |
| 53 | + |
| 54 | + ss.getRange("Sheet1!F2").setValue(JSON.stringify(ddObj)); |
| 55 | + ss.getRange("Sheet1!F3").setValue(count); |
| 56 | + |
| 57 | +} |
| 58 | + |
| 59 | +/** |
| 60 | + * This function checks if the 'dropdowns' object is present |
| 61 | + * in the document properties. If not, it creates one. Note that even though 'newObj' is |
| 62 | + * declared as an object in this script, Google's document properties seems to only be capable of storing |
| 63 | + * stringified JSON objects - hence the reason for JSON.parse() being used in the createDDs() function. |
| 64 | + */ |
| 65 | + |
| 66 | +function areDDs(){ |
| 67 | + var dProps = PropertiesService.getDocumentProperties(); |
| 68 | + var bool = (dProps.getProperties()['dropdowns'] == null); |
| 69 | + var newObj = {"dropdowns":{}}; |
| 70 | + |
| 71 | + //Logger.log(JSON.stringify(dProps)); |
| 72 | + //Logger.log(dProps.getProperties()['dropdowns']); |
| 73 | + //Logger.log('boolean: ' + bool); |
| 74 | + |
| 75 | + //if there is not a dropdown object, create one |
| 76 | + |
| 77 | + |
| 78 | + if(bool === true){ |
| 79 | + dProps.setProperties(newObj) |
| 80 | + } |
| 81 | + |
| 82 | + //Logger.log("updated boolean : "+ (dProps.getProperties()['dropdowns'] == null)) |
| 83 | + //Logger.log("getProperties : " + JSON.stringify(dProps.getProperties()['dropdowns'])); |
| 84 | + //Logger.log(dProps.getProperties.hasOwnProperty('dropdowns')); |
| 85 | + //Logger.log(JSON.stringify(dProps.getProperties()['dropdowns'])) |
| 86 | + |
| 87 | +} |
| 88 | + |
| 89 | +/** |
| 90 | + * A function that deletes all document properties |
| 91 | + */ |
| 92 | + |
| 93 | +function deleteDocProps(){ |
| 94 | + PropertiesService.getDocumentProperties().deleteAllProperties() |
| 95 | +} |
| 96 | + |
| 97 | +/** |
| 98 | + * Todo: create a function that will count the number of dropdown functions present in the document properties |
| 99 | + * The number given below is just a placeholder. |
| 100 | + */ |
| 101 | + |
| 102 | +function countProp(obj){ |
| 103 | + var res = Object.keys(obj).length; |
| 104 | + return res |
| 105 | + |
| 106 | +} |
| 107 | + |
| 108 | +/** |
| 109 | + * Todo: create a function that compares the dimensions of two arrays |
| 110 | + */ |
| 111 | + |
| 112 | +function compareRange(range1,range2){ |
| 113 | + |
| 114 | +} |
| 115 | + |
| 116 | +/** |
| 117 | + * This function takes an array argument and returns a JSON object containing sub objects (labeled tier_1, tier_2, etc.) that correspond |
| 118 | + * with the dropdown column tiers. |
| 119 | + * |
| 120 | + * @array - a one-dimensional array containing categories that will be |
| 121 | + * nested within the returned JSON object. This function contains a method for parsing individual columns of 2d arrays that was |
| 122 | + * pulled from: https://stackoverflow.com/questions/7848004/get-column-from-a-two-dimensional-array |
| 123 | + * |
| 124 | + * @array - the subject array of values |
| 125 | + * @headers - a true/false value indicating whether the array includes a single header row that should not be included in the dd |
| 126 | + * objects |
| 127 | + * |
| 128 | + */ |
| 129 | + |
| 130 | +function nestObj(array,header = true){ |
| 131 | + var obj = {}; |
| 132 | + var arr = array; |
| 133 | + |
| 134 | + //get the dimensions of the array as seperate variables |
| 135 | + var width = arr[0].length; |
| 136 | + var height = arr.length; |
| 137 | + |
| 138 | + //iterate over each column in the array in oreder to create "tiers" of dropdown menu objects |
| 139 | + //that correspond to the number of columns. |
| 140 | + for (var i = 0; i < width-1 ;i++){ |
| 141 | + var nestObj = {}; |
| 142 | + var key = "tier_" + (i+1).toString(); //note that the tiers are not 0-indexed |
| 143 | + var firstCol = arr.map(function(value,index) { return value[i]; }); |
| 144 | + var firstUnqVal = firstCol.filter(onlyUnique); |
| 145 | + var firstUnqLen = firstUnqVal.length; |
| 146 | + |
| 147 | + //iterate over just the unique objects extracted from the current column |
| 148 | + for (var j = 0; j < firstUnqLen; j++){ |
| 149 | + var jVal = firstUnqVal[j]; |
| 150 | + |
| 151 | + //create empty array to hold the nested values |
| 152 | + nestArr = []; |
| 153 | + path = []; |
| 154 | + |
| 155 | + //create array that serves as the value for the deepest tier of the current nested object. |
| 156 | + for (var k = 0; k < height; k++){ |
| 157 | + var nxtVal = arr[k][i+1]; |
| 158 | + var curVal = arr[k][i]; |
| 159 | + var rowArr = arr[k]; |
| 160 | + |
| 161 | + if(jVal == curVal && !nestArr.includes(nxtVal) && (!nxtVal == null || !nxtVal.length == 0 || !nxtVal.length == undefined)){ |
| 162 | + nestArr.push(nxtVal); |
| 163 | + var path = rowArr.slice(0,i+1); |
| 164 | + }; |
| 165 | + }; |
| 166 | + //create the path |
| 167 | + set(nestObj,path,nestArr); |
| 168 | + }; |
| 169 | + obj[key] = nestObj; |
| 170 | + }; |
| 171 | + return obj |
| 172 | +} |
| 173 | + |
| 174 | +/** |
| 175 | + * This accepts an object, an array, and a "val" - a last value. The second array argument gets converted into |
| 176 | + * a nested array, and the innermost object. This is based on a function available at https://stackoverflow.com/questions/5484673/javascript-how-to-dynamically-create-nested-objects-using-object-names-given-by |
| 177 | + * |
| 178 | + * @obj - object to have key value pairings added |
| 179 | + * @path - the path of the nested value as a one-dimensional array |
| 180 | + * @val - the value of the innermost key in the nested object |
| 181 | + */ |
| 182 | +const set = (obj, path, val) => { |
| 183 | + const keys = path; |
| 184 | + const lastKey = keys.pop(); |
| 185 | + const lastObj = keys.reduce((obj, key) => |
| 186 | + obj[key] = obj[key] || {}, |
| 187 | + obj); |
| 188 | + lastObj[lastKey] = val; |
| 189 | +} |
| 190 | + |
| 191 | +function openCreateForm(){ |
| 192 | + var modalForm = HtmlService.createHtmlOutputFromFile('createMenu'); |
| 193 | + SpreadsheetApp.getUi().showModalDialog(modalForm,'Create New Set of Cascading Dropdown Menus'); |
| 194 | +} |
| 195 | + |
0 commit comments