UNPKG

@progress/kendo-ui

Version:

This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.

1,478 lines (1,351 loc) 57.7 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__(1490); module.exports = __webpack_require__(1490); /***/ }), /***/ 3: /***/ (function(module, exports) { module.exports = function() { throw new Error("define cannot be used indirect"); }; /***/ }), /***/ 1485: /***/ (function(module, exports) { module.exports = require("./runtime"); /***/ }), /***/ 1490: /***/ (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__(1485) ], __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, -W054 */ /* jshint latedef: false */ var util = kendo.util; var spreadsheet = kendo.spreadsheet; var Ref = spreadsheet.Ref; var RangeRef = spreadsheet.RangeRef; var CellRef = spreadsheet.CellRef; var NameRef = spreadsheet.NameRef; var exports = spreadsheet.calc; var runtime = exports.runtime; // Excel formula parser and compiler to JS. // some code adapted from http://lisperator.net/pltut/ // these two will hold the same operators, except that when the comma is used as decimal // separator, the union operator must be the semicolon (;) instead of comma. var OPERATORS_STANDARD = Object.create(null); var OPERATORS_COMMA = Object.create(null); (function(ops){ ops.forEach(function(cls, i){ cls.forEach(function(op){ OPERATORS_STANDARD[op] = ops.length - i; OPERATORS_COMMA[op == "," ? ";" : op] = ops.length - i; }); }); })([ [ ":" ], [ " " ], [ "," ], [ "%" ], [ "^" ], [ "*", "/" ], [ "+", "-" ], [ "&" ], [ "=", "<", ">", "<=", ">=", "<>" ] ]); var OPERATORS = OPERATORS_STANDARD; var SEPARATORS = { DEC: ".", ARG: ",", COL: "," }; function setDecimalSeparator(sep) { SEPARATORS.DEC = sep; SEPARATORS.ARG = sep == "," ? ";" : ","; SEPARATORS.COL = sep == "," ? "\\" : ","; OPERATORS = sep == "," ? OPERATORS_COMMA : OPERATORS_STANDARD; } exports.withDecimalSeparator = function(sep, f) { if (SEPARATORS.DEC == sep) { return f(); } var save = SEPARATORS.DEC; setDecimalSeparator(sep); try { return f(); } finally { setDecimalSeparator(save); } }; exports._separators = SEPARATORS; var ParseError = kendo.Class.extend({ init: function ParseError(message, pos) { this.message = message; this.pos = pos; }, toString: function() { return this.message; } }); var TRUE = { type: "bool", value: true }; var FALSE = { type: "bool", value: false }; function getcol(str) { str = str.toUpperCase(); for (var col = 0, i = 0; i < str.length; ++i) { col = col * 26 + str.charCodeAt(i) - 64; } return col - 1; } function getrow(str) { return parseInt(str, 10) - 1; } function parseReference(name, noThrow) { if (name.toLowerCase() == "#sheet") { return spreadsheet.SHEETREF; } OUT: { // this is redundant, but let's keep it fast for the most // common case — A1. If this fails, we'll try to employ the // whole tokenizer. var m; if ((m = /^(\$)?([a-z]+)(\$)?(\d+)$/i.exec(name))) { var row = getrow(m[4]), col = getcol(m[2]); if (row < 0x100000 && col < 0x4000) { return new CellRef(getrow(m[4]), getcol(m[2])); } // no NameRef-s from this function break OUT; // jshint ignore:line } var stream = TokenStream(name, {}); var a = []; while (true) { var ref = stream.next(); if (ref instanceof CellRef) { // this function always makes absolute references ref.rel = 0; } else if (ref instanceof RangeRef) { ref.topLeft.rel = 0; ref.bottomRight.rel = 0; } else { break OUT; // jshint ignore:line } a.push(ref); if (stream.eof()) { break; } if (!stream.is("op", SEPARATORS.ARG)) { break OUT; // jshint ignore:line } stream.next(); } return a.length == 1 ? a[0] : new spreadsheet.UnionRef(a); } if (!noThrow) { throw new Error("Cannot parse reference: " + name); } } function parseFormula(sheet, row, col, input) { var refs = []; input = TokenStream(input, { row: row, col: col }); var is = input.is; return { type: "exp", ast: parseExpression(true), refs: refs, sheet: sheet, row: row, col: col }; function addReference(ref) { ref.index = refs.length; refs.push(ref); return ref; } function skip(type, value, allowEOF) { if (is(type, value)) { return input.next(); } else { var tok = input.peek(); if (tok) { input.croak("Expected " + type + " «" + value + "» but found " + tok.type + " «" + tok.value + "»"); } else if (!allowEOF) { input.croak("Expected " + type + " «" + value + "»"); } } } function parseExpression(commas) { return maybeBinary(maybeIntersect(parseAtom()), 0, commas); } function parseSymbol(tok) { if (tok.upper == "TRUE" || tok.upper == "FALSE") { return tok.upper == "TRUE" ? TRUE : FALSE; } return addReference(new NameRef(tok.value)); } function parseFuncall() { var fname = input.next(); fname = fname.value; skip("punc", "("); var args = []; while (1) { if (is("punc", ")")) { break; } if (is("op", SEPARATORS.ARG)) { args.push({ type: "null" }); input.next(); continue; } args.push(parseExpression(false)); if (input.eof() || is("punc", ")")) { break; } skip("op", SEPARATORS.ARG); } skip("punc", ")", true); return { type: "func", func: fname, args: args }; } function fixReference(ref) { if (!ref.hasSheet()) { ref.setSheet(sheet); } return addReference(ref); } function parseAtom() { var exp; if (is("ref")) { exp = fixReference(input.next()); } else if (is("func")) { exp = parseFuncall(); } else if (is("punc", "(")) { input.next(); exp = parseExpression(true); skip("punc", ")", true); } else if (is("punc", "{")) { input.next(); exp = parseArray(); skip("punc", "}", true); } else if (is("num") || is("str") || is("error")) { exp = input.next(); } else if (is("sym")) { exp = parseSymbol(input.next()); } else if (is("op", "+") || is("op", "-")) { exp = { type: "prefix", op: input.next().value, exp: parseAtom() }; } else if (!input.peek()) { input.croak("Incomplete expression"); } else if (is("punc", "[")) { input.croak("External reference not supported"); } else { input.croak("Parse error"); } return maybePercent(exp); } function parseArray() { var row = [], value = [ row ], first = true; while (!input.eof() && !is("punc", "}")) { if (first) { first = false; } else if (is(null, ";")) { value.push(row = []); input.next(); } else { skip(null, SEPARATORS.COL); } row.push(parseExpression(false)); } return { type: "matrix", value: value }; } function maybeIntersect(exp) { if (is("punc", "(") || is("ref") || is("num") || is("func")) { return { type: "binary", op: " ", left: exp, right: parseExpression(false) }; } else { return exp; } } function maybePercent(exp) { if (is("op", "%")) { input.next(); return maybePercent({ type: "postfix", op: "%", exp: exp }); } else { return exp; } } function maybeBinary(left, my_prec, commas) { var tok = is("op"); if (tok && (commas || tok.value != SEPARATORS.ARG)) { var his_prec = OPERATORS[tok.value]; if (his_prec > my_prec) { input.next(); var right = maybeBinary(parseAtom(), his_prec, commas); return maybeBinary({ type: "binary", op: tok.value == ";" ? "," : tok.value, // translate union back to comma left: left, right: right }, my_prec, commas); } } return left; } } function parseNameDefinition(name, def) { var nameRef = parseFormula(null, 0, 0, name); if (!(nameRef.ast instanceof NameRef)) { throw new ParseError("Invalid name: " + name); } nameRef = nameRef.ast; if (!(def instanceof Ref)) { var defAST = parseFormula(nameRef.sheet, 0, 0, def); if (defAST.ast instanceof Ref) { def = defAST.ast; // single reference } else if (/^(?:str|num|bool|error)$/.test(defAST.ast.type)) { def = defAST.ast.value; // constant } else { def = makeFormula(defAST); // formula } } return { name: nameRef, value: def }; } var makeClosure = (function(cache){ return function(code) { var f = cache[code]; if (!f) { f = cache[code] = new Function("'use strict';return(" + code + ")")(); } return f; }; })(Object.create(null)); function makePrinter(exp) { return makeClosure("function(row, col, mod){return(" + print(exp.ast, exp, 0) + ")}"); function print(node, parent, prec) { // jshint ignore:line, because you are stupid. switch (node.type) { case "num": return "(kendo.spreadsheet.calc._separators.DEC == '.' ? " + JSON.stringify(JSON.stringify(node.value)) + " : " + JSON.stringify(JSON.stringify(node.value)) + ".replace('.' , kendo.spreadsheet.calc._separators.DEC))"; case "bool": return JSON.stringify(node.value); case "error": return JSON.stringify("#" + node.value); case "str": return JSON.stringify(JSON.stringify(node.value)); case "ref": return "this.refs[" + node.index + "].print(row, col, mod)"; case "prefix": return withParens(function(){ return JSON.stringify(node.op) + " + " + print(node.exp, node, OPERATORS[node.op]); }); case "postfix": return withParens(function(){ return print(node.exp, node, OPERATORS[node.op]) + " + " + JSON.stringify(node.op); }); case "binary": return withParens(function(){ var left = parenthesize( print(node.left, node, OPERATORS[node.op]), node.left instanceof NameRef && node.op == ":" ); var right = parenthesize( print(node.right, node, OPERATORS[node.op]), node.right instanceof NameRef && node.op == ":" ); if (/^[,;]/.test(node.op)) { return left + " + kendo.spreadsheet.calc._separators.ARG + " + right; } else { return left + " + " + JSON.stringify(node.op) + " + " + right; } }); case "func": return JSON.stringify(node.func + "(") + " + " + (node.args.length > 0 ? node.args.map(function(arg){ return print(arg, node, 0); }).join(" + kendo.spreadsheet.calc._separators.ARG + ' ' + ") : "''") + " + ')'"; case "matrix": return "'{ ' + " + node.value.map(function(el){ return el.map(function(el){ return print(el, node, 0); }).join(" + kendo.spreadsheet.calc._separators.COL + ' ' + "); }).join(" + '; ' + ") + "+ ' }'"; case "null": return "''"; } throw new Error("Cannot make printer for node " + node.type); function withParens(f) { var op = node.op; var needParens = (OPERATORS[op] < prec || (!prec && op == ",") || (parent.type == "prefix" && prec == OPERATORS[op] && parent.op == "-") || (parent.type == "binary" && prec == OPERATORS[op] && node === parent.right)); return parenthesize(f(), needParens); } } function parenthesize(code, cond) { return cond ? "'(' + " + code + " + ')'" : code; } } function toCPS(ast, k) { var GENSYM = 0; return cps(ast, k); function cps(node, k){ switch (node.type) { case "ref" : return cpsRef(node, k); case "num" : case "str" : case "null" : case "error" : case "bool" : return cpsAtom(node, k); case "prefix" : case "postfix" : return cpsUnary(node, k); case "binary" : return cpsBinary(node, k); case "func" : return cpsFunc(node, k); case "lambda" : return cpsLambda(node, k); case "matrix" : return cpsMatrix(node.value, k, true); } throw new Error("Cannot CPS " + node.type); } function cpsRef(node, k) { return node.ref == "name" ? cpsNameRef(node, k) : cpsAtom(node, k); } function cpsAtom(node, k) { return k(node); } function cpsNameRef(node, k) { return { type: "func", func: ",getname", args: [ makeContinuation(k), node ] }; } function cpsUnary(node, k) { return cps({ type: "func", func: "unary" + node.op, args: [ node.exp ] }, k); } function cpsBinary(node, k) { return cps({ type: "func", func: "binary" + node.op, args: [ node.left, node.right ] }, k); } function cpsIf(co, th, el, k) { return cps(co, function(co){ // compile THEN and ELSE into a lambda which takes a callback to invoke with the // result of the branches, and the IF itself will become a call the internal "if" // function. var rest = makeContinuation(k); var thenK = gensym("T"); var elseK = gensym("E"); return { type: "func", func: "if", args: [ rest, co, // condition { // then type: "lambda", vars: [ thenK ], body: cps(th || TRUE, function(th){ return { type: "call", func: { type: "var", name: thenK }, args: [ th ] }; }) }, { // else type: "lambda", vars: [ elseK ], body: cps(el || FALSE, function(el){ return { type: "call", func: { type: "var", name: elseK }, args: [ el ] }; }) } ] }; }); } function cpsAnd(args, k) { if (args.length === 0) { return cpsAtom(TRUE, k); } return cps({ type: "func", func: "IF", args: [ // first item args[0], // if true, apply AND for the rest { type: "func", func: "AND", args: args.slice(1) }, // otherwise return false FALSE ] }, k); } function cpsOr(args, k) { if (args.length === 0) { return cpsAtom(FALSE, k); } return cps({ type: "func", func: "IF", args: [ // first item args[0], // if true, return true TRUE, // otherwise apply OR for the rest { type: "func", func: "OR", args: args.slice(1) } ] }, k); } function cpsFunc(node, k) { switch (node.func.toLowerCase()) { case "if": return cpsIf(node.args[0], node.args[1], node.args[2], k); case "and": return cpsAnd(node.args, k); case "or": return cpsOr(node.args, k); case "true": return k(TRUE); case "false": return k(FALSE); } // actual function return (function loop(args, i){ if (i == node.args.length) { return { type : "func", func : node.func, args : args }; } else { return cps(node.args[i], function(value){ return loop(args.concat([ value ]), i + 1); }); } })([ makeContinuation(k) ], 0); } function cpsLambda(node, k) { var cont = gensym("K"); var body = cps(node.body, function(body){ return { type: "call", func: { type: "var", value: cont }, args: [ body ] }; }); return k({ type: "lambda", vars: [ cont ].concat(node.vars), body: body }); } function cpsMatrix(elements, k, isMatrix) { var a = []; return (function loop(i){ if (i == elements.length) { return k({ type: "matrix", value: a }); } else { return (isMatrix ? cpsMatrix : cps)(elements[i], function(val){ a[i] = val; return loop(i + 1); }); } })(0); } function makeContinuation(k) { var cont = gensym("R"); return { type : "lambda", vars : [ cont ], body : k({ type: "var", name: cont }) }; } function gensym(name) { if (!name) { name = ""; } name = "_" + name; return name + (++GENSYM); } } var FORMULA_CACHE = Object.create(null); function makeFormula(exp) { var printer = makePrinter(exp); var hash = printer.call(exp); // needs .refs var formula = FORMULA_CACHE[hash]; if (formula) { // we need to clone because formulas cache the result; even if the formula is the same, // its value will depend on its location, hence we need different objects. Still, using // this cache is a good idea because we'll reuse the same refs array, handler and // printer instead of allocating new ones (and we skip compiling it). return formula.clone(exp.sheet, exp.row, exp.col); } var code = js(toCPS(exp.ast, function(ret){ return { type: "return", value: ret }; })); code = [ "function(){", "var context = this, refs = context.formula.absrefs", code, "}" ].join(";\n"); formula = new runtime.Formula(exp.refs, makeClosure(code), printer, exp.sheet, exp.row, exp.col); FORMULA_CACHE[hash] = formula.clone(exp.sheet, exp.row, exp.col); return formula; function js(node){ var type = node.type; if (type == "num") { return node.value + ""; } else if (type == "str") { return JSON.stringify(node.value); } else if (type == "error") { return "context.error(" + JSON.stringify(node.value) + ")"; } else if (type == "return") { return "context.resolve(" + js(node.value) + ")"; } else if (type == "func") { return "context.func(" + JSON.stringify(node.func) + ", " + js(node.args[0]) + ", " // the callback + jsArray(node.args.slice(1)) // the arguments + ")"; } else if (type == "call") { return js(node.func) + "(" + node.args.map(js).join(", ") + ")"; } else if (type == "ref") { return "refs[" + node.index + "]"; } else if (type == "bool") { return "" + node.value; } else if (type == "if") { return "(context.bool(" + js(node.co) + ") ? " + js(node.th) + " : " + js(node.el) + ")"; } else if (type == "lambda") { return "(function(" + node.vars.join(", ") + "){ return(" + js(node.body) + ") })"; } else if (type == "var") { return node.name; } else if (type == "matrix") { return jsArray(node.value); } else if (type == "null") { return "null"; } else { throw new Error("Cannot compile expression " + type); } } function jsArray(a) { return "[ " + a.map(js).join(", ") + " ]"; } } function identity(x) { return x; } function TokenStream(input, options) { input = RawTokenStream(InputStream(input), options); var ahead = input.ahead; var skip = input.skip; var token = null; var fixCell = options.row != null && options.col != null ? function(cell) { if (cell.rel & 1) { cell.col -= options.col; } if (cell.rel & 2) { cell.row -= options.row; } return cell; } : identity; var addPos = options.forEditor ? function(thing, startToken, endToken) { thing.begin = startToken.begin; thing.end = endToken.end; return thing; } : identity; return { peek : peek, next : next, croak : input.croak, eof : input.eof, is : is }; function is(type, value) { var tok = peek(); return tok != null && (type == null || tok.type === type) && (value == null || tok.value === value) ? tok : null; } function peek() { if (token == null) { token = readNext(); } return token; } function next() { if (token != null) { var tmp = token; token = null; return tmp; } return readNext(); } function readNext() { var ret; var t = input.peek(); if (t) { if (t.type == "sym" || t.type == "rc" || t.type == "num") { ret = ahead(8, refRange3D) || ahead(6, refCell3D) || ahead(6, refSheetRange) || ahead(4, refSheetCell) || ahead(4, refRange) || ahead(2, refCell) || ahead(2, funcall); } if (!ret) { ret = input.next(); } } return ret; } function toCell(tok, isFirst) { if (tok.type == "rc") { // RC notation is read properly without knowing where // we are, so no need to fixCell on this one. // However, if only absolute refs were asked for (from // i.e. parseReference) I feel it's alright to yell // about it here. if (tok.rel && !options.forEditor && (options.row == null || options.col == null)) { input.croak("Cannot read relative cell in RC notation"); } return new CellRef(tok.row, tok.col, tok.rel); } if (tok.type == "num") { if (tok.value <= 1048577) { // whole row return fixCell(new CellRef( getrow(tok.value), isFirst ? -Infinity : +Infinity, 2 )); } else { return null; } } // otherwise it's "sym". The OOXML spec (SpreadsheetML // 18.2.5) defines the maximum value to be interpreted as // a cell reference to be XFD1048576. var name = tok.value; var m = /^(\$)?([a-z]+)(\$)?(\d+)$/i.exec(name); if (m) { var row = getrow(m[4]), col = getcol(m[2]); if (row <= 1048576 && col <= 16383) { return fixCell(new CellRef( getrow(m[4]), getcol(m[2]), (m[1] ? 0 : 1) | (m[3] ? 0 : 2) )); } else { return null; } } var abs = name.charAt(0) == "$"; if (abs) { name = name.substr(1); } if (/^\d+$/.test(name)) { var row = getrow(name); if (row <= 1048576) { return fixCell(new CellRef( getrow(name), isFirst ? -Infinity : +Infinity, (abs ? 0 : 2) )); } } else { var col = getcol(name); if (col <= 16383) { return fixCell(new CellRef( isFirst ? -Infinity : +Infinity, getcol(name), (abs ? 0 : 1) )); } } } // Sheet1(a) :(b) Sheet2(c) !(d) A1(e) :(f) C3(g) not followed by paren (h) function refRange3D(a, b, c, d, e, f, g, h) { if (a.type == "sym" && b.type == "op" && b.value == ":" && c.type == "sym" && d.type == "punc" && d.value == "!" && (e.type == "sym" || e.type == "rc" || (e.type == "num" && e.value == e.value|0)) && f.type == "op" && f.value == ":" && (g.type == "sym" || g.type == "rc" || (g.type == "num" && g.value == g.value|0)) && g.type == e.type && !(h.type == "punc" && h.value == "(" && !g.space)) { var tl = toCell(e, true), br = toCell(g, false); if (tl && br) { // skip them except the last one, we only wanted to // ensure it's not paren. skip(7); return addPos(new RangeRef( tl.setSheet(a.value, true), br.setSheet(c.value, true) ).setSheet(a.value, true), a, g); } } } // Sheet1(a) :(b) Sheet2(c) !(d) A1(e) not followed by paren (f) function refCell3D(a, b, c, d, e, f) { if (a.type == "sym" && b.type == "op" && b.value == ":" && c.type == "sym" && d.type == "punc" && d.value == "!" && (e.type == "sym" || e.type == "rc" || (e.type == "num" && e.value == e.value|0)) && !(f.type == "punc" && f.value == "(" && !e.space)) { var tl = toCell(e); if (tl) { skip(5); var br = tl.clone(); return addPos(new RangeRef( tl.setSheet(a.value, true), br.setSheet(c.value, true) ).setSheet(a.value, true), a, e); } } } // Sheet1(a) !(b) A1(c) :(d) C3(e) not followed by paren (f) function refSheetRange(a, b, c, d, e, f) { if (a.type == "sym" && b.type == "punc" && b.value == "!" && (c.type == "sym" || c.type == "rc" || (c.type == "num" && c.value == c.value|0)) && d.type == "op" && d.value == ":" && (e.type == "sym" || e.type == "rc" || (e.type == "num" && e.value == e.value|0)) && !(f.type == "punc" && f.value == "(" && !e.space)) { var tl = toCell(c, true), br = toCell(e, false); if (tl && br) { skip(5); return addPos(new RangeRef(tl, br).setSheet(a.value, true), a, e); } } } // Sheet1(a) !(b) A1(c) not followed by paren (d) function refSheetCell(a, b, c, d) { if (a.type == "sym" && b.type == "punc" && b.value == "!" && (c.type == "sym" || c.type == "rc" || (c.type == "num" && c.value == c.value|0)) && !(d.type == "punc" && d.value == "(" && !c.space)) { skip(3); var x = toCell(c); if (!x || !isFinite(x.row)) { x = new NameRef(c.value); } return addPos(x.setSheet(a.value, true), a, c); } } // A1(a) :(b) C3(c) not followed by paren (d) function refRange(a, b, c, d) { if ((a.type == "sym" || a.type == "rc" || (a.type == "num" && a.value == a.value|0)) && (b.type == "op" && b.value == ":") && (c.type == "sym" || c.type == "rc" || (c.type == "num" && c.value == c.value|0)) && !(d.type == "punc" && d.value == "(" && !c.space)) { var tl = toCell(a, true), br = toCell(c, false); if (tl && br) { skip(3); return addPos(new RangeRef(tl, br), a, c); } } } // A1(a) not followed by paren (b) function refCell(a, b) { if ((a.type == "sym" || a.type == "rc") && !(b.type == "punc" && b.value == "(" && !a.space)) { var x = toCell(a); if (x && isFinite(x.row) && isFinite(x.col)) { skip(1); return addPos(x, a, a); } } } function funcall(a, b) { if (a.type == "sym" && b.type == "punc" && b.value == "(" && !a.space) { a.type = "func"; skip(1); return a; // already has position } } } function isWhitespace(ch) { return " \t\r\n\xa0\u200b".indexOf(ch) >= 0; } var EOF = { type: "eof" }; function RawTokenStream(input, options) { var tokens = [], index = 0; var readWhile = input.readWhile; return { next : next, peek : peek, eof : eof, croak : input.croak, ahead : ahead, skip : skip }; function isDigit(ch) { return (/[0-9]/i.test(ch)); } function isIdStart(ch) { return (/[a-z$_]/i.test(ch) || util.isUnicodeLetter(ch)); } function isId(ch) { return isIdStart(ch) || isDigit(ch) || ch == "."; } function isOpChar(ch) { return ch in OPERATORS; } function isPunc(ch) { return "\\!;(){}[]".indexOf(ch) >= 0; } function readNumber() { // XXX: TODO: exponential notation var has_dot = false; var number = readWhile(function(ch){ if (ch == SEPARATORS.DEC) { if (has_dot) { return false; } has_dot = true; return true; } return isDigit(ch); }); if (number == SEPARATORS.DEC) { return { type: "punc", value: SEPARATORS.DEC }; } else { return { type: "num", value: parseFloat(number.replace(SEPARATORS.DEC, ".")) }; } } function symbol(id, quote) { return { type : "sym", value : id, upper : id.toUpperCase(), space : isWhitespace(input.peek()), quote : quote }; } function getRC(a, b, c) { if (!a && !b && !c) { return null; } if ((!a && !c) || (a && c)) { var num = b ? parseInt(b, 10) : 0; return a ? num : num - 1; } } function readSymbol() { var m = input.lookingAt(/^R(\[)?(-?[0-9]+)?(\])?C(\[)?(-?[0-9]+)?(\])?/i); if (m) { var row = getRC(m[1], m[2], m[3]); var col = getRC(m[4], m[5], m[6]); if (row != null && col != null) { input.skip(m); return { type: "rc", row: row, col: col, rel: ((m[4] || !(m[4] || m[5] || m[6]) ? 1 : 0) // col | (m[1] || !(m[1] || m[2] || m[3]) ? 2 : 0) // row ) }; } } return symbol(readWhile(isId)); } function readString() { input.next(); return { type: "str", value: input.readEscaped('"') }; } function readSheetName() { input.next(); return symbol(input.readEscaped("'"), true); } function readOperator() { return { type : "op", value : readWhile(function(ch, op){ return (op + ch) in OPERATORS; }) }; } function readPunc() { return { type : "punc", value : input.next() }; } function readNext() { if (input.eof()) { return null; } var ch = input.peek(), m; if (ch == '"') { return readString(); } if (ch == "'") { return readSheetName(); } if (isDigit(ch) || ch == SEPARATORS.DEC) { return readNumber(); } if (isIdStart(ch)) { return readSymbol(); } if (isOpChar(ch)) { return readOperator(); } if (isPunc(ch)) { return readPunc(); } if ((m = input.lookingAt(/^#([a-z\/]+)[?!]?/i))) { input.skip(m); return { type: "error", value: m[1] }; } if (!options.forEditor) { input.croak("Can't handle character with code: " + ch.charCodeAt(0)); } return { type: "error", value: input.next() }; } function peek() { while (tokens.length <= index) { readWhile(isWhitespace); var begin = input.pos(); var tok = readNext(); if (options.forEditor && tok) { tok.begin = begin; tok.end = input.pos(); } tokens.push(tok); } return tokens[index]; } function next() { var tok = peek(); if (tok) { index++; } return tok; } function ahead(n, f) { var pos = index, a = []; while (n-- > 0) { a.push(next() || EOF); } index = pos; return f.apply(a, a); } function skip(n) { index += n; } function eof() { return peek() == null; } } function InputStream(input) { var pos = 0, line = 1, col = 0; return { next : next, peek : peek, eof : eof, croak : croak, readWhile : readWhile, readEscaped : readEscaped, lookingAt : lookingAt, skip : skip, forward : forward, pos : location }; function location() { // jshint ignore:line, :-( return pos; } function next() { var ch = input.charAt(pos++); if (ch == "\n") { line++; col = 0; } else { col++; } return ch; } function peek() { return input.charAt(pos); } function eof() { return peek() === ""; } function croak(msg) { throw new ParseError(msg + " (input: " + input + ")", pos); } function skip(ch) { if (typeof ch == "string") { if (input.substr(pos, ch.length) != ch) { croak("Expected " + ch); } forward(ch.length); } else if (ch instanceof RegExp) { var m = ch.exec(input.substr(pos)); if (m) { forward(m[0].length); return m; } } else { // assuming RegExp match data forward(ch[0].length); } } function forward(n) { while (n-- > 0) { next(); } } function readEscaped(end) { var escaped = false, str = ""; while (!eof()) { var ch = next(); if (escaped) { str += ch; escaped = false; } else if (ch == "\\") { escaped = true; } else if (ch == end) { break; } else { str += ch; } } return str; } function readWhile(predicate) { var str = ""; while (!eof() && predicate(peek(), str)) { str += next(); } return str; } function lookingAt(rx) { return rx.exec(input.substr(pos)); } } //// exports var FORMAT_PARSERS = []; var registerFormatParser = exports.registerFormatParser = function(p) { FORMAT_PARSERS.push(p); }; exports.parse = function(sheet, row, col, input, format) { if (input instanceof Date) { return { type: "date", value: runtime.dateToSerial(input) }; } if (typeof input == "number") { return { type: "number", value: input }; } if (typeof input == "boolean") { return { type: "boolean", value: input }; } input += ""; if (/^'/.test(input)) { return { type: "string", value: input.substr(1) }; } // trivial (integer) percent values; more complex formats are handled below via // registerFormatParser; this case could be dropped completely. if (/^-?[0-9]+%$/.test(input)) { var str = input.substr(0, input.length - 1); var num = parseFloat(str); if (!isNaN(num) && num == str) { return { type: "percent", value: num / 100 }; } } if (/^=/.test(input)) { input = input.substr(1); if (/\S/.test(input)) { return parseFormula(sheet, row, col, input); } else { return { type: "string", value: "=" + input }; } } for (var i = 0; i < FORMAT_PARSERS.length; ++i) { var result = FORMAT_PARSERS[i](input); if (result) { return result; } } if (input.toLowerCase() == "true") { return { type: "boolean", value: true }; } if (input.toLowerCase() == "false") { return { type: "boolean", value: false }; } var date = runtime.parseDate(input, format); if (date) { return { type: "date", value: runtime.dateToSerial(date) }; } var num = parseFloat(input); if (!isNaN(num) && input.length > 0 && num == input) { format = null; if (num != Math.floor(num)) { format = "0." + String(num).split(".")[1].replace(/\d/g, "0"); } return { type: "number", value: num, format: format }; } return { type: "string", value: input }; }; function tokenize(input, row, col) { var tokens = []; input = TokenStream(input, { forEditor: true, row: row, col: col }); while (!input.eof()) { tokens.push(next()); } var tok = tokens[0]; if (tok.type == "op" && tok.value == "=") { tok.type = "startexp"; } return tokens; function next() { var tok = input.next(); if (tok.type == "sym") { if (tok.upper == "TRUE") { tok.type = "bool"; tok.value = true; } else if (tok.upper == "FALSE") { tok.type = "bool"; tok.value = false; } } else if (tok.type == "ref") { tok = { type : "ref", ref : (row != null && col != null ? tok.absolute(row, col) : tok), begin : t