mirror of
https://github.com/RGBCube/serenity
synced 2025-05-16 19:45:07 +00:00

This also refactors the js integration stuff to allow sheets to reference each other safely.
187 lines
5.1 KiB
JavaScript
187 lines
5.1 KiB
JavaScript
function range(start, end, columnStep, rowStep) {
|
|
columnStep = integer(columnStep ?? 1);
|
|
rowStep = integer(rowStep ?? 1);
|
|
start = parse_cell_name(start) ?? { column: "A", row: 0 };
|
|
end = parse_cell_name(end) ?? start;
|
|
|
|
if (end.column.length > 1 || start.column.length > 1)
|
|
throw new TypeError("Only single-letter column names are allowed (TODO)");
|
|
|
|
const cells = [];
|
|
|
|
for (
|
|
let col = Math.min(start.column.charCodeAt(0), end.column.charCodeAt(0));
|
|
col <= Math.max(start.column.charCodeAt(0), end.column.charCodeAt(0));
|
|
col += columnStep
|
|
) {
|
|
for (
|
|
let row = Math.min(start.row, end.row);
|
|
row <= Math.max(start.row, end.row);
|
|
row += rowStep
|
|
) {
|
|
cells.push(String.fromCharCode(col) + row);
|
|
}
|
|
}
|
|
|
|
return cells;
|
|
}
|
|
|
|
// FIXME: Remove this and use String.split() eventually
|
|
function split(str, sep) {
|
|
const parts = [];
|
|
let splitIndex = -1;
|
|
for (;;) {
|
|
splitIndex = str.indexOf(sep);
|
|
if (splitIndex == -1) {
|
|
if (str.length) parts.push(str);
|
|
return parts;
|
|
}
|
|
parts.push(str.substring(0, splitIndex));
|
|
str = str.slice(splitIndex + sep.length);
|
|
}
|
|
}
|
|
|
|
function R(fmt, ...args) {
|
|
if (args.length !== 0) throw new TypeError("R`` format must be literal");
|
|
|
|
fmt = fmt[0];
|
|
return range(...split(fmt, ":"));
|
|
}
|
|
|
|
function select(criteria, t, f) {
|
|
if (criteria) return t;
|
|
return f;
|
|
}
|
|
|
|
function sumIf(condition, cells) {
|
|
let sum = null;
|
|
for (let name of cells) {
|
|
let cell = thisSheet[name];
|
|
if (condition(cell)) sum = sum === null ? cell : sum + cell;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
function countIf(condition, cells) {
|
|
let count = 0;
|
|
for (let name of cells) {
|
|
let cell = thisSheet[name];
|
|
if (condition(cell)) count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
function now() {
|
|
return new Date();
|
|
}
|
|
|
|
function repeat(count, str) {
|
|
return Array(count + 1).join(str);
|
|
}
|
|
|
|
function randRange(min, max) {
|
|
return Math.random() * (max - min) + min;
|
|
}
|
|
|
|
function integer(value) {
|
|
return value | 0;
|
|
}
|
|
|
|
function sheet(name) {
|
|
return workbook.sheet(name);
|
|
}
|
|
|
|
// Cheat the system and add documentation
|
|
range.__documentation = JSON.stringify({
|
|
name: "range",
|
|
argc: 2,
|
|
argnames: ["start", "end", "column step", "row step"],
|
|
doc:
|
|
"Generates a list of cell names in a rectangle defined by two " +
|
|
"_top left_ and _bottom right_ cells `start` and `end`, spaced" +
|
|
" `column step` columns, and `row step` rows apart.",
|
|
examples: {
|
|
'range("A1", "C4")': "Generate a range A1:C4",
|
|
'range("A1", "C4", 2)': "Generate a range A1:C4, skipping every other column",
|
|
},
|
|
});
|
|
|
|
select.__documentation = JSON.stringify({
|
|
name: "select",
|
|
argc: 3,
|
|
argnames: ["criteria", "true value", "false value"],
|
|
doc: "Selects between the two `true` and `false` values based on the value of `criteria`",
|
|
examples: {
|
|
"select(A1, A2, A3)": "Evaluates to A2 if A1 is true, A3 otherwise",
|
|
},
|
|
});
|
|
|
|
sumIf.__documentation = JSON.stringify({
|
|
name: "sumIf",
|
|
argc: 2,
|
|
argnames: ["condition", "cell names"],
|
|
doc:
|
|
"Calculates the sum of cells the value of which evaluates to true when passed to `condition`",
|
|
examples: {
|
|
'sumIf(x => x instanceof Number, range("A1", "C4"))':
|
|
"Calculates the sum of all numbers within A1:C4",
|
|
},
|
|
});
|
|
|
|
countIf.__documentation = JSON.stringify({
|
|
name: "countIf",
|
|
argc: 2,
|
|
argnames: ["condition", "cell names"],
|
|
doc: "Counts cells the value of which evaluates to true when passed to `condition`",
|
|
examples: {
|
|
'countIf(x => x instanceof Number, range("A1", "C4"))':
|
|
"Counts the number of cells which have numbers within A1:C4",
|
|
},
|
|
});
|
|
|
|
now.__documentation = JSON.stringify({
|
|
name: "now",
|
|
argc: 0,
|
|
argnames: [],
|
|
doc: "Returns a Date instance for the current moment",
|
|
examples: {},
|
|
});
|
|
|
|
repeat.__documentation = JSON.stringify({
|
|
name: "repeat",
|
|
argc: 2,
|
|
argnames: ["string", "count"],
|
|
doc: "Returns a string equivalent to `string` repeated `count` times",
|
|
examples: {
|
|
'repeat("a", 10)': 'Generates the string "aaaaaaaaaa"',
|
|
},
|
|
});
|
|
|
|
randRange.__documentation = JSON.stringify({
|
|
name: "randRange",
|
|
argc: 2,
|
|
argnames: ["start", "end"],
|
|
doc: "Returns a random number in the range (`start`, `end`)",
|
|
examples: {},
|
|
});
|
|
|
|
integer.__documentation = JSON.stringify({
|
|
name: "integer",
|
|
argc: 1,
|
|
argnames: ["value"],
|
|
doc: "Returns the integer value of `value`",
|
|
examples: {
|
|
"A1 = integer(A0)": "Sets the value of the cell A1 to the integer value of the cell A0",
|
|
},
|
|
});
|
|
|
|
sheet.__documentation = JSON.stringify({
|
|
name: "sheet",
|
|
argc: 1,
|
|
argnames: ["name or index"],
|
|
doc: "Returns a reference to another sheet, identified by _name_ or _index_",
|
|
examples: {
|
|
"sheet('Sheet 1').A4": "Read the value of the cell A4 in a sheet named 'Sheet 1'",
|
|
"sheet(0).A0 = 123": "Set the value of the cell A0 in the first sheet to 123",
|
|
},
|
|
});
|