-
Notifications
You must be signed in to change notification settings - Fork 0
/
code.gs
322 lines (286 loc) · 10.6 KB
/
code.gs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
var dialogMsg = ""
function getDialogMsg(){
return dialogMsg
}
function onOpen(e) {
SpreadsheetApp.getUi()
.createAddonMenu()
.addItem('Convert Columns to Named Ranges', 'NameRangesCols')
.addItem('Convert Rows to Named Ranges', 'NameRangesRows')
.addItem('Conditionally Format selection one column at a time', 'ColourCols')
.addItem('Conditionally Format selection one row at a time', 'ColourRows')
.addSubMenu(SpreadsheetApp.getUi().createMenu('Flatten')
.addItem('Flatten Sheet (has header row)', 'FlattenWithHeader')
.addItem('Flatten Sheet (no header row)', 'FlattenNoHeader')
)
.addSubMenu(SpreadsheetApp.getUi().createMenu('Sort Horizontally')
.addItem('Sort Sheet Horizontally A-Z', 'SortSheetHorizontallyAZ')
.addItem('Sort Sheet Horizontally Z-A', 'SortSheetHorizontallyZA')
.addItem('Sort Range Horizontally A-Z', 'SortRangeHorizontallyAZ')
.addItem('Sort Range Horizontally Z-A', 'SortRangeHorizontallyZA')
)
.addToUi();
}
function onInstall(e) {
onOpen(e);
}
//function menuLink(){
//showURL("http://www.google.com")
//}
//
//function showURL(href){
// var app = UiApp.createApplication().setHeight(500).setWidth(500);
// app.setTitle("Open Link");
// var link = app.createAnchor('Google.com ', href).setId("link");
// app.add(link);
// var doc = SpreadsheetApp.getActive();
// doc.show(app);
// }
var OPTYPE = {COLS: 0, ROWS: 1}
function NameRangesCols(){
return NameRanges(OPTYPE.COLS)
}
function NameRangesRows(){
return NameRanges(OPTYPE.ROWS)
}
function NameRanges(type) {
var ui = SpreadsheetApp.getUi();
var ss = SpreadsheetApp.getActiveSpreadsheet()
var rangeList = ss.getActiveRangeList()
var ranges = rangeList.getRanges()
if(type != OPTYPE.ROWS && type != OPTYPE.COLS){
throw("Invalid type for NameRanges()")
}
if(rangeList == null || ranges.length == 0 || (ranges.length == 1 && ranges[0].getNumColumns() == 1 && ranges[0].getNumRows() == 1)){ //check if anything has been selected
ui.alert(
'Please select some cells to convert to named ranges.',
ui.ButtonSet.OK)
return
} else { //something has been selected
switch(type){
case OPTYPE.COLS: //col
dialogMsg = 'This will convert each selected Column to a Named Range, using the top cell as the name. Any named ranges with the same name will be over-written. Are you sure you want to continue?'
break
case OPTYPE.ROWS: //row
dialogMsg = 'This will convert each selected Row to a Named Range, using the left cell as the name. Any named ranges with the same name will be over-written. Are you sure you want to continue?'
break
}
var result = ui.alert( //check the user wants to do this thing
'Continue?',
dialogMsg,
ui.ButtonSet.YES_NO);
if (result == ui.Button.YES) { //user wants to do this thing
for (var i = 0; i < ranges.length; i++) {
switch(type){
case OPTYPE.COLS://col
var num_blocks = ranges[i].getNumColumns()
break
case OPTYPE.ROWS://row
var num_blocks = ranges[i].getNumRows()
break
}
for(var j=0; j<num_blocks; j++) {
switch(type){
case OPTYPE.COLS://col
var block = ranges[i].offset(1,j,ranges[i].getNumRows()-1, 1)
var name = ranges[i].offset(0,j).getValue()
break
case OPTYPE.ROWS://row
var block = ranges[i].offset(j,1,1,ranges[i].getNumColumns()-1)
var name = ranges[i].offset(j,0).getValue()
break
}
// clean up name to be a valid NamedRange name
name = String(name) //ensure it's a string not a number
.replace(/\s/g,"_") //replace spaces with _
.replace(/[^0-9a-zA-Z_]/g,"") //get rid of non alphanum or _
.replace(/^([0-9])/,"_$1") // if starts with number, put underscore in front
.replace(/^(true|false)/i,"_$1") //if starts with true/false, put underscore in front
if(name == ""){ //skip over empty named columns
continue;
}
ss.setNamedRange(name, block)
}
}
}
}
}
//find the conditional formatting rules applied to a given range
function getCondFmtRulesForRange(range, sheet){
var condFmtRules = sheet.getConditionalFormatRules()
var rulesForRange = []
for (var i = 0; i < condFmtRules.length; i++) {
var rulesRanges = condFmtRules[i].getRanges()
for(var j=0; j<rulesRanges.length; j++){
//check if the range is wholly within a conditional formatting rule
if(range.getColumn() >= rulesRanges[j].getColumn() && range.getLastColumn() <= rulesRanges[j].getLastColumn()) {
if(range.getRow() >= rulesRanges[j].getRow() && range.getLastRow() <= rulesRanges[j].getLastRow()) {
rulesForRange.push(condFmtRules[i])
}
}
}
}
return rulesForRange
}
function ColourCols() {
return ColourRanges(OPTYPE.COLS)
}
function ColourRows() {
return ColourRanges(OPTYPE.ROWS)
}
function ColourRanges(type) {
var ui = SpreadsheetApp.getUi();
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getActiveSheet()
var conditionalFormatRules = sheet.getConditionalFormatRules();
var rangeList = ss.getActiveRangeList()
var ranges = rangeList.getRanges()
if(rangeList == null || ranges.length == 0 || (ranges.length == 1 && ranges[0].getNumColumns() == 1 && ranges[0].getNumRows() == 1)){ //check if anything has been selected
ui.alert(
'Please select some cells to format.',
ui.ButtonSet.OK)
return
} else { //something has been selected
switch(type){
case OPTYPE.COLS: //col
dialogMsg = 'This will conditionally format the selection column-by-column based on the rules set for the first column. Are you sure you want to continue?'
break
case OPTYPE.ROWS: //row
dialogMsg = 'This will conditionally format the selection row-by-row based on the rules set for the first row. Are you sure you want to continue?'
break
}
var result = ui.alert( //check the user wants to do this thing
'Continue?',
dialogMsg,
ui.ButtonSet.YES_NO);
if (result == ui.Button.YES) { //user wants to do this thing
switch(type){
case OPTYPE.COLS:
var rules = getCondFmtRulesForRange(ranges[0].offset(0,0,ranges[0].getNumRows(),1), sheet)
break
case OPTYPE.ROWS:
var rules = getCondFmtRulesForRange(ranges[0].offset(0,0,1, ranges[0].getNumColumns()), sheet)
break
}
if(rules.length == 0){
ui.alert("No conditional formatting has been set for the first row/column of your selection. Please set conditional formatting for the first column of you selection then try again", ui.ButtonSet.OK)
return
}
for (var i = 0; i < ranges.length; i++) {
switch(type){
case OPTYPE.COLS:
var numrowscols = ranges[i].getNumColumns()
break
case OPTYPE.ROWS:
var numrowscols = ranges[i].getNumRows()
break
}
for(var j=0; j<numrowscols; j++) {
if(i==0 && j==0){
continue //skip first row/column as that's our source of formatting; no need to apply it back to itself
}
switch(type){
case OPTYPE.COLS:
var range = ranges[i].offset(0,j,ranges[i].getNumRows(), 1)
break
case OPTYPE.ROWS:
var range = ranges[i].offset(j,0,1, ranges[i].getNumColumns())
break
}
for(var k=0; k<rules.length; k++){
var rulebuilder = rules[k].copy()
conditionalFormatRules.push(rulebuilder.setRanges([range]).build());
}
}
}
sheet.setConditionalFormatRules(conditionalFormatRules);
}
}
}
function SortSheetHorizontallyAZ(){
SortHorizontally('sheet', true)
}
function SortRangeHorizontallyAZ(){
SortHorizontally('range', true)
}
function SortSheetHorizontallyZA(){
SortHorizontally('sheet', false)
}
function SortRangeHorizontallyZA(){
SortHorizontally('range', false)
}
function SortHorizontally(type, order){
var ui = SpreadsheetApp.getUi();
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getActiveSheet()
var range = null
switch(type) {
case 'sheet':
range = sheet.getDataRange()
break;
case 'range':
range = sheet.getActiveRange()
break;
default:
throw("error: unknown sort type")
}
var response = ui.prompt("Sort by which row?", "Specify the absolute row number from the left side of the spreadsheet", ui.ButtonSet.OK_CANCEL)
if(response.getSelectedButton() == ui.Button.CANCEL) {
return
}
if(response.getResponseText().match(/^\d+$/)){
var sort_row = parseInt(response.getResponseText())
}else{
throw("invalid row entered")
}
sort_row = sort_row - range.getRow() + 1
if(sort_row < 1 || sort_row > range.getNumRows()){
throw("invalid row entered")
}
var tempSheetName = 'ColumnTools Temporary Sorting Sheet'
while(ss.getSheetByName(tempSheetName) != null){
tempSheetName += Math.floor(Math.random() * 9).toString()
}
var tempsheet = ss.insertSheet(tempSheetName);
range.copyTo(tempsheet.getRange("A1"), SpreadsheetApp.CopyPasteType.PASTE_NORMAL, true)
var temprange = tempsheet.getRange(1,1,range.getNumColumns(), range.getNumRows())
SpreadsheetApp.flush()
temprange.sort({column: sort_row, ascending: order})
temprange.copyTo(range, SpreadsheetApp.CopyPasteType.PASTE_NORMAL, true)
ss.deleteSheet(tempsheet)
ss.setActiveSheet(sheet, true) // go back to sheet and selection where we started
}
function FlattenWithHeader(){
return FlattenSheet(true)
}
function FlattenNoHeader(){
return FlattenSheet(false)
}
function FlattenSheet(hasHeader) {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getActiveSheet()
var dataRange = sheet.getDataRange()
var data = dataRange.getValues()
var output = []
if(hasHeader){
var header = data.shift().slice(0,2)
output.push(header)
}
for(var i in data){
var record_name = data[i][0]
var added_something = false
for(var j=1; j<data[i].length; j++){
if(data[i][j]){
output.push([record_name, data[i][j]])
added_something = true
}
}
if(! added_something){ //have a record with no data - just add the record name with a blank cell
output.push([record_name,""])
}
}
dataRange.clear()
dataRange = sheet.getRange(1,1,output.length,2)
dataRange.setNumberFormat('@STRING@')
dataRange.setValues(output)
}