@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
1,278 lines (1,190 loc) • 63.3 kB
JavaScript
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ({
/***/ 0:
/***/ (function(module, exports, __webpack_require__) {
__webpack_require__(1530);
module.exports = __webpack_require__(1530);
/***/ }),
/***/ 3:
/***/ (function(module, exports) {
module.exports = function() { throw new Error("define cannot be used indirect"); };
/***/ }),
/***/ 1516:
/***/ (function(module, exports) {
module.exports = require("./references");
/***/ }),
/***/ 1530:
/***/ (function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// -*- fill-column: 100 -*-
(function(f, define){
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(1516) ], __WEBPACK_AMD_DEFINE_FACTORY__ = (f), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
})(function(){
"use strict";
if (kendo.support.browser.msie && kendo.support.browser.version < 9) {
return;
}
// WARNING: removing the following jshint declaration and turning
// == into === to make JSHint happy will break functionality.
/* jshint eqnull:true, newcap:false, laxbreak:true, shadow:true, validthis:true, -W054, loopfunc: true */
/* global console */
/* jshint latedef: nofunc */
var calc = {};
var spreadsheet = kendo.spreadsheet;
spreadsheet.calc = calc;
var exports = calc.runtime = {};
var Class = kendo.Class;
var Ref = spreadsheet.Ref;
var CellRef = spreadsheet.CellRef;
var RangeRef = spreadsheet.RangeRef;
var UnionRef = spreadsheet.UnionRef;
var NULL = spreadsheet.NULLREF;
/* -----[ Errors ]----- */
function CalcError(code) {
if (code instanceof CalcError) {
return code;
}
this.code = code;
}
CalcError.prototype.toString = function() {
return "#" + this.code + (this.code == "NAME" ? "?" : "!");
};
/* -----[ Context ]----- */
var Context = Class.extend({
init: function Context(callback, formula, ss, parent) {
this.callback = callback;
this.formula = formula;
this.ss = ss;
this.parent = parent;
},
resolve: function(val) {
var self = this;
if (val instanceof Ref) {
self.resolveCells([ val ], function(){
self._resolve(val);
});
} else {
self._resolve(val);
}
},
error: function(val) {
return new CalcError(val);
},
_resolve: function(val) {
if (val === undefined) {
val = null;
} else if (Array.isArray(val)) {
val = this.asMatrix(val);
} else {
val = maybeRoundFloatErrors(val);
}
var f = this.formula;
if (f.arrayFormulaRange) {
// when this is an array formula, make sure we return a Matrix. The value already
// *should* be a Matrix, so this won't incur speed penalty in normal cases.
val = this.asMatrix(val) || this.asMatrix([[ val ]]);
} else if (val instanceof RangeRef) {
val = this._arrayArg(val);
}
f.value = val;
if (this.ss.onFormula(f) && this.callback) {
this.callback.call(f, val);
}
},
resolveCells: function(a, f) {
var context = this, formulas = [];
(function loop(a){
for (var i = 0; i < a.length; ++i) {
var x = a[i];
if (x instanceof Ref) {
add(context.getRefCells(x));
}
if (Array.isArray(x)) {
// make sure we resolve cells in literal matrices
loop(x);
}
}
})(a);
if (!formulas.length) {
return f.call(context);
}
for (var pending = formulas.length, i = 0; i < formulas.length; ++i) {
fetch(formulas[i]);
}
function fetch(formula) { // jshint ignore:line, because you are stupid.
formula.exec(context.ss, function(){
if (!--pending) {
f.call(context);
}
}, context);
}
function add(a) {
for (var i = 0; i < a.length; ++i) {
var cell = a[i];
if (cell.formula) {
formulas.push(cell.formula);
}
}
return true;
}
},
cellValues: function(a, wantNulls) {
var ret = [];
for (var i = 0; i < a.length; ++i) {
var val = a[i];
if (val instanceof Ref) {
val = this.getRefData(val, wantNulls);
ret = ret.concat(val);
} else if (Array.isArray(val)) {
ret = ret.concat(this.cellValues(val, wantNulls));
} else if (val instanceof Matrix) {
ret = ret.concat(this.cellValues(val.data, wantNulls));
} else {
ret.push(val);
}
}
return ret;
},
fetchName: function(ref, callback) {
var f = this.formula;
var val = this.ss.nameValue(ref, f.sheet, f.row, f.col);
if (val instanceof Formula) {
// clone and relocate to calling formula, so that relative references in a named
// formula would work as expected.
val = val.clone(f.sheet, f.row, f.col, true);
// XXX: I don't like this dependency here; basically we only need ss.onFormula to
// return true and do nothing else.
var ss = new spreadsheet.ValidationFormulaContext(this.ss.workbook);
val.exec(ss, callback, this);
} else {
if (val instanceof Ref) {
// relocate for relative refs
val = val.absolute(f.row, f.col);
if (!val.sheet) {
val.sheet = f.sheet;
}
}
callback(val == null ? new CalcError("NAME") : val);
}
},
force: function(val) {
if (val instanceof Ref) {
return this.getRefData(val);
}
return val;
},
func: function(fname, callback, args) {
fname = fname.toLowerCase();
var f = FUNCS[fname];
if (f) {
return f.call(this, callback, args);
}
callback(new CalcError("NAME"));
},
bool: function(val) {
if (val instanceof Ref) {
val = this.getRefData(val);
}
if (typeof val == "string") {
return val.toLowerCase() == "true";
}
if (typeof val == "number") {
return val !== 0;
}
if (typeof val == "boolean") {
return val;
}
return val != null;
},
_arrayArg: function(ref) { // not necessarily a reference though
var f = this.formula;
if (!f.arrayFormulaRange && ref instanceof RangeRef) {
// if formula wasn't saved with Ctrl-Shift-Enter but a range is passed, Excel does
// something special:
//
// - if range is Wx1 (horizontal array) and current formula resides within the
// horizontal bounds of the range, the appropriate cell is selected. Example:
// A1..D1 contain 1, 2, 3, 4 and we have in B2: =A1:D1, then B2 will get 2 (the
// value in A2). If the same formula is typed outside the horizontal span of the
// range, say in E2: =A1:D1, then #VALUE! is returned.
//
// - similarly, if the range is 1xH (vertical array), the formula must reside within
// the vertical bounds of the range and it gets the corresponding cell; otherwise,
// #VALUE!.
if (ref.height() == 1 && f.col >= ref.topLeft.col && f.col <= ref.bottomRight.col) {
return this.getRefData(new CellRef(ref.topLeft.row, f.col).setSheet(ref.sheet));
}
if (ref.width() == 1 && f.row >= ref.topLeft.row && f.row <= ref.bottomRight.row) {
return this.getRefData(new CellRef(f.row, ref.topLeft.col).setSheet(ref.sheet));
}
return new CalcError("VALUE");
} else {
return this.asMatrix(ref);
}
},
asMatrix: function(range) {
if (range instanceof Matrix) {
return range;
}
var self = this;
if (range instanceof RangeRef) {
var tl = range.topLeft;
var top = tl.row, left = tl.col;
var cells = self.getRefCells(range);
var m = new Matrix(self);
if (isFinite(range.width())) {
m.width = range.width();
}
if (isFinite(range.height())) {
m.height = range.height();
}
if (!isFinite(top)) {
top = 0;
}
if (!isFinite(left)) {
left = 0;
}
cells.forEach(function(cell){
m.set(cell.row - top,
cell.col - left,
cell.value);
});
return m;
}
if (Array.isArray(range) && range.length > 0) {
var m = new Matrix(self), row = 0;
range.forEach(function(line){
var col = 0;
var h = 1;
line.forEach(function(el){
var isRange = el instanceof RangeRef;
if (el instanceof Ref && !isRange) {
el = self.getRefData(el);
}
if (isRange || Array.isArray(el)) {
el = self.asMatrix(el);
}
if (el instanceof Matrix) {
el.each(function(el, r, c){
m.set(row + r, col + c, el);
});
h = Math.max(h, el.height);
col += el.width;
} else {
m.set(row, col++, el);
}
});
row += h;
});
return m;
}
},
getRefCells: function(refs, hiddenInfo, wantNulls) {
var f = this.formula;
return this.ss.getRefCells(refs, hiddenInfo, f.sheet, f.row, f.col, wantNulls);
},
getRefData: function(ref, wantNulls) {
var f = this.formula;
return this.ss.getData(ref, f.sheet, f.row, f.col, wantNulls);
},
workbook: function() {
return this.ss.workbook;
}
});
var Matrix = Class.extend({
init: function Matrix(context) {
this.context = context;
this.height = 0;
this.width = 0;
this.data = [];
},
clone: function() {
var m = new Matrix(this.context);
m.height = this.height;
m.width = this.width;
m.data = this.data.map(function(row){ return row.slice(); });
return m;
},
get: function(row, col) {
var line = this.data[row];
var val = line ? line[col] : null;
return val instanceof Ref ? this.context.getRefData(val) : val;
},
getNA: function(row, col) {
if (row < this.height && col < this.width) {
return this.get(row, col);
}
return new CalcError("N/A");
},
set: function(row, col, data) {
var line = this.data[row];
if (line == null) {
line = this.data[row] = [];
}
line[col] = data;
if (row >= this.height) {
this.height = row + 1;
}
if (col >= this.width) {
this.width = col + 1;
}
},
each: function(f, includeEmpty) {
for (var row = 0; row < this.height; ++row) {
for (var col = 0; col < this.width; ++col) {
var val = this.get(row, col);
if (includeEmpty || val != null) {
val = f.call(this.context, val, row, col);
if (val !== undefined) {
return val;
}
}
}
}
},
map: function(f, includeEmpty) {
var m = new Matrix(this.context);
this.each(function(el, row, col){
// here `this` is actually the context
m.set(row, col, f.call(this, el, row, col));
}, includeEmpty);
return m;
},
eachRow: function(f) {
for (var row = 0; row < this.height; ++row) {
var val = f.call(this.context, row);
if (val !== undefined) {
return val;
}
}
},
eachCol: function(f) {
for (var col = 0; col < this.width; ++col) {
var val = f.call(this.context, col);
if (val !== undefined) {
return val;
}
}
},
mapRow: function(f) {
var m = new Matrix(this.context);
this.eachRow(function(row){
m.set(row, 0, f.call(this.context, row));
});
return m;
},
mapCol: function(f) {
var m = new Matrix(this.context);
this.eachCol(function(col){
m.set(0, col, f.call(this.context, col));
});
return m;
},
toString: function() {
return JSON.stringify(this.data);
},
transpose: function() {
var m = new Matrix(this.context);
this.each(function(el, row, col){
m.set(col, row, el);
});
return m;
},
unit: function(n) {
this.width = this.height = n;
var a = this.data = new Array(n);
for (var i = n; --i >= 0;) {
var row = a[i] = new Array(n);
for (var j = n; --j >= 0;) {
row[j] = i == j ? 1 : 0;
}
}
return this;
},
multiply: function(b) {
var a = this, m = new Matrix(a.context);
for (var row = 0; row < a.height; ++row) {
for (var col = 0; col < b.width; ++col) {
var s = 0;
for (var i = 0; i < a.width; ++i) {
var va = a.get(row, i);
var vb = b.get(i, col);
if (typeof va != "number" || typeof vb != "number") {
throw new CalcError("VALUE");
}
s += va * vb;
}
m.set(row, col, s);
}
}
return m;
},
adds: function(b, s) {
var a = this, m = new Matrix(a.context);
var sign = s ? -1 : 1;
for (var row = 0; row < a.height; ++row) {
for (var col = 0; col < a.width; ++col) {
var x = a.get(row, col),
y = b.get(row, col);
m.set(row, col, x + sign * y);
}
}
return m;
},
determinant: function() {
var a = this.clone().data;
var n = a.length;
var d = 1, C, L, i, k;
for (C = 0; C < n; C++) {
for (L = C; (L < n) && (!a[L][C]); L++) {}
if (L == n) {
return 0;
}
if (L != C) {
d = -d;
for (k = C; k < n; k++) {
var t = a[C][k];
a[C][k] = a[L][k];
a[L][k] = t;
}
}
for (i = C+1; i < n; i++) {
for (k = C+1; k < n; k++) {
a[i][k] -= a[C][k] * a[i][C] / a[C][C];
}
}
d *= a[C][C];
}
return d;
},
inverse: function() {
var n = this.width;
var m = this.augment(new Matrix(this.context).unit(n));
var a = m.data;
var tmp;
// Gaussian elimination
// https://en.wikipedia.org/wiki/Gaussian_elimination#Finding_the_inverse_of_a_matrix
// 1. Get zeros below main diagonal
for (var k = 0; k < n; ++k) {
var imax = argmax(k, n, function(i){ return a[i][k]; });
if (!a[imax][k]) {
return null; // singular matrix
}
if (k != imax) {
tmp = a[k];
a[k] = a[imax];
a[imax] = tmp;
}
for (var i = k+1; i < n; ++i) {
for (var j = k+1; j < 2*n; ++j) {
a[i][j] -= a[k][j] * a[i][k] / a[k][k];
}
a[i][k] = 0;
}
}
// 2. Get 1-s on main diagonal, dividing by pivot
for (var i = 0; i < n; ++i) {
for (var f = a[i][i], j = 0; j < 2*n; ++j) {
a[i][j] /= f;
}
}
// 3. Get zeros above main diagonal. Actually, we only care to compute the right side
// here (that will be the inverse), so in the inner loop below we go while j >= n,
// instead of j >= k.
for (var k = n; --k >= 0;) {
for (var i = k; --i >= 0;) {
if (a[i][k]) {
for (var j = 2*n; --j >= n;) {
a[i][j] -= a[k][j] * a[i][k];
}
}
}
}
return m.slice(0, n, n, n);
},
augment: function(m) {
var ret = this.clone(), n = ret.width;
m.each(function(val, row, col){
ret.set(row, col + n, val);
});
return ret;
},
slice: function(row, col, height, width) {
var m = new Matrix(this.context);
for (var i = 0; i < height; ++i) {
for (var j = 0; j < width; ++j) {
m.set(i, j, this.get(row + i, col + j));
}
}
return m;
}
// XXX: debug
// dump: function() {
// this.data.forEach(function(row){
// console.log(row.map(function(val){
// var str = val.toFixed(3).replace(/\.?0*$/, function(s){
// return [ "", " ", " ", " ", " " ][s.length];
// });
// if (val >= 0) { str = " " + str; }
// return str;
// }).join(" "));
// });
// }
});
function argmax(i, end, f) {
var max = f(i), pos = i;
while (++i < end) {
var v = f(i);
if (v > max) {
max = v;
pos = i;
}
}
return pos;
}
/* -----[ Formula ]----- */
var Formula = Class.extend({
init: function Formula(refs, handler, printer, sheet, row, col, arrayFormulaRange){
this.refs = refs;
this.handler = handler;
this.print = printer;
this.absrefs = null;
this.sheet = sheet;
this.row = row;
this.col = col;
this.onReady = [];
this.pending = false;
this.arrayFormulaRange = arrayFormulaRange;
},
setArrayFormulaRange: function(ref) {
this.arrayFormulaRange = ref.clone().setSheet(this.sheet);
},
clone: function(sheet, row, col, forceRefs) {
var lcsheet = sheet.toLowerCase();
var refs = this.refs;
var range = this.arrayFormulaRange;
if (forceRefs || lcsheet != this.sheet.toLowerCase()) {
refs = refs.map(function(ref){
if (!ref.hasSheet() && (!ref.sheet || ref.sheet.toLowerCase() != lcsheet)) {
ref = ref.clone().setSheet(sheet);
}
return ref;
});
if (range) {
range = range.clone().setSheet(sheet);
}
}
return new Formula(refs, this.handler, this.print, sheet, row, col, range);
},
deepClone: function() {
var refs = this.refs.map(function(ref){ return ref.clone(); });
return new Formula(refs, this.handler, this.print, this.sheet, this.row, this.col, this.arrayFormulaRange);
},
resolve: function(val) {
this.pending = false;
this.onReady.forEach(function(callback){
callback(val);
});
},
exec: function(ss, callback, parentContext) {
if ("value" in this) {
if (callback) {
callback(this.value);
}
} else {
if (callback) {
this.onReady.push(callback);
}
var ctx = new Context(this.resolve, this, ss, parentContext);
var level = 0;
// if the call chain leads back to this same formula, we have a circular dependency.
while (parentContext) {
if (parentContext.formula === this) {
this.pending = false;
ctx.resolve(new CalcError("CIRCULAR"));
return;
}
parentContext = parentContext.parent;
++level;
}
// pending is still useful for ASYNC formulas
if (this.pending) {
return;
}
this.pending = true;
var next = function() {
// compute and cache the absolute references
if (!this.absrefs) {
this.absrefs = this.refs.map(function(ref){
return ref.absolute(this.row, this.col);
}, this);
}
// finally invoke the handler given to us by the compiler in calc.js
this.handler.call(ctx);
}.bind(this);
if (level < 20) {
next();
} else {
setTimeout(next, 0);
}
}
},
reset: function() {
this.onReady = [];
this.pending = false;
delete this.value;
},
renameSheet: function(oldSheetName, newSheetName) {
oldSheetName = oldSheetName.toLowerCase();
this.absrefs = null;
if (this.sheet.toLowerCase() == oldSheetName) {
this.sheet = newSheetName;
}
this.refs.forEach(function(ref){
ref.renameSheet(oldSheetName, newSheetName);
});
},
adjust: function(affectedSheet, operation, start, delta) {
affectedSheet = affectedSheet.toLowerCase();
var formulaRow = this.row;
var formulaCol = this.col;
var formulaSheet = this.sheet.toLowerCase();
var formulaMoves = false;
if (formulaSheet == affectedSheet) {
// move formula if it's after the change point
if (operation == "row" && formulaRow >= start) {
this.row += delta;
formulaMoves = true;
}
if (operation == "col" && formulaCol >= start) {
this.col += delta;
formulaMoves = true;
}
}
var newFormulaRow = this.row;
var newFormulaCol = this.col;
this.absrefs = null;
var prevRefs = this.refs;
var modified = formulaMoves;
this.refs = prevRefs.map(function(ref){
var newRef = adjust(ref);
if (!modified && !sameRef(newRef, ref)) {
modified = true;
}
return newRef;
});
var prevRange = this.arrayFormulaRange;
if (prevRange) {
this.arrayFormulaRange = adjust(prevRange);
if (!modified && !sameRef(prevRange, this.arrayFormulaRange)) {
modified = true;
}
}
if (modified) {
// return a clone of the original formula. needed to undo operations like
// deleteRow, which can transform a reference into NULL.
return new Formula(prevRefs, this.handler, this.print, this.sheet, formulaRow, formulaCol, prevRange);
}
function adjust(ref){
if (ref === NULL) {
return ref;
}
if (ref.sheet.toLowerCase() != affectedSheet) {
if (formulaMoves) {
// a reference to another sheet should still point to the same location
// after adjustment; thus if row/col was removed before formula, relative
// references must be adjusted by delta.
if (operation == "row" && formulaRow >= start) {
ref = ref.relative(delta, 0);
}
if (operation == "col" && formulaCol >= start) {
ref = ref.relative(0, delta);
}
}
return ref;
}
return ref.adjust(
formulaRow, formulaCol,
newFormulaRow, newFormulaCol,
operation == "row",
start, delta
);
}
},
toString: function() {
return this.print(this.row, this.col);
}
});
function sameRef(r1, r2) {
// note: r1.eq(r2) will not do, because it returns true for A1 and A1:A1 (CellRef
// vs. RangeRef). To properly undo we need to assert that the references are exactly the
// same (including type).
if (r1.constructor !== r2.constructor) {
return false;
}
if (r1 instanceof CellRef) {
return r1.sheet == r2.sheet
&& r1.row == r2.row
&& r1.col == r2.col
&& r1.rel == r2.rel;
}
if (r1 instanceof RangeRef) {
return sameRef(r1.topLeft , r2.topLeft)
&& sameRef(r1.bottomRight , r2.bottomRight)
&& r1.endSheet == r2.endSheet;
}
if (r1 instanceof UnionRef) {
var i = r1.refs.length;
if (i != r2.refs.length) {
return false;
}
while (--i >= 0) {
if (!sameRef(r1.refs[i], r2.refs[i])) {
return false;
}
}
}
return true;
}
// spreadsheet functions --------
var FUNCS = Object.create(null);
FUNCS["if"] = function(callback, args) {
var self = this;
var co = args[0], th = args[1], el = args[2];
// XXX: I don't like this resolveCells here. We should try to declare IF with
// defineFunction.
this.resolveCells([ co ], function(){
var comatrix = self.asMatrix(co);
if (comatrix) {
// XXX: calling both branches in this case, since we'll typically need values from
// both. We could optimize and call them only when first needed, but oh well.
th(function(th){
el(function(el){
var thmatrix = self.asMatrix(th);
var elmatrix = self.asMatrix(el);
callback(comatrix.map(function(val, row, col){
if (val instanceof CalcError) {
return val;
} else if (self.bool(val)) {
return thmatrix ? thmatrix.get(row, col) : th;
} else {
return elmatrix ? elmatrix.get(row, col) : el;
}
}));
});
});
} else {
co = this.force(co);
if (co instanceof CalcError) {
callback(co);
} else if (self.bool(co)) {
th(callback);
} else {
el(callback);
}
}
});
};
FUNCS["φ"] = function(callback) {
callback((1+Math.sqrt(5))/2);
};
// Lasciate ogni speranza, voi ch'entrate.
//
// XXX: document this function.
function compileArgumentChecks(functionName, args) {
var arrayArgs = "function arrayArgs(args) { var xargs = [], width = 0, height = 0, arrays = [], i = 0; ";
var resolve = "function resolve(args, callback) { var toResolve = [], i = 0; ";
var name, forced, main = "'use strict'; function check(args) { var stack = [], tmp, xargs = [], i = 0, m, err = 'VALUE'; ", haveForced = false;
var canBeArrayArg = false, hasArrayArgs = false;
main += args.map(comp).join("");
main += "if (i < args.length) return new CalcError('N/A'); ";
main += "return xargs; } ";
arrayArgs += "return { args: xargs, width: width, height: height, arrays: arrays }; } ";
var f;
if (haveForced) {
resolve += "this.resolveCells(toResolve, callback); } ";
f = new Function("CalcError", "round", main + resolve + arrayArgs + " return { resolve: resolve, check: check, arrayArgs: arrayArgs };");
} else {
f = new Function("CalcError", "round", main + " return { check: check };");
}
f = f(CalcError, limitPrecision);
if (!hasArrayArgs) {
delete f.arrayArgs;
}
return f;
function comp(x) {
name = x[0];
var code = "{ ";
if (Array.isArray(name)) {
arrayArgs += "while (i < args.length) { ";
resolve += "while (i < args.length) { ";
code += "xargs.push(tmp = []); stack.push(xargs); xargs = tmp; ";
code += "while (i < args.length) { ";
code += x.map(comp).join("");
code += "} ";
code += "xargs = stack.pop(); ";
resolve += "} ";
arrayArgs += "} ";
} else if (name == "+") {
arrayArgs += "while (i < args.length) { ";
resolve += "while (i < args.length) { ";
code += "if (i >= args.length) return new CalcError('N/A'); ";
code += "xargs.push(tmp = []); stack.push(xargs); xargs = tmp; ";
code += "do { ";
code += x.slice(1).map(comp).join("");
code += "} while (i < args.length); ";
code += "xargs = stack.pop(); ";
resolve += "} ";
arrayArgs += "} ";
} else if (name == "?") {
// standalone assertion without a new argument
code += "if (!(" + cond(x[1]) + ")) return new CalcError(err); ";
} else {
var type = x[1];
if (Array.isArray(type) && /^#?collect/.test(type[0])) {
var wantNulls = /!$/.test(type[0]);
var n = type[2];
force();
code += "try {"
+ "var $" + name + " = this.cellValues(args.slice(i";
if (n) {
code += ", i + " + n;
}
code += ")" + (wantNulls ? ",true" : "") + ").reduce(function(ret, $"+name+"){ ";
if (type[0].charAt(0) != "#") {
code += "if ($"+name+" instanceof CalcError) throw $"+name+"; ";
}
code += "if (" + cond(type[1]) + ") ret.push($"+name+"); ";
code += "return ret; ";
code += "}.bind(this), []); ";
if (n) {
code += "i += " + n + "; ";
} else {
code += "i = args.length; ";
}
code += "xargs.push($"+name+")"
+ "} catch(ex) { if (ex instanceof CalcError) return ex; throw ex; } ";
resolve += "toResolve.push(args.slice(i)); ";
} else if (type == "rest") {
code += "xargs.push(args.slice(i)); i = args.length; ";
} else {
if ((canBeArrayArg = /^\*/.test(name))) {
hasArrayArgs = true;
name = name.substr(1);
}
code += "var $" + name + " = args[i++]; ";
var allowError = false;
if (/!$/.test(type)) {
type = type.substr(0, type.length - 1);
allowError = true;
} else {
code += "if ($"+name+" instanceof CalcError) return $"+name+"; ";
}
code += typeCheck(type, allowError) + "xargs.push($"+name+"); ";
}
}
code += "} ";
return code;
}
function force() {
if (forced) {
return "$"+name+"";
}
haveForced = true;
forced = true;
resolve += "toResolve.push(args[i++]); ";
return "($"+name+" = this.force($"+name+"))";
}
function forceNum(round) {
return "("
+ (round
? ("(typeof " + force() + " == 'number' ? ($"+name+" = round($"+name+"), true) : false) || ")
: ("(typeof " + force() + " == 'number') || "))
+ "(typeof $"+name+" == 'boolean' ? ($"+name+" = +$" + name + ", true) : false) || "
+ "(typeof $"+name+" == 'string' && !/^(?:=|true|false)/i.test($"+name+") ? ("
+ "tmp = kendo.spreadsheet.calc.parse(0, 0, 0, $"+name+"), "
+ "/^date|number|percent$/.test(tmp.type) ? ($"+name+" = +tmp.value, true) : false"
+ ") : false)"
+ ")";
}
function typeCheck(type, allowError) {
forced = false;
var ret = "if (!(" + cond(type) + ")) { ";
if (forced && !allowError) {
ret += " if ($" + name + " instanceof CalcError) return $" + name + "; ";
}
ret += "return new CalcError(err); } ";
if (!forced) {
resolve += "i++; ";
}
if (canBeArrayArg) {
arrayArgs += "var $" + name + " = this._arrayArg(args[i]); "
+ "if ($" + name + ") { "
+ "xargs.push($" + name + "); "
+ "width = Math.max(width, $" + name + ".width); "
+ "height = Math.max(height, $" + name + ".height); "
+ "arrays.push(true) } else { "
+ "xargs.push(args[i]); "
+ "arrays.push(false); } i++; ";
} else {
arrayArgs += "xargs.push(args[i++]); arrays.push(false); ";
}
return ret;
}
function cond(type) {
if (Array.isArray(type)) {
if (type[0] == "or") {
return "(" + type.slice(1).map(cond).join(") || (") + ")";
}
if (type[0] == "and") {
return "(" + type.slice(1).map(cond).join(") && (") + ")";
}
if (type[0] == "values") {
return "(" + type.slice(1).map(function(val){
return force() + " === " + val;
}).join(") || (") + ")";
}
if (type[0] == "null") {
return "(" + cond("null") + " ? (($"+name+" = " + type[1] + "), true) : false)";
}
if (type[0] == "between" || type[0] == "[between]") {
return "(" + force() + " >= " + type[1] + " && " + "$"+name+" <= " + type[2] + " ? true : ((err = 'NUM'), false))";
}
if (type[0] == "(between)") {
return "(" + force() + " > " + type[1] + " && " + "$"+name+" < " + type[2] + " ? true : ((err = 'NUM'), false))";
}
if (type[0] == "(between]") {
return "(" + force() + " > " + type[1] + " && " + "$"+name+" <= " + type[2] + " ? true : ((err = 'NUM'), false))";
}
if (type[0] == "[between)") {
return "(" + force() + " >= " + type[1] + " && " + "$"+name+" < " + type[2] + " ? true : ((err = 'NUM'), false))";
}
if (type[0] == "assert") {
var err = type[2] || "N/A";
return "((" + type[1] + ") ? true : (err = " + JSON.stringify(err) + ", false))";
}
if (type[0] == "not") {
return "!(" + cond(type[1]) + ")";
}
throw new Error("Unknown array type condition: " + type[0]);
}
if (type == "number" || type == "datetime") {
return forceNum(true);
}
if (type == "number!") {
return "(typeof " + force() + " == 'number' ? ($"+name+" = round($"+name+"), true) : false)";
}
if (type == "integer" || type == "date") {
return "(" + forceNum() + " && (($"+name+" |= 0), true))";
}
if (type == "divisor") {
return "(" + forceNum(true) + " && ($"+name+" == 0 ? ((err = 'DIV/0'), false) : true))";
}
if (type == "number+") {
return "(" + forceNum(true) + " && ($"+name+" >= 0 ? true : ((err = 'NUM'), false)))";
}
if (type == "integer+") {
return "(" + forceNum() + " && (($"+name+" |= 0) >= 0 ? true : ((err = 'NUM'), false)))";
}
if (type == "number++") {
return "(" + forceNum(true) + " && ($"+name+" > 0 ? true : ((err = 'NUM'), false)))";
}
if (type == "integer++") {
return "(" + forceNum() + " && (($"+name+" |= 0) > 0 ? true : ((err = 'NUM'), false)))";
}
if (type == "string") {
return "((typeof " + force() + " == 'string' || typeof $"+name+" == 'boolean' || typeof $"+name+" == 'number') ? ($"+name+" += '', true) : ($"+name+" === undefined ? (($"+name+" = ''), true) : false))";
}
if (type == "boolean") {
return "(typeof " + force() + " == 'boolean')";
}
if (type == "logical") {
return "(typeof " + force() + " == 'boolean' || (typeof $"+name+" == 'number' ? ($"+name+" = !!$"+name+", true) : false))";
}
if (type == "matrix") {
force();
return "((m = this.asMatrix($"+name+")) ? ($"+name+" = m) : false)";
}
if (type == "#matrix") {
return "((m = this.asMatrix($"+name+")) ? ($"+name+" = m) : false)";
}
if (type == "ref") {
return "($"+name+" instanceof kendo.spreadsheet.Ref)";
}
if (type == "area") {
return "($"+name+" instanceof kendo.spreadsheet.CellRef || $"+name+" instanceof kendo.spreadsheet.RangeRef)";
}
if (type == "cell") {
return "($"+name+" instanceof kendo.spreadsheet.CellRef)";
}
if (type == "null") {
return "(" + force() + " == null)";
}
if (type == "anyvalue") {
return "(" + force() + " != null && i <= args.length)";
}
if (type == "forced") {
return "(" + force() + ", i <= args.length)";
}
if (type == "anything") {
return "(i <= args.length)";
}
if (type == "blank") {
return "(" + force() + " == null || $"+name+" === '')";
}
throw new Error("Can't check for type: " + type);
}
}
function limitPrecision(num) {
return num === parseInt(num, 10) ? num : +num.toPrecision(14);
}
function maybeRoundFloatErrors(num) {
if (typeof num == "number") {
return limitPrecision(num);
} else {
return num;
}
}
function withErrorHandling(obj, f, args) {
if (args instanceof CalcError) {
return args;
}
try {
return f.apply(obj, args);
} catch(ex) {
if (ex instanceof CalcError) {
return ex;
} else {
throw ex;
}
}
}
function makeSyncFunction(handler, resolve, check, arrayArgs) {
return function(callback, args) {
function doit() {
if (arrayArgs) {
var x = arrayArgs.call(this, args);
args = x.args;
if (x.width > 0 && x.height > 0) {
var result = new Matrix(this);
for (var row = 0; row < x.height; ++row) {
for (var col = 0; col < x.width; ++col) {
var xargs = [];
for (var i = 0; i < args.length; ++i) {
if (x.arrays[i]) {
xargs[i] = args[i].getNA(row, col);
} else {
xargs[i] = args[i];
}
}
xargs = check.call(this, xargs);
result.set(row, col, withErrorHandling(this, handler, xargs));
}
}
return callback(result);
}
}
var xargs = check.call(this, args);
callback(withErrorHandling(this, handler, xargs));
}
if (resolve) {
resolve.call(this, args, doit);
} else {
doit.call(this);
}
};
}
function makeAsyncFunction(handler, resolve, check, arrayArgs) {
return function(callback, args) {
function doit() {
if (arrayArgs) {
var x = arrayArgs.call(this, args);
args = x.args;
if (x.width > 0 && x.height > 0) {
var result = new Matrix(this);
var count = x.width * x.height;
var makeCallback = function(row, col) {
return function(value) {
result.set(row, col, value);
--count;
if (count === 0) {
return callback(result);
}
};
};
for (var row = 0; row < x.height && count > 0; ++row) {
for (var col = 0; col < x.width && count > 0; ++col) {
var xargs = [];
for (var i = 0; i < args.length; ++i) {
if (x.arrays[i]) {
xargs[i] = args[i].getNA(row, col);
} else {
xargs[i] = args[i];
}
}
xargs = check.call(this, xargs);
if (xargs instanceof CalcError) {
result.set(row, col, xargs);
--count;
if (count === 0) {
return callback(result);
}
} else {
xargs.unshift(makeCallback(row, col));
handler.apply(this, xargs);
}
}
}
return;
}
}
var x = check.call(this, args);
if (x instanceof CalcError) {
callback(x);
} else {
x.unshift(callback);
handler.apply(this, x);
}
}
if (resolve) {
resolve.call(this, args, doit);
} else {
doit.call(this);
}
};
}
function defineFunction(name, func) {
name = name.toLowerCase();
FUNCS[name] = func;
return {
args: function(args