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