UNPKG

@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
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