1391 lines
41 KiB
JavaScript
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;
|
|
}
|
|
}
|
|
|
|
}
|