Files
vkservicesllc.github.io/projects/google-apps/export.js
2019-04-16 18:21:13 -04:00

1391 lines
41 KiB
JavaScript

/**
* Updated: 2019-04-16
*/
/** Form Entities **/
function Title_(value, row, column, height, width, horizontalAlignment, omitIndent) {
/*
* arguments: [value[, row[, column[, height[, width[, horizontalAlignment[, omitIndent]]]]]]]
*
* value -> string
* row -> integer (>0)
* column -> integer (>0)
* height -> integer (>0)
* width -> integer (>0)
* horizontalAlignment -> selection op string options "left", "right" and "center" to override existing setting
* omitIndent -> boolean, if true indent will not be applied
*
*/
return new RangeDescription(value, row, column, {
height: (height) ? height : undefined,
width: (width) ? width : undefined,
font: { family: 'Montserrat', size: 12, color: '#434343', weight: 'bold', line: 'underline' },
alignment: (horizontalAlignment) ? { horizontal: horizontalAlignment } : undefined,
indent: (omitIndent) ? undefined : { left: 2, right: 2 },
});
}
function HeaderMain_(value, row, column, height, width, horizontalAlignment, omitIndent) {
/*
* arguments: [value[, row[, column[, height[, width[, horizontalAlignment[, omitIndent]]]]]]]
*
* value -> string
* row -> integer (>0)
* column -> integer (>0)
* height -> integer (>0)
* width -> integer (>0)
* horizontalAlignment -> selection op string options "left", "right" and "center" to override existing setting
* omitIndent -> boolean, if true indent will not be applied
*
*/
return new RangeDescription(value, row, column, {
height: (height) ? height : undefined,
width: (width) ? width : undefined,
font: { family: 'Roboto', size: 11, color: '#cccccc' },
background: 'black',
border: {
top: true,
left: true,
bottom: true,
right: true,
color: Title_().font.color,
},
alignment: (horizontalAlignment) ? { horizontal: horizontalAlignment } : undefined,
indent: (omitIndent) ? undefined : { left: 2, right: 2 },
});
}
function Header_(value, row, column, height, width, horizontalAlignment, omitIndent) {
/*
* arguments: [value[, row[, column[, height[, width[, horizontalAlignment[, omitIndent]]]]]]]
*
* value -> string
* row -> integer (>0)
* column -> integer (>0)
* height -> integer (>0)
* width -> integer (>0)
* horizontalAlignment -> selection op string options "left", "right" and "center" to override existing setting
* omitIndent -> boolean, if true indent will not be applied
*
*/
return new RangeDescription(value, row, column, {
height: (height) ? height : undefined,
width: (width) ? width : undefined,
font: { family: 'Roboto', size: 10, color: '#434343', style: 'italic' },
border: {
top: true,
left: true,
bottom: true,
right: true,
color: Title_().font.color,
},
alignment: (horizontalAlignment) ? { horizontal: horizontalAlignment } : undefined,
indent: (omitIndent) ? undefined : { left: 2, right: 2 },
});
}
function Label_(value, required, row, column, height, width, omitIndent, applyBorder) {
/*
* arguments: value[, required[, row[, column[, height[, width[, omitIndent[, applyBorder]]]]]]]
*
* value -> string
* required -> boolean, determines if label points at required field
* row -> integer (>0)
* column -> integer (>0)
* height -> integer (>0)
* width -> integer (>0)
* omitIndent -> boolean, if true indent will not be applied
* applyBorder -> boolean, if true border will be applied
*
*/
if (value && typeof value == 'string') {
var fontColor = '#666666';
if (required) {
value += ' *';
fontColor = '#134f5c';
}
return new RangeDescription(value, row, column, {
height: (height) ? height : undefined,
width: (width) ? width : undefined,
wrapStrategy: (!height && !width && !applyBorder) ? SpreadsheetApp.WrapStrategy.OVERFLOW : undefined,
alignment: { horizontal: 'right' },
font: { family: 'Roboto', color: fontColor },
border: (applyBorder) ? {
top: true,
left: true,
bottom: true,
right: true,
color: Title_().font.color,
} : undefined,
indent: (omitIndent) ? undefined : { right: 2 },
});
}
}
function Cell_(value, row, column, height, width, numberFormat, horizontalAlignment, omitIndent) {
/*
* arguments: [value[, row[, column[, height[, width[, numberFormat[, horizontalAlignment[, omitIndent]]]]]]]]
*
* value -> any type
* row -> integer (>0)
* column -> integer (>0)
* height -> integer (>0)
* width -> integer (>0)
* numberFormat -> string reprsenting format pattern
* horizontalAlignment -> selection op string options "left", "right" and "center" to override existing setting
* omitIndent -> boolean, if true indent will not be applied
*
*/
return new RangeDescription(value, row, column, {
height: (height) ? height : undefined,
width: (width) ? width : undefined,
numberFormat: (numberFormat) ? numberFormat : undefined,
font: { family: 'Poppins', size: 11 },
border: {
top: true,
left: true,
bottom: true,
right: true,
color: Title_().font.color,
},
alignment: (horizontalAlignment) ? { horizontal: horizontalAlignment } : undefined,
indent: (omitIndent) ? undefined : { left: 2, right: 2 },
});
}
function InputRequired_(value, row, column, height, width, validation, numberFormat, horizontalAlignment, unprotected) {
/*
* arguments: [value [,row[, column[, height[, width[, validation[, numberFormat[, horizontalAlignment[, unprotected]]]]]]]]]
*
* value -> any type
* row -> integer (>0)
* column -> integer (>0)
* height -> integer (>0)
* width -> integer (>0)
* validation -> data validation object
* numberFormat -> string reprsenting format pattern
* horizontalAlignment -> selection op string options "left", "right" and "center" to override existing setting
* unprotected -> boolean, if false the range will be protected along with protected sheet
*
*/
if (unprotected == undefined) unprotected = true;
return new RangeDescription(value, row, column, {
tag: 'input',
height: (height) ? height : undefined,
width: (width) ? width : undefined,
numberFormat: (numberFormat) ? numberFormat : undefined,
font: { family: 'Poppins', size: 11, weight: 'bold' },
background: '#c9daf8',
border: {
top: true,
left: true,
bottom: true,
right: true,
color: Title_().font.color,
},
alignment: (horizontalAlignment) ? { horizontal: horizontalAlignment } : undefined,
validation: (validation) ? validation : undefined,
unprotected: unprotected,
});
}
function InputOptional_(value, row, column, height, width, validation, numberFormat, horizontalAlignment, unprotected) {
/*
* arguments: [value [,row[, column[, height[, width[, validation[, numberFormat[, horizontalAlignment[, unprotected]]]]]]]]]
*
* value -> any type
* row -> integer (>0)
* column -> integer (>0)
* height -> integer (>0)
* width -> integer (>0)
* validation -> data validation object
* numberFormat -> string reprsenting format pattern
* horizontalAlignment -> selection op string options "left", "right" and "center" to override existing setting
* unprotected -> boolean, if false the range will be protected along with protected sheet
*
*/
if (unprotected == undefined) unprotected = true;
return new RangeDescription(value, row, column, {
tag: 'input',
height: (height) ? height : undefined,
width: (width) ? width : undefined,
numberFormat: (numberFormat) ? numberFormat : undefined,
font: { family: 'Poppins', size: 11, weight: 'bold' },
background: '#d0e0e3',
border: {
top: true,
left: true,
bottom: true,
right: true,
color: Title_().font.color,
},
alignment: (horizontalAlignment) ? { horizontal: horizontalAlignment } : undefined,
validation: (validation) ? validation : undefined,
unprotected: unprotected,
});
}
function InputAction_(value, row, column, height, width, validation) {
/*
* arguments: [value [,row[, column[, height[, width[, validation]]]]]]
*
* value -> any type
* row -> integer (>0)
* column -> integer (>0)
* height -> integer (>0)
* width -> integer (>0)
* validation -> data validation object
*
*/
return new RangeDescription(value, row, column, {
tag: 'input',
height: (height) ? height : undefined,
width: (width) ? width : undefined,
font: { family: 'Poppins', color: 'blue', size: 11, weight: 'bold' },
background: '#d9ead3',
border: {
top: true,
left: true,
bottom: true,
right: true,
color: Title_().font.color,
},
validation: (validation) ? validation : undefined,
unprotected: true,
});
}
function Checkbox_(row, column, value, horizontalAlignment, applyBorder, height, width) {
/*
* arguments: [row[, column[, value[, horizontalAlignment[, applyBorder[, height[, width]]]]]]]
*
* row -> integer (>0)
* column -> integer (>0)
* value -> boolean (false by default)
* horizontalAlignment -> selection op string options "left", "right" and "center" to override existing setting
* applyBorder -> boolean, if true border will be applied
* height -> integer (>0)
* width -> integer (>0)
*
*/
if (value == undefined) value = false;
return new RangeDescription(value, row, column, {
tag: 'input',
height: (height) ? height : undefined,
width: (width) ? width : undefined,
font: { color: Label_('/').font.color },
numberFormat: new Formats().auto,
border: (applyBorder) ? {
top: true,
left: true,
bottom: true,
right: true,
color: Title_().font.color,
} : undefined,
alignment: (horizontalAlignment) ? { horizontal: horizontalAlignment } : undefined,
validation: buildValidation(['checkbox']),
unprotected: true,
});
}
function Message_(message, row, column, height, width) {
/*
* arguments: [message[, row[, column[, height[, width]]]]]
*
* message -> string
* row -> integer (>0)
* column -> integer (>0)
* height -> integer (>0)
* width -> integer (>0)
*
*/
return new RangeDescription(message, row, column, {
height: (height) ? height : undefined,
width: (width) ? width : undefined,
font: { family: 'Inconsolata', size: 11, color: '#cccccc' },
border: {
top: true,
left: true,
bottom: true,
right: true,
color: '#cccccc',
style: SpreadsheetApp.BorderStyle.DOUBLE,
},
});
}
function Info_(info, row, column, height, width, background, color) {
/*
* arguments: [info[, row[, column[, height[, width]]]]]
*
* info -> string
* row -> integer (>0)
* column -> integer (>0)
* height -> integer (>0)
* width -> integer (>0)
* background -> string, representing color
* color -> string, representing color
*
*/
return new RangeDescription(info, row, column, {
height: (height) ? height : undefined,
width: (width) ? width : undefined,
font: { family: 'Roboto', color: (color) ? color : '#666666', style: 'italic' },
background: (background) ? background : undefined,
border: {
top: true,
left: true,
bottom: true,
right: true,
color: '#999999',
style: SpreadsheetApp.BorderStyle.DOTTED,
},
alignment: { horizontal: 'left' },
indent: { 'left': 1 },
});
}
function Separator_(direction, row, column, length) {
/*
* arguments: direction, start, length
*
* direction -> string, either "vertical" or "horizontal"
* row -> integer (>0)
* column -> integer (>0)
* length -> integer (>0), height if vertical, width if horizontal
*
*/
var separator = new RangeDescription(undefined, row, column, {
mustMerge: false,
border: {
color: '#cccccc',
style: SpreadsheetApp.BorderStyle.SOLID_MEDIUM,
},
});
switch (direction) {
case 'vertical':
separator.height = length;
separator.border.right = true;
break;
case 'horizontal':
separator.width = length;
separator.border.bottom = true;
break;
}
return separator;
}
/** Misc **/
function applyLabel() {
var u;
var value = this.label.value;
var prop;
switch (this.purpose) {
case 'required':
case 'optional':
case 'action':
case 'checkbox':
prop = 'input';
break;
default: prop = 'text_range';
}
var required = (this.purpose == 'required') ? true : false,
noIndent = (this.purpose == 'checkbox') ? true : false;
return Label_(value, required, this[prop].row, col(this[prop].column) - 1, u, u, noIndent);
}
function applyInput() {
var u;
var input;
var value = (isPropDefined(this.input, 'value')) ? this.input.value : '',
row = this.input.row,
column = col(this.input.column),
height = (isPropDefined(this.input, 'height')) ? this.input.height : u,
width = (isPropDefined(this.input, 'width')) ? this.input.width : u,
validation = (isPropDefined(this.input, 'validation')) ? this.input.validation : u,
numberFormat = (isPropDefined(this.input, 'numberFormat')) ? this.input.numberFormat : u,
horizontalAlignment = (isPropDefined(this.input, 'horizontalAlignment')) ? this.input.horizontalAlignment : u,
applyBorder = (isPropDefined(this.input, 'applyBorder')) ? this.input.applyBorder : u,
unprotected = (isPropDefined(this.input, 'unprotected')) ? this.input.unprotected : u;
switch (this.purpose) {
case 'required':
input = InputRequired_(value, row, column, height, width, validation, numberFormat, horizontalAlignment, unprotected);
break;
case 'optional':
input = InputOptional_(value, row, column, height, width, validation, numberFormat, horizontalAlignment, unprotected);
break;
case 'action':
input = InputAction_(value, row, column, height, width, validation);
break;
case 'checkbox':
input = Checkbox_(row, column, value, horizontalAlignment, applyBorder, height, width);
break;
}
return input;
}
function applyTextRange() {
var u;
var textRange;
var value = (isPropDefined(this.text_range, 'value')) ? this.text_range.value : '',
row = this.text_range.row,
column = col(this.text_range.column),
height = (isPropDefined(this.text_range, 'height')) ? this.text_range.height : u,
width = (isPropDefined(this.text_range, 'width')) ? this.text_range.width : u,
numberFormat = (isPropDefined(this.text_range, 'numberFormat')) ? this.text_range.numberFormat : u,
horizontalAlignment = (isPropDefined(this.text_range, 'horizontalAlignment')) ?
this.text_range.horizontalAlignment : u,
omitIndent = (isPropDefined(this.text_range, 'omitIndent')) ? this.text_range.omitIndent : u,
background = (isPropDefined(this.text_range, 'background')) ? this.text_range.background : u,
color = (isPropDefined(this.text_range, 'color')) ? this.text_range.color : u;
switch (this.purpose) {
case 'title':
textRange = Title_(value, row, column, height, width, horizontalAlignment, omitIndent);
break;
case 'header_main':
textRange = HeaderMain_(value, row, column, height, width, horizontalAlignment, omitIndent);
break;
case 'header':
textRange = Header_(value, row, column, height, width, horizontalAlignment, omitIndent);
break;
case 'table_label':
textRange = Label_(value, required, row, column, height, width, omitIndent, true);
break;
case 'cell':
textRange = Cell_(value, row, column, height, width, numberFormat, horizontalAlignment, omitIndent);
break;
case 'message':
textRange = Message_(value, row, column, height, width);
break;
case 'info':
textRange = Info_(value, row, column, height, width, background, color);
break;
}
return textRange;
}
/**
* Updated: 2019-04-16
*/
/** Descriptions **/
function RangeDescription(value, row, column, desc) {
/*
* arguments: [value[, row[, column[, desc]]]]
*
* value -> any type
* row -> integer (>0)
* column -> integer (>0)
* desc -> object containing full range description to override default settings
*
*/
var tag = 'range',
sheetName = '',
row = (row && typeof row == 'number') ? row : 1,
column = (column && typeof column == 'number') ? column : 1,
height = 1, width = 1,
mustMerge = true, // options: true, false, 'vertically', 'across'
wrapStrategy = SpreadsheetApp.WrapStrategy.CLIP,
numberFormat = '@',
background = '#efefef',
validation = null,
value = (value) ? value : '',
protection = false, // options: true, false, 'warning'
unprotected = false;
if (desc) {
if (isPropDefined(desc, 'tag')) tag = desc.tag;
if (desc.hasOwnProperty('sheetName') && typeof desc.sheetName == 'string') sheetName = desc.sheetName;
if (desc.hasOwnProperty('row') && typeof desc.row == 'number') row = desc.row;
if (desc.hasOwnProperty('column') && typeof desc.column == 'number') column = desc.column;
if (desc.hasOwnProperty('height') && typeof desc.height == 'number') height = desc.height;
if (desc.hasOwnProperty('width') && typeof desc.width == 'number') width = desc.width;
if (isPropDefined(desc, 'mustMerge')) mustMerge = desc.mustMerge;
if (isPropDefined(desc, 'wrapStrategy')) wrapStrategy = desc.wrapStrategy;
if (isPropDefined(desc, 'numberFormat')) numberFormat = desc.numberFormat;
if (isPropDefined(desc, 'background')) background = desc.background;
if (isPropDefined(desc, 'validation')) validation = desc.validation;
if (isPropDefined(desc, 'value')) value = desc.value;
if (isPropDefined(desc, 'protection')) protection = desc.protection;
if (isPropDefined(desc, 'unprotected')) unprotected = desc.unprotected;
}
function Border() {
var top = null, left = null, bottom = null, right = null,
vertical = null, horizontal = null,
color = null, style = null;
if (desc && isPropDefined(desc, 'border')) {
var border = desc.border;
if (isPropDefined(border, 'top')) top = border.top;
if (isPropDefined(border, 'left')) left = border.left;
if (isPropDefined(border, 'bottom')) bottom = border.bottom;
if (isPropDefined(border, 'right')) right = border.right;
if (isPropDefined(border, 'vertical')) vertical = border.vertical;
if (isPropDefined(border, 'horizontal')) horizontal = border.horizontal;
if (isPropDefined(border, 'color')) color = border.color;
if (isPropDefined(border, 'style')) style = border.style;
}
this.top = top;
this.left = left;
this.bottom = bottom;
this.right = right;
this.vertical = vertical;
this.horizontal = horizontal;
this.color = color;
this.style = style;
}
function Alignment() {
var vertical = 'middle', horizontal = 'center';
if (desc && isPropDefined(desc, 'alignment')) {
var alignment = desc.alignment;
if (isPropDefined(alignment, 'vertical')) vertical = alignment.vertical;
if (isPropDefined(alignment, 'horizontal')) horizontal = alignment.horizontal;
}
this.vertical = vertical;
this.horizontal = horizontal;
}
function Font() {
var family = 'Inconsolata',
color = null,
size = 10,
weight = 'normal',
style = 'normal',
line = 'none';
if (desc && isPropDefined(desc, 'font')) {
var font = desc.font;
if (isPropDefined(font, 'family')) family = font.family;
if (isPropDefined(font, 'color')) color = font.color;
if (isPropDefined(font, 'size')) size = font.size;
if (isPropDefined(font, 'weight')) weight = font.weight;
if (isPropDefined(font, 'style')) style = font.style;
if (isPropDefined(font, 'line')) line = font.line;
}
this.family = family;
this.color = color;
this.size = size;
this.weight = weight;
this.style = style;
this.line = line;
}
function Indent() {
var left = 0, right = 0;
if (desc && isPropDefined(desc, 'indent')) {
var indent = desc.indent;
if (isPropDefined(indent, 'left')) left = indent.left;
if (isPropDefined(indent, 'right')) right = indent.right;
}
this.left = left;
this.right = right;
}
// Meta
this.tag = tag;
// Range
this.sheetName = sheetName;
this.row = row;
this.column = column;
this.height = height;
this.width = width;
this.mustMerge = mustMerge;
// Appearance
this.wrapStrategy = wrapStrategy;
this.numberFormat = numberFormat;
this.background = background;
this.border = new Border;
this.alignment = new Alignment;
this.font = new Font;
this.indent = new Indent;
// Data
this.validation = validation;
this.value = value;
// Protection
this.protection = protection;
this.unprotected = unprotected;
}
function SheetDescription(height, width, sheetName) {
/*
* arguments: [height[, width[, sheetName]]]
*
* height -> integer (>0) || array of integers(>0) representing different row heights
* width -> integer (>0) || array of integers(>0) representing different column widths
* sheetName -> string
*
*/
if (!height) height = 1;
if (!width) width = 1;
var rowHeight = 21, columnWidth = 100;
if (Array.isArray(height)) {
if (height.every(function(n) { return typeof n == 'number'; })) {
rowHeight = height;
height = height.length;
}
}
if (Array.isArray(width)) {
if (width.every(function(n) { return typeof n == 'number'; })) {
columnWidth = width;
width = width.length;
}
}
this.sheetName = (sheetName && typeof sheetName == 'string') ? sheetName : '';
// Dimensions
this.height = height;
this.width = width;
this.rowHeight = rowHeight;
this.columnWidth = columnWidth;
this.frozenRows = 0;
this.frozenColumns = 0;
// Body
this.range = new RangeDescription;
this.range.height = height;
this.range.width = width;
this.range.mustMerge = false;
this.hasGridlines = false;
// Protection
this.protection = false;
/* must be either false or Protection Object (may be ommitted or deleted)
* Protection Object -> {
* warning -> boolean
* unprotected -> array of unprotected ranges
* }
*/
}
/** Builders **/
function buildForm(sheetIdx, formIdx) {
/*
* arguments: sheetIdx, formIdx
*
* sheetIdx -> integer (>=0), representing sheet index in Custom Settings
* formIdx -> integer (>=0), representing form index in list of forms in sheet
*
*/
var builtForm = new Object;
var settings = new Settings;
sheetName = Object.keys(settings.sheets)[sheetIdx],
sheetSettings = settings.sheets[sheetName],
formProp = Object.keys(sheetSettings.forms)[formIdx],
form = sheetSettings.forms[formProp],
keys = Object.keys(form),
sheetDesc = new SheetDescription,
rangeDescs = new Array;
sheetDesc.sheetName = sheetName;
if (isPropDefined(sheetSettings, 'height')) sheetDesc.height = sheetSettings.height;
if (isPropDefined(sheetSettings, 'width')) sheetDesc.width = col(sheetSettings.width);
if (isPropDefined(sheetSettings, 'rowHeight')) sheetDesc.rowHeight = sheetSettings.rowHeight;
if (isPropDefined(sheetSettings, 'columnWidth')) sheetDesc.columnWidth = sheetSettings.columnWidth;
if (isPropDefined(sheetSettings, 'frozenRows')) sheetDesc.frozenRows = sheetSettings.frozenRows;
if (isPropDefined(sheetSettings, 'frozenColumns')) sheetDesc.frozenColumns = col(sheetSettings.frozenColumns);
if (isPropDefined(sheetSettings, 'protection')) sheetDesc.protection = sheetSettings.protection;
for (var i = 0; i < keys.length; i++) {
if (isPropDefined(form[keys[i]], 'setLabel')) rangeDescs.push(form[keys[i]].setLabel());
if (isPropDefined(form[keys[i]], 'setInput')) rangeDescs.push(form[keys[i]].setInput());
if (isPropDefined(form[keys[i]], 'setTextRange')) rangeDescs.push(form[keys[i]].setTextRange());
}
if (isPropDefined(sheetSettings, 'separators')) {
var separators = sheetSettings.separators;
if (Array.isArray(separators) && separators.length)
rangeDescs = rangeDescs.concat(separators);
}
builtForm.sheetDesc = sheetDesc;
builtForm.rangeDescs = rangeDescs;
return builtForm;
}
function buildRange(desc) {
/*
* arguments: desc
*
* desc -> object containing full range description
*
*/
var ss = SpreadsheetApp.getActive(),
sheet = (desc.sheetName) ? ss.getSheetByName(desc.sheetName) : ss.getActiveSheet(),
range = sheet.getRange(desc.row, desc.column, desc.height, desc.width),
cell = sheet.getRange(desc.row, desc.column);
var value = desc.value;
if (value && typeof value == 'string') {
var left = desc.indent.left,
right = desc.indent.right;
if (left || right) {
polyfills__String_repeat();
var lI = (left) ? ' '.repeat(left) : '',
rI = (right) ? ' '.repeat(right) : '';
value = lI + value + rI;
}
}
if (desc.mustMerge) {
switch (desc.mustMerge) {
case 'vertically': range.mergeVertically(); break;
case 'across': range.mergeAcross(); break;
default: range.merge();
}
} else range.breakApart();
range
.setBorder(
desc.border.top, desc.border.left,
desc.border.bottom, desc.border.right,
desc.border.vertical, desc.border.horizontal,
desc.border.color, desc.border.style
)
.setWrapStrategy(desc.wrapStrategy)
.setNumberFormat(desc.numberFormat)
.setHorizontalAlignment(desc.alignment.horizontal)
.setVerticalAlignment(desc.alignment.vertical)
.setBackground(desc.background)
.setFontColor(desc.font.color)
.setFontFamily(desc.font.family)
.setFontSize(desc.font.size)
.setFontLine(desc.font.line)
.setFontWeight(desc.font.weight)
.setFontStyle(desc.font.style)
.setDataValidation(desc.validation);
cell.setValue(value);
}
function buildRanges(descArr) {
/*
* arguments: descArr
*
* descArr -> array of objects containing full range descriptions
*
*/
if (Array.isArray(descArr)) {
for (var i = 0; i < descArr.length; i++) buildRange(descArr[i]);
}
}
function buildSheet(desc, rangeArr, isNew) {
/*
* arguments: desc[, rangeArr[, isNew]]
*
* desc -> object containing full sheet description
* rangeArr -> array of ranges to build
* isNew -> boolean (false by default)
*/
if (!isNew) isNew = false;
var ss = SpreadsheetApp.getActive();
var sheet, unprotected1 = [], unprotected2 = [];
var sheetName = (desc.sheetName) ? desc.sheetName : undefined,
height = desc.height,
width = desc.width,
rowHeight = desc.rowHeight,
columnWidth = desc.columnWidth,
frozenRows = desc.frozenRows,
frozenColumns = desc.frozenColumns;
if (isNew) {
(sheetName) ? ss.insertSheet(sheetName) : ss.insertSheet();
sheet = ss.getActiveSheet();
} else {
sheet = (sheetName) ? ss.getSheetByName(sheetName) : ss.getActiveSheet();
}
shrinkSheet(sheet);
sheet.setHiddenGridlines(!desc.hasGridlines);
if (height > 1) sheet.insertRowsAfter(1, height - 1);
if (width > 1) sheet.insertColumnsAfter(1, width - 1);
if (Array.isArray(rowHeight)) {
for (var r = 0; r < rowHeight.length; r++) {
sheet.setRowHeight(r + 1, rowHeight[r]);
}
} else {
sheet.setRowHeights(1, height, rowHeight);
}
if (Array.isArray(columnWidth)) {
for (var c = 0; c < columnWidth.length; c++) {
sheet.setColumnWidth(c + 1, columnWidth[c]);
}
} else {
sheet.setColumnWidths(1, width, columnWidth);
}
if (frozenRows && frozenRows < height) sheet.setFrozenRows(frozenRows);
if (frozenColumns && frozenColumns < width) sheet.setFrozenColumns(frozenColumns);
if (rangeArr && Array.isArray(rangeArr)) {
for (var r = 0; r < rangeArr.length; r++) {
var rng = rangeArr[r];
if (sheetName) rng.sheetName = sheetName;
if (rng.unprotected) {
unprotected1.push(sheet.getRange(rng.row, rng.column, rng.height, rng.width));
if (unprotected1.length == 1) sheet.setCurrentCell(sheet.getRange(rng.row, rng.column));
}
}
buildRanges(rangeArr);
}
var protection = sheet.getProtections(SpreadsheetApp.ProtectionType.SHEET)[0];
if (protection) protection.remove();
if (desc.hasOwnProperty('protection') && desc.protection) {
protection = sheet.protect();
if (desc.protection.hasOwnProperty('warning') && desc.protection.warning) {
protection.setWarningOnly(true);
} else {
var owner = ss.getOwner();
protection.addEditor(owner);
protection.removeEditors(protection.getEditors());
}
var hasUnprotected = isPropDefined(desc.protection, 'unprotected');
if (hasUnprotected || unprotected1.length) {
if (hasUnprotected && desc.protection.unprotected.length) {
unprotected2 = desc.protection.unprotected;
}
var unprotected = unprotected1.concat(unprotected2);
if (unprotected.length) protection.setUnprotectedRanges(unprotected);
}
}
}
function buildValidation(desc, showArrow, allowInvalid) {
/*
* arguments: desc[, showArrow[, allowInvalid]]
*
* desc -> array of description entities, where index 0 is purpose and others are arguments
* showArrow -> boolean, if true dropdown arrow will appear in dropdown type of validation
* allowInvalid -> boolean, if true will allow validation override
*
*/
if (desc) {
var validation = SpreadsheetApp.newDataValidation(),
purpose = desc[0];
(showArrow == undefined || showArrow == null) ? showArrow = true : showArrow = showArrow;
(allowInvalid == undefined) ? allowInvalid = false : allowInvalid = allowInvalid;
switch (purpose) {
// Dates
case 'date': validation.requireDate(); break;
case 'date =': validation = validation.requireDateEqualTo(date(desc[1])); break;
case 'date >': validation = validation.requireDateAfter(date(desc[1])); break;
case 'date >=': validation = validation.requireDateOnOrAfter(date(desc[1])); break;
case 'date <': validation = validation.requireDateBefore(date(desc[1])); break;
case 'date <=': validation = validation.requireDateOnOrBefore(date(desc[1])); break;
case 'date <=>': validation = validation.requireDateBetween(date(desc[1]), date(desc[2])); break;
case 'date <>': validation = validation.requireDateNotBetween(date(desc[1]), date(desc[2])); break;
// Numbers
case 'number =': validation = validation.requireNumberEqualTo(desc[1]); break;
case 'number !=': validation = validation.requireNumberNotEqualTo(desc[1]); break;
case 'number >': validation = validation.requireNumberGreaterThan(desc[1]); break;
case 'number >=': validation = validation.requireNumberGreaterThanOrEqualTo(desc[1]); break;
case 'number <': validation = validation.requireNumberLessThan(desc[1]); break;
case 'number <=': validation = validation.requireNumberLessThanOrEqualTo(desc[1]); break;
case 'number <=>': validation = validation.requireNumberBetween(desc[1], desc[2]); break;
case 'number <>': validation = validation.requireNumberNotBetween(desc[1], desc[2]); break;
// Text
case 'text =': validation = requireTextEqualTo(desc[1]); break;
case 'text +': validation = requireTextContallowInvalidns(desc[1]); break;
case 'text -': validation = requireTextDoesNotContallowInvalidn(desc[1]); break;
// Misc
case 'checkbox':
if (desc[1]) {
if (desc[2]) validation = validation.requireCheckbox(desc[1], desc[2]);
else validation = validation.requireCheckbox(desc[1]);
} else validation = validation.requireCheckbox();
break;
case 'formula': validation = validation.requireFormulaSatisfied(desc[1]); break;
case 'emallowInvalidl': validation = validation.requireTextIsEmallowInvalidl(); break;
case 'url': validation = validation.requireTextIsUrl(); break;
case 'list': validation = validation.requireValueInList(desc[1], showArrow); break;
case 'range': validation = validation.requireValueInRange(desc[1], showArrow); break;
}
validation = validation.setAllowInvalid(allowInvalid).build();
return validation;
}
}
/** Misc **/
function shrinkSheet(sheet) {
/*
* arguments: sheet
*
* sheet -> sheet object
*
*/
var u;
var maxRows = sheet.getMaxRows(),
maxColumns = sheet.getMaxColumns(),
frozenRows = sheet.getFrozenRows(),
frozenColumns = sheet.getFrozenColumns();
if (frozenRows) sheet.setFrozenRows(0);
if (frozenColumns) sheet.setFrozenColumns(0);
if (maxRows > 1) sheet.deleteRows(2, maxRows - 1);
if (maxColumns > 1) sheet.deleteColumns(2, maxColumns - 1);
desc = new SheetDescription;
sheet.setRowHeight(1, desc.rowHeight);
sheet.setColumnWidth(1, desc.columnWidth);
sheet.setHiddenGridlines(false);
buildRange(new RangeDescription(u, u, u, {
sheetName: sheet.getName(),
border: { top: false, right: false, bottom: false, left: false },
font: { color: new RangeDescription().background }
}));
}
function ssHasSheetWithName(sheetName, ss) {
/*
* arguments: sheetName[, ss]
*
* sheetName -> string, sheet name to look for in spreadsheet
* ss -> spreadsheet
*
*/
if (ss == undefined) ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets(),
hasSheet = false;
for (var i = 0; i < sheets.length; i++) {
if (sheets[i].getName() == sheetName) {
hasSheet = true;
break;
}
}
return hasSheet;
}
/**
* Updated: 2019-04-12
*/
/** Formats **/
function Formats() {
this.auto = '0.###############';
this.text = '@';
this.date = {
reversed: 'yyyy-mm-dd',
us: 'mm/dd/yyyy',
intl: 'dd.mm.yyyy',
month_short: 'mmm d, yyyy',
month_long: 'mmmm d, yyyy',
};
this.dollar = {
no_text: [
'$#,##0.00;;;',
'$#,##0.00;($#,##0.00);;',
'$#,##0.00;($#,##0.00);$0.00;'
],
text_allowed: [
'$#,##0.00;;',
'$#,##0.00;($#,##0.00);',
'$#,##0.00;($#,##0.00);$0.00'
],
};
}
/** Converters **/
function convertLocalDateToEST(params) {
/*
* arguments: [params]
*
* paramas -> string representing time parameters
*
*/
var date;
if (params != undefined) date = new Date(params);
else date = new Date;
return new Date(date.toLocaleString('en-US', { timeZone: 'America/New_York' }));
}
function convertDateToString(date, format) {
/*
* arguments: date, format
*
* date -> date object to convert to string
* format -> string as date format representation
*
*/
var dateStr;
var yyyy = date.getFullYear(),
yy = date.getYear(),
month = date.getMonth(),
calendarDay = date.getDate(),
weekDay = date.getDay(),
hours = date.getHours(),
minutes = date.getMinutes(),
seconds = date.getSeconds();
var ap_ = /\bam\/pm\b/gi, ap = 'AM';
if (format.search(ap) > -1 && hours > 12) {
hours = hours - 12;
ap = 'PM';
}
var months = {
short: [
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
],
long: [
'January', 'February', 'March',
'April', 'May', 'June',
'July', 'August', 'September',
'October', 'November', 'December'
],
};
var weekDays = {
short: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
long: [
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
],
};
dateStr = format
.replace(/\byyyy\b/gi, yyyy)
.replace(/\byy\b/gi, yy)
.replace(/\bmmmm\b/gi, months.long[month])
.replace(/\bmmm\b/gi, months.short[month])
.replace(/\bmm\b/g, ((month + 1) < 10) ? '0' + (month + 1) : month + 1)
.replace(/\bm\b/gi, month + 1)
.replace(/\bdddd\b/gi, weekDays.long[weekDay])
.replace(/\bddd\b/gi, weekDays.short[weekDay])
.replace(/\bdd\b/gi, (calendarDay < 10) ? '0' + calendarDay : calendarDay)
.replace(/\bd\b/gi, calendarDay)
.replace(/\bHH\b/g, (hours < 10) ? '0' + hours : hours)
.replace(/\bMM\b/g, (minutes < 10) ? '0' + minutes : minutes)
.replace(/\bSS\b/g, (seconds < 10) ? '0' + seconds : seconds)
.replace(ap_, ap);;
return dateStr;
}
function convertDateStringReversedToUS(reversedDateStr) {
/*
* arguments: reversedDateStr
*
* reversedDateStr -> string representing date pattern "yyyy-mm-dd"
*
*/
if (
reversedDateStr && typeof reversedDateStr == 'string' &&
reversedDateStr.length == 10 &&
reversedDateStr.search('-') == 4 &&
reversedDateStr.lastIndexOf('-') == 7
)
{
var d = reversedDateStr.split('-');
return d[1] + '/' + d[2] + '/' + d[0];
}
}
/**
* Updated: 2019-04-10
*/
/** Simplifiers **/
function abc_() {
return [
, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'
, 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
];
}
function col(ltr_num, isUC) {
/*
* arguments: ltr_num[, isUC]
*
* ltr_num -> string of letters (len<=2) || integer (>0,len<=2)
* isUC -> boolean determining if return column must be presented in upper case
*
*/
var abc = abc_();
if (typeof ltr_num == 'string') {
var colIdx,
ltr = ltr_num.toLowerCase();
if (ltr.length == 2) colIdx = abc.indexOf(ltr[0]) * 26 + abc.indexOf(ltr[1]);
else colIdx = abc.indexOf(ltr[0]);
return colIdx;
} else if (typeof ltr_num == 'number') {
var ltr, num = ltr_num;
if (num > 26) ltr = abc[quotient(num, 26)] + abc[num % 26];
else ltr = abc[num];
if (isUC) ltr = ltr.toUpperCase();
return ltr;
}
}
function quotient(n1, n2) {
/*
* arguments: n1, n2
*
* n1 -> integer
* n2 -> integer
*
*/
return (n1 - (n1 % n2)) / n2;
}
function clone(source) {
/*
* arguments: source
*
* source -> object to clone
*
*/
var target = new Object();
for (var prop in source) {
if (source.hasOwnProperty(prop)) {
target[prop] = source[prop];
}
}
return target;
}
function isPropDefined(source, prop) {
/*
* arguments: source, prop
*
* source -> object to test
* prop -> string representing object's property
*
*/
return source.hasOwnProperty(prop) && source[prop] != undefined;
}
/** Polyfills **/
// String
function polyfills__String_repeat() {
/*
* source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
*
*/
if (!String.prototype.repeat) {
String.prototype.repeat = function(count) {
'use strict';
if (this == null)
throw new TypeError('can\'t convert ' + this + ' to object');
var str = '' + this;
// To convert string to integer.
count = +count;
// Check NaN
if (count != count)
count = 0;
if (count < 0)
throw new RangeError('repeat count must be non-negative');
if (count == Infinity)
throw new RangeError('repeat count must be less than infinity');
count = Math.floor(count);
if (str.length == 0 || count == 0)
return '';
// Ensuring count is a 31-bit integer allows us to heavily optimize the
// main part. But anyway, most current (August 2014) browsers can't handle
// strings 1 << 28 chars or longer, so:
if (str.length * count >= 1 << 28)
throw new RangeError('repeat count must not overflow maximum string size');
var maxCount = str.length * count;
count = Math.floor(Math.log(count) / Math.log(2));
while (count) {
str += str;
count--;
}
str += str.substring(0, maxCount - str.length);
return str;
}
}
}