@visactor/vtable
Version:
canvas table width high performance
206 lines (179 loc) • 6.77 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: !0
}), exports.isPercent = exports.couldBeValidNumber = exports.toPx = void 0;
const vutils_1 = require("@visactor/vutils"), helper_1 = require("./helper"), TYPE_PAREN = 0, TYPE_UNIT = 1, TYPE_OPERATOR = 2, TYPE_NUMBER = 3, NODE_TYPE_UNIT = 10, NODE_TYPE_BINARY_EXPRESSION = 11, NODE_TYPE_NUMBER = 12, TABULATION = 9, CARRIAGE_RETURN = 13, LINE_FEED = 10, FORM_FEED = 12, SPACE = 32, PERCENT = 37, FULL_STOP = 46, DIGIT_0 = 48, DIGIT_9 = 57, LATIN_CAPITAL_A = 65, LATIN_CAPITAL_Z = 90, LATIN_SMALL_A = 97, LATIN_SMALL_Z = 122;
function isUpperLetter(cp) {
return cp >= LATIN_CAPITAL_A && cp <= LATIN_CAPITAL_Z;
}
function isLowerLetter(cp) {
return cp >= LATIN_SMALL_A && cp <= LATIN_SMALL_Z;
}
function isLetter(cp) {
return isLowerLetter(cp) || isUpperLetter(cp);
}
function isWhitespace(cp) {
return cp === TABULATION || cp === LINE_FEED || cp === FORM_FEED || cp === CARRIAGE_RETURN || cp === SPACE;
}
function isDigit(cp) {
return cp >= DIGIT_0 && cp <= DIGIT_9;
}
function isDot(cp) {
return cp === FULL_STOP;
}
function isUnit(cp) {
return isLetter(cp) || cp === PERCENT;
}
function createError(calc) {
return new Error(`calc parse error: ${calc}`);
}
function tokenize(calc) {
const exp = calc.replace(/calc\(/g, "(").trim(), tokens = [], len = exp.length;
for (let index = 0; index < len; index++) {
const c = exp[index], cp = c.charCodeAt(0);
if ("(" === c || ")" === c) tokens.push({
value: c,
type: TYPE_PAREN
}); else if ("*" === c || "/" === c) tokens.push({
value: c,
type: TYPE_OPERATOR
}); else if ("+" === c || "-" === c) index = parseSign(c, index + 1) - 1; else if (isDigit(cp) || isDot(cp)) index = parseNum(c, index + 1) - 1; else if (!isWhitespace(cp)) throw createError(calc);
}
function parseSign(sign, start) {
if (start < len) {
const c = exp[start], cp = c.charCodeAt(0);
if (isDigit(cp) || isDot(cp)) return parseNum(sign + c, start + 1);
}
return tokens.push({
value: sign,
type: TYPE_OPERATOR
}), start;
}
function parseNum(num, start) {
let index = start;
for (;index < len; index++) {
const c = exp[index], cp = c.charCodeAt(0);
if (isDigit(cp)) num += c; else {
if ("." !== c) {
if (isUnit(cp)) return parseUnit(num, c, index + 1);
break;
}
if (num.indexOf(".") >= 0) throw createError(calc);
num += c;
}
}
if ("." === num) throw createError(calc);
return tokens.push({
value: parseFloat(num),
type: TYPE_NUMBER
}), index;
}
function parseUnit(num, unit, start) {
let index = start;
for (;index < len; index++) {
const c = exp[index];
if (!isUnit(c.charCodeAt(0))) break;
unit += c;
}
return tokens.push({
value: parseFloat(num),
unit: unit,
type: TYPE_UNIT
}), index;
}
return tokens;
}
const PRECEDENCE = {
"*": 3,
"/": 3,
"+": 2,
"-": 2
};
function lex(tokens, calc) {
function buildBinaryExpNode(stack) {
const right = stack.pop(), op = stack.pop(), left = stack.pop();
if (!(left && left.nodeType && op && op.type === TYPE_OPERATOR && right && right.nodeType)) throw createError(calc);
return {
nodeType: NODE_TYPE_BINARY_EXPRESSION,
left: left,
op: op,
right: right
};
}
const stack = [];
for (;tokens.length; ) {
const token = tokens.shift();
if (token.type === TYPE_PAREN && "(" === token.value) {
let deep = 0;
const closeIndex = helper_1.array.findIndex(tokens, (t => {
if (t.type === TYPE_PAREN && "(" === t.value) deep++; else if (t.type === TYPE_PAREN && ")" === t.value) {
if (!deep) return !0;
deep--;
}
return !1;
}));
if (-1 === closeIndex) throw createError(calc);
stack.push(lex(tokens.splice(0, closeIndex), calc)), tokens.shift();
} else if (token.type === TYPE_OPERATOR) {
if (stack.length >= 3) {
const beforeOp = stack[stack.length - 2].value;
PRECEDENCE[token.value] <= PRECEDENCE[beforeOp] && stack.push(buildBinaryExpNode(stack));
}
stack.push(token);
} else if (token.type === TYPE_UNIT) {
const {value: num, unit: unit} = token;
stack.push({
nodeType: NODE_TYPE_UNIT,
value: num,
unit: unit
});
} else token.type === TYPE_NUMBER && stack.push({
nodeType: NODE_TYPE_NUMBER,
value: token.value
});
}
for (;stack.length > 1; ) stack.push(buildBinaryExpNode(stack));
return stack[0];
}
function parse(calcStr) {
return lex(tokenize(calcStr), calcStr);
}
function calcNode(node, context) {
if (node.nodeType === NODE_TYPE_BINARY_EXPRESSION) {
const left = calcNode(node.left, context), right = calcNode(node.right, context);
switch (node.op.value) {
case "+":
return left + right;
case "-":
return left - right;
case "*":
return left * right;
case "/":
return left / right;
default:
throw new Error(`calc error. unknown operator: ${node.op.value}`);
}
} else if (node.nodeType === NODE_TYPE_UNIT) switch (node.unit) {
case "%":
return node.value * context.full / 100;
case "px":
return node.value;
default:
throw new Error(`calc error. unknown unit: ${node.unit}`);
} else if (node.nodeType === NODE_TYPE_NUMBER) return node.value;
throw new Error("calc error.");
}
function toPxInternal(value, context) {
return calcNode(parse(value), context);
}
function toPx(value, context) {
return "string" == typeof value ? toPxInternal(value.trim(), context) : value - 0;
}
function couldBeValidNumber(v) {
return !(0, vutils_1.isNil)(v) && (!!(0, vutils_1.isNumber)(v) || +v == +v);
}
function isPercent(v) {
return !!(0, vutils_1.isString)(v) && (!!v.endsWith("%") && couldBeValidNumber(v.substring(0, v.length - 1)));
}
exports.toPx = toPx, exports.couldBeValidNumber = couldBeValidNumber, exports.isPercent = isPercent;
//# sourceMappingURL=calc.js.map