UNPKG

cione-comp-lib

Version:

`$ yarn add cione-comp-lib` 或者 `$ npm i cione-comp-lib -S`

251 lines (250 loc) 8.37 kB
import { normalizeUnit } from "./conversions.mjs"; import { ZERO, numberNode } from "./parsers.mjs"; /* @license * Copyright 2019 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an 'AS IS' BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var _a, _b, _c; const $evaluate = Symbol("evaluate"); const $lastValue = Symbol("lastValue"); class Evaluator { constructor() { this[_a] = null; } static evaluatableFor(node, basis = ZERO) { if (node instanceof Evaluator) { return node; } if (node.type === "number") { if (node.unit === "%") { return new PercentageEvaluator(node, basis); } return node; } switch (node.name.value) { case "calc": return new CalcEvaluator(node, basis); case "env": return new EnvEvaluator(node); } return ZERO; } static evaluate(evaluatable) { if (evaluatable instanceof Evaluator) { return evaluatable.evaluate(); } return evaluatable; } static isConstant(evaluatable) { if (evaluatable instanceof Evaluator) { return evaluatable.isConstant; } return true; } static applyIntrinsics(evaluated, intrinsics) { const { basis, keywords } = intrinsics; const { auto } = keywords; return basis.map((basisNode, index) => { const autoSubstituteNode = auto[index] == null ? basisNode : auto[index]; let evaluatedNode = evaluated[index] ? evaluated[index] : autoSubstituteNode; if (evaluatedNode.type === "ident") { const keyword = evaluatedNode.value; if (keyword in keywords) { evaluatedNode = keywords[keyword][index]; } } if (evaluatedNode == null || evaluatedNode.type === "ident") { evaluatedNode = autoSubstituteNode; } if (evaluatedNode.unit === "%") { return numberNode(evaluatedNode.number / 100 * basisNode.number, basisNode.unit); } evaluatedNode = normalizeUnit(evaluatedNode, basisNode); if (evaluatedNode.unit !== basisNode.unit) { return basisNode; } return evaluatedNode; }); } get isConstant() { return false; } evaluate() { if (!this.isConstant || this[$lastValue] == null) { this[$lastValue] = this[$evaluate](); } return this[$lastValue]; } } _a = $lastValue; const $percentage = Symbol("percentage"); const $basis = Symbol("basis"); class PercentageEvaluator extends Evaluator { constructor(percentage, basis) { super(); this[$percentage] = percentage; this[$basis] = basis; } get isConstant() { return true; } [$evaluate]() { return numberNode(this[$percentage].number / 100 * this[$basis].number, this[$basis].unit); } } const $identNode = Symbol("identNode"); class EnvEvaluator extends Evaluator { constructor(envFunction) { super(); this[_b] = null; const identNode = envFunction.arguments.length ? envFunction.arguments[0].terms[0] : null; if (identNode != null && identNode.type === "ident") { this[$identNode] = identNode; } } get isConstant() { return false; } [(_b = $identNode, $evaluate)]() { if (this[$identNode] != null) { switch (this[$identNode].value) { case "window-scroll-y": const verticalScrollPosition = window.pageYOffset; const verticalScrollMax = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight); const scrollY = verticalScrollPosition / (verticalScrollMax - window.innerHeight) || 0; return { type: "number", number: scrollY, unit: null }; } } return ZERO; } } const IS_MULTIPLICATION_RE = /[\*\/]/; const $evaluator = Symbol("evaluator"); class CalcEvaluator extends Evaluator { constructor(calcFunction, basis = ZERO) { super(); this[_c] = null; if (calcFunction.arguments.length !== 1) { return; } const terms = calcFunction.arguments[0].terms.slice(); const secondOrderTerms = []; while (terms.length) { const term = terms.shift(); if (secondOrderTerms.length > 0) { const previousTerm = secondOrderTerms[secondOrderTerms.length - 1]; if (previousTerm.type === "operator" && IS_MULTIPLICATION_RE.test(previousTerm.value)) { const operator = secondOrderTerms.pop(); const leftValue = secondOrderTerms.pop(); if (leftValue == null) { return; } secondOrderTerms.push(new OperatorEvaluator(operator, Evaluator.evaluatableFor(leftValue, basis), Evaluator.evaluatableFor(term, basis))); continue; } } secondOrderTerms.push(term.type === "operator" ? term : Evaluator.evaluatableFor(term, basis)); } while (secondOrderTerms.length > 2) { const [left, operator, right] = secondOrderTerms.splice(0, 3); if (operator.type !== "operator") { return; } secondOrderTerms.unshift(new OperatorEvaluator(operator, Evaluator.evaluatableFor(left, basis), Evaluator.evaluatableFor(right, basis))); } if (secondOrderTerms.length === 1) { this[$evaluator] = secondOrderTerms[0]; } } get isConstant() { return this[$evaluator] == null || Evaluator.isConstant(this[$evaluator]); } [(_c = $evaluator, $evaluate)]() { return this[$evaluator] != null ? Evaluator.evaluate(this[$evaluator]) : ZERO; } } const $operator = Symbol("operator"); const $left = Symbol("left"); const $right = Symbol("right"); class OperatorEvaluator extends Evaluator { constructor(operator, left, right) { super(); this[$operator] = operator; this[$left] = left; this[$right] = right; } get isConstant() { return Evaluator.isConstant(this[$left]) && Evaluator.isConstant(this[$right]); } [$evaluate]() { const leftNode = normalizeUnit(Evaluator.evaluate(this[$left])); const rightNode = normalizeUnit(Evaluator.evaluate(this[$right])); const { number: leftValue, unit: leftUnit } = leftNode; const { number: rightValue, unit: rightUnit } = rightNode; if (rightUnit != null && leftUnit != null && rightUnit != leftUnit) { return ZERO; } const unit = leftUnit || rightUnit; let value; switch (this[$operator].value) { case "+": value = leftValue + rightValue; break; case "-": value = leftValue - rightValue; break; case "/": value = leftValue / rightValue; break; case "*": value = leftValue * rightValue; break; default: return ZERO; } return { type: "number", number: value, unit }; } } const $evaluatables = Symbol("evaluatables"); const $intrinsics = Symbol("intrinsics"); class StyleEvaluator extends Evaluator { constructor(expressions, intrinsics) { super(); this[$intrinsics] = intrinsics; const firstExpression = expressions[0]; const terms = firstExpression != null ? firstExpression.terms : []; this[$evaluatables] = intrinsics.basis.map((basisNode, index) => { const term = terms[index]; if (term == null) { return { type: "ident", value: "auto" }; } if (term.type === "ident") { return term; } return Evaluator.evaluatableFor(term, basisNode); }); } get isConstant() { for (const evaluatable of this[$evaluatables]) { if (!Evaluator.isConstant(evaluatable)) { return false; } } return true; } [$evaluate]() { const evaluated = this[$evaluatables].map((evaluatable) => Evaluator.evaluate(evaluatable)); return Evaluator.applyIntrinsics(evaluated, this[$intrinsics]).map((numberNode2) => numberNode2.number); } } export { CalcEvaluator, EnvEvaluator, Evaluator, OperatorEvaluator, PercentageEvaluator, StyleEvaluator };