cione-comp-lib
Version:
`$ yarn add cione-comp-lib` 或者 `$ npm i cione-comp-lib -S`
251 lines (250 loc) • 8.37 kB
JavaScript
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 };