@phin-tech/dottie 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PARITY.md +2 -0
- package/README.md +15 -0
- package/dist/compile.js +22 -9
- package/dist/dottie-compile.mjs +30 -11
- package/dist/dottie-logic.min.js +2171 -0
- package/dist/dottie-player.js +483 -141
- package/dist/dottie-player.min.js +58 -56
- package/dist/dottie-sim.min.js +5 -4
- package/dist/dottie.js +483 -141
- package/dist/types/clicks.d.ts +3 -2
- package/dist/types/compile.d.ts +3 -0
- package/dist/types/expression.d.ts +51 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/logic-entry.d.ts +1 -0
- package/dist/types/logic-loader.d.ts +9 -0
- package/dist/types/logic-polyfill.d.ts +2 -0
- package/dist/types/logic.d.ts +32 -0
- package/dist/types/measure.d.ts +4 -3
- package/dist/types/model.d.ts +10 -3
- package/dist/types/player.d.ts +8 -3
- package/dist/types/state.d.ts +14 -13
- package/package.json +3 -1
package/dist/dottie.js
CHANGED
|
@@ -1,84 +1,334 @@
|
|
|
1
|
-
/*! dottie.js 0.
|
|
1
|
+
/*! dottie.js 0.11.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __defNormalProp = (obj, key, value2) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value: value2 }) : obj[key] = value2;
|
|
4
4
|
var __publicField = (obj, key, value2) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value2);
|
|
5
5
|
|
|
6
|
-
// src/
|
|
6
|
+
// src/expression.ts
|
|
7
|
+
var METRIC_NAMES = ["throughput", "load", "delay", "backlog", "depth", "inFlight", "transit", "backup"];
|
|
8
|
+
var FUNCTIONS = { min: null, max: null, abs: 1, floor: 1, ceil: 1, round: 1, clamp: 3 };
|
|
7
9
|
var MAX_NAME = 64;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
var truthy = (v) => typeof v === "boolean" ? v : v !== 0 && !Number.isNaN(v);
|
|
11
|
+
var num = (v) => typeof v === "boolean" ? v ? 1 : 0 : v;
|
|
12
|
+
var parsed = /* @__PURE__ */ new Map();
|
|
13
|
+
function parseExpr(text) {
|
|
14
|
+
const hit = parsed.get(text);
|
|
15
|
+
if (hit !== void 0) return hit;
|
|
16
|
+
const p = new Parser(text);
|
|
17
|
+
let e = p.or();
|
|
18
|
+
if (e && !p.atEnd()) e = null;
|
|
19
|
+
if (parsed.size > 4096) parsed.clear();
|
|
20
|
+
parsed.set(text, e);
|
|
21
|
+
return e;
|
|
10
22
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
23
|
+
function evaluate(e, state, metric = null) {
|
|
24
|
+
switch (e.k) {
|
|
25
|
+
case "lit":
|
|
26
|
+
return e.v;
|
|
27
|
+
case "name":
|
|
28
|
+
return state[e.name] ?? false;
|
|
29
|
+
case "metric":
|
|
30
|
+
return metric?.(e.metric, e.target) ?? NaN;
|
|
31
|
+
case "not":
|
|
32
|
+
return !truthy(evaluate(e.e, state, metric));
|
|
33
|
+
case "neg":
|
|
34
|
+
return -num(evaluate(e.e, state, metric));
|
|
35
|
+
case "call": {
|
|
36
|
+
const xs = e.args.map((a) => num(evaluate(a, state, metric)));
|
|
37
|
+
const fold = (better) => {
|
|
38
|
+
if (xs.some(Number.isNaN)) return NaN;
|
|
39
|
+
let out = xs[0];
|
|
40
|
+
for (const x of xs.slice(1)) if (better(x, out)) out = x;
|
|
41
|
+
return out;
|
|
42
|
+
};
|
|
43
|
+
switch (e.fn) {
|
|
44
|
+
case "min":
|
|
45
|
+
return fold((a, b) => a < b);
|
|
46
|
+
case "max":
|
|
47
|
+
return fold((a, b) => a > b);
|
|
48
|
+
case "abs":
|
|
49
|
+
return Math.abs(xs[0]);
|
|
50
|
+
case "floor":
|
|
51
|
+
return Math.floor(xs[0]);
|
|
52
|
+
case "ceil":
|
|
53
|
+
return Math.ceil(xs[0]);
|
|
54
|
+
case "round":
|
|
55
|
+
return Math.floor(xs[0] + 0.5);
|
|
56
|
+
default:
|
|
57
|
+
if (xs.some(Number.isNaN)) return NaN;
|
|
58
|
+
return xs[0] < xs[1] ? xs[1] : xs[0] > xs[2] ? xs[2] : xs[0];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
case "bin": {
|
|
62
|
+
if (e.op === "&&") return truthy(evaluate(e.l, state, metric)) && truthy(evaluate(e.r, state, metric));
|
|
63
|
+
if (e.op === "||") return truthy(evaluate(e.l, state, metric)) || truthy(evaluate(e.r, state, metric));
|
|
64
|
+
const a = evaluate(e.l, state, metric), b = evaluate(e.r, state, metric);
|
|
65
|
+
const x = num(a), y = num(b);
|
|
66
|
+
switch (e.op) {
|
|
67
|
+
case "+":
|
|
68
|
+
return x + y;
|
|
69
|
+
case "-":
|
|
70
|
+
return x - y;
|
|
71
|
+
case "*":
|
|
72
|
+
return x * y;
|
|
73
|
+
case "/":
|
|
74
|
+
return x / y;
|
|
75
|
+
case "%":
|
|
76
|
+
return x % y;
|
|
77
|
+
}
|
|
78
|
+
if (Number.isNaN(x) || Number.isNaN(y)) return false;
|
|
79
|
+
switch (e.op) {
|
|
80
|
+
case "<":
|
|
81
|
+
return x < y;
|
|
82
|
+
case "<=":
|
|
83
|
+
return x <= y;
|
|
84
|
+
case ">":
|
|
85
|
+
return x > y;
|
|
86
|
+
case ">=":
|
|
87
|
+
return x >= y;
|
|
88
|
+
case "==":
|
|
89
|
+
return typeof a === "boolean" && typeof b === "boolean" ? a === b : x === y;
|
|
90
|
+
default:
|
|
91
|
+
return typeof a === "boolean" && typeof b === "boolean" ? a !== b : x !== y;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
48
94
|
}
|
|
49
95
|
}
|
|
50
|
-
function
|
|
51
|
-
|
|
96
|
+
function exprNames(e) {
|
|
97
|
+
const out = [];
|
|
98
|
+
const walk = (x) => {
|
|
99
|
+
switch (x.k) {
|
|
100
|
+
case "name":
|
|
101
|
+
if (!out.includes(x.name)) out.push(x.name);
|
|
102
|
+
break;
|
|
103
|
+
case "not":
|
|
104
|
+
case "neg":
|
|
105
|
+
walk(x.e);
|
|
106
|
+
break;
|
|
107
|
+
case "bin":
|
|
108
|
+
walk(x.l);
|
|
109
|
+
walk(x.r);
|
|
110
|
+
break;
|
|
111
|
+
case "call":
|
|
112
|
+
x.args.forEach(walk);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
walk(e);
|
|
117
|
+
return out;
|
|
52
118
|
}
|
|
53
|
-
function
|
|
119
|
+
function exprMetrics(e) {
|
|
54
120
|
const out = [];
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
121
|
+
const walk = (x) => {
|
|
122
|
+
switch (x.k) {
|
|
123
|
+
case "metric":
|
|
124
|
+
out.push({ metric: x.metric, target: x.target });
|
|
125
|
+
break;
|
|
126
|
+
case "not":
|
|
127
|
+
case "neg":
|
|
128
|
+
walk(x.e);
|
|
129
|
+
break;
|
|
130
|
+
case "bin":
|
|
131
|
+
walk(x.l);
|
|
132
|
+
walk(x.r);
|
|
133
|
+
break;
|
|
134
|
+
case "call":
|
|
135
|
+
x.args.forEach(walk);
|
|
136
|
+
break;
|
|
62
137
|
}
|
|
63
|
-
}
|
|
138
|
+
};
|
|
139
|
+
walk(e);
|
|
64
140
|
return out;
|
|
65
141
|
}
|
|
142
|
+
var isDigit = (c) => c !== void 0 && c >= "0" && c <= "9";
|
|
143
|
+
var isLetter = (c) => c !== void 0 && (c === "_" || c >= "a" && c <= "z" || c >= "A" && c <= "Z");
|
|
144
|
+
var Parser = class {
|
|
145
|
+
constructor(text) {
|
|
146
|
+
__publicField(this, "s");
|
|
147
|
+
__publicField(this, "i", 0);
|
|
148
|
+
this.s = [...text];
|
|
149
|
+
}
|
|
150
|
+
atEnd() {
|
|
151
|
+
this.skip();
|
|
152
|
+
return this.i === this.s.length;
|
|
153
|
+
}
|
|
154
|
+
skip() {
|
|
155
|
+
while (this.i < this.s.length && " \n\r".includes(this.s[this.i])) this.i++;
|
|
156
|
+
}
|
|
157
|
+
eat(token) {
|
|
158
|
+
this.skip();
|
|
159
|
+
const t = [...token];
|
|
160
|
+
if (this.i + t.length > this.s.length) return false;
|
|
161
|
+
for (let k = 0; k < t.length; k++) if (this.s[this.i + k] !== t[k]) return false;
|
|
162
|
+
if (t.length === 1 && this.s[this.i + 1] === "=" && "<>!=".includes(t[0])) return false;
|
|
163
|
+
this.i += t.length;
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
or() {
|
|
167
|
+
let l = this.and();
|
|
168
|
+
if (!l) return null;
|
|
169
|
+
while (this.eat("||")) {
|
|
170
|
+
const r = this.and();
|
|
171
|
+
if (!r) return null;
|
|
172
|
+
l = { k: "bin", op: "||", l, r };
|
|
173
|
+
}
|
|
174
|
+
return l;
|
|
175
|
+
}
|
|
176
|
+
and() {
|
|
177
|
+
let l = this.compare();
|
|
178
|
+
if (!l) return null;
|
|
179
|
+
while (this.eat("&&")) {
|
|
180
|
+
const r = this.compare();
|
|
181
|
+
if (!r) return null;
|
|
182
|
+
l = { k: "bin", op: "&&", l, r };
|
|
183
|
+
}
|
|
184
|
+
return l;
|
|
185
|
+
}
|
|
186
|
+
compare() {
|
|
187
|
+
const l = this.sum();
|
|
188
|
+
if (!l) return null;
|
|
189
|
+
for (const op of ["<=", ">=", "==", "!=", "<", ">"]) {
|
|
190
|
+
if (this.eat(op)) {
|
|
191
|
+
const r = this.sum();
|
|
192
|
+
return r ? { k: "bin", op, l, r } : null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return l;
|
|
196
|
+
}
|
|
197
|
+
sum() {
|
|
198
|
+
let l = this.product();
|
|
199
|
+
if (!l) return null;
|
|
200
|
+
for (; ; ) {
|
|
201
|
+
const op = this.eat("+") ? "+" : this.eat("-") ? "-" : null;
|
|
202
|
+
if (!op) return l;
|
|
203
|
+
const r = this.product();
|
|
204
|
+
if (!r) return null;
|
|
205
|
+
l = { k: "bin", op, l, r };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
product() {
|
|
209
|
+
let l = this.unary();
|
|
210
|
+
if (!l) return null;
|
|
211
|
+
for (; ; ) {
|
|
212
|
+
const op = ["*", "/", "%"].find((o) => this.eat(o));
|
|
213
|
+
if (!op) return l;
|
|
214
|
+
const r = this.unary();
|
|
215
|
+
if (!r) return null;
|
|
216
|
+
l = { k: "bin", op, l, r };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
unary() {
|
|
220
|
+
if (this.eat("!")) {
|
|
221
|
+
const e = this.unary();
|
|
222
|
+
return e ? { k: "not", e } : null;
|
|
223
|
+
}
|
|
224
|
+
if (this.eat("-")) {
|
|
225
|
+
const e = this.unary();
|
|
226
|
+
return e ? { k: "neg", e } : null;
|
|
227
|
+
}
|
|
228
|
+
return this.primary();
|
|
229
|
+
}
|
|
230
|
+
primary() {
|
|
231
|
+
this.skip();
|
|
232
|
+
if (this.i >= this.s.length) return null;
|
|
233
|
+
if (this.eat("(")) {
|
|
234
|
+
const e = this.or();
|
|
235
|
+
return e && this.eat(")") ? e : null;
|
|
236
|
+
}
|
|
237
|
+
const s = this.s;
|
|
238
|
+
const c = s[this.i];
|
|
239
|
+
if (isDigit(c)) {
|
|
240
|
+
const start3 = this.i;
|
|
241
|
+
while (isDigit(s[this.i])) this.i++;
|
|
242
|
+
if (s[this.i] === "." && isDigit(s[this.i + 1])) {
|
|
243
|
+
this.i++;
|
|
244
|
+
while (isDigit(s[this.i])) this.i++;
|
|
245
|
+
}
|
|
246
|
+
return { k: "lit", v: Number(s.slice(start3, this.i).join("")) };
|
|
247
|
+
}
|
|
248
|
+
if (!isLetter(c)) return null;
|
|
249
|
+
const start2 = this.i;
|
|
250
|
+
while (isLetter(s[this.i]) || isDigit(s[this.i])) this.i++;
|
|
251
|
+
const name = s.slice(start2, this.i).join("");
|
|
252
|
+
if (name.length > MAX_NAME) return null;
|
|
253
|
+
let j = this.i;
|
|
254
|
+
while (s[j] === " " || s[j] === " ") j++;
|
|
255
|
+
const called = s[j] === "(";
|
|
256
|
+
if (METRIC_NAMES.includes(name)) {
|
|
257
|
+
if (!called) return { k: "metric", metric: name, target: "" };
|
|
258
|
+
this.eat("(");
|
|
259
|
+
const from = this.i;
|
|
260
|
+
while (this.i < s.length && s[this.i] !== ")" && s[this.i] !== "(") this.i++;
|
|
261
|
+
if (s[this.i] !== ")") return null;
|
|
262
|
+
const target = s.slice(from, this.i).join("").replace(/^[ \t]+|[ \t]+$/g, "");
|
|
263
|
+
this.i++;
|
|
264
|
+
return { k: "metric", metric: name, target };
|
|
265
|
+
}
|
|
266
|
+
if (name in FUNCTIONS) {
|
|
267
|
+
if (!called || !this.eat("(")) return null;
|
|
268
|
+
const args = [];
|
|
269
|
+
if (!this.eat(")")) {
|
|
270
|
+
do {
|
|
271
|
+
const a = this.or();
|
|
272
|
+
if (!a) return null;
|
|
273
|
+
args.push(a);
|
|
274
|
+
} while (this.eat(","));
|
|
275
|
+
if (!this.eat(")")) return null;
|
|
276
|
+
}
|
|
277
|
+
const arity = FUNCTIONS[name];
|
|
278
|
+
if (arity !== null ? args.length !== arity : args.length === 0) return null;
|
|
279
|
+
return { k: "call", fn: name, args };
|
|
280
|
+
}
|
|
281
|
+
if (called) return null;
|
|
282
|
+
if (name === "true") return { k: "lit", v: true };
|
|
283
|
+
if (name === "false") return { k: "lit", v: false };
|
|
284
|
+
return { k: "name", name };
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
function describeVar(v) {
|
|
288
|
+
return typeof v === "boolean" ? v ? "true" : "false" : formatTwo(v);
|
|
289
|
+
}
|
|
290
|
+
function formatTwo(v) {
|
|
291
|
+
const n = Math.floor(Math.abs(v) * 100 + 0.5);
|
|
292
|
+
if (!(n < 1e15)) return v < 0 ? "-\u221E" : "\u221E";
|
|
293
|
+
const whole = Math.floor(n / 100), part = n - whole * 100;
|
|
294
|
+
let s = String(whole);
|
|
295
|
+
if (part > 0) s += "." + String(part).padStart(2, "0").replace(/0+$/, "");
|
|
296
|
+
return (v < 0 && n > 0 ? "-" : "") + s;
|
|
297
|
+
}
|
|
298
|
+
function filling(text, values) {
|
|
299
|
+
if (!text.includes("{")) return text;
|
|
300
|
+
let out = "", rest = text;
|
|
301
|
+
for (let open = rest.indexOf("{"); open >= 0; open = rest.indexOf("{")) {
|
|
302
|
+
out += rest.slice(0, open);
|
|
303
|
+
const close = rest.indexOf("}", open);
|
|
304
|
+
if (close < 0) {
|
|
305
|
+
out += rest.slice(open);
|
|
306
|
+
rest = "";
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
const inner = rest.slice(open + 1, close);
|
|
310
|
+
const e = inner.includes("{") ? null : parseExpr(inner);
|
|
311
|
+
const names = e ? exprNames(e) : [];
|
|
312
|
+
if (e && exprMetrics(e).length === 0 && names.length > 0 && names.every((n) => values[n] !== void 0)) out += describeVar(evaluate(e, values));
|
|
313
|
+
else out += rest.slice(open, close + 1);
|
|
314
|
+
rest = rest.slice(close + 1);
|
|
315
|
+
}
|
|
316
|
+
return out + rest;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// src/state.ts
|
|
320
|
+
var MAX_NAME2 = 64;
|
|
321
|
+
function isName(name) {
|
|
322
|
+
return name.length <= MAX_NAME2 && /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
|
|
323
|
+
}
|
|
324
|
+
function mentionsMetric(condition) {
|
|
325
|
+
const e = parseExpr(condition);
|
|
326
|
+
return e !== null && exprMetrics(e).length > 0;
|
|
327
|
+
}
|
|
66
328
|
function holds(condition, state, metric = null) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (m !== null) {
|
|
71
|
-
const v = metric ? metric(m) : null;
|
|
72
|
-
return v !== null && passes(m, v);
|
|
73
|
-
}
|
|
74
|
-
let term = trim(all);
|
|
75
|
-
let negated = false;
|
|
76
|
-
if (term.startsWith("!")) {
|
|
77
|
-
negated = true;
|
|
78
|
-
term = trim(term.slice(1));
|
|
79
|
-
}
|
|
80
|
-
return (state[term] ?? false) !== negated;
|
|
81
|
-
}));
|
|
329
|
+
const e = parseExpr(condition);
|
|
330
|
+
if (!e) return false;
|
|
331
|
+
return truthy(evaluate(e, state, metric ? (m, target) => metric({ metric: m, target, op: "", value: 0 }) : null));
|
|
82
332
|
}
|
|
83
333
|
function stateValues(page, choice) {
|
|
84
334
|
if (choice === "all") return null;
|
|
@@ -117,7 +367,7 @@ function applyOverride(o, e) {
|
|
|
117
367
|
return out;
|
|
118
368
|
}
|
|
119
369
|
function changesState(c) {
|
|
120
|
-
return !!c && (c.reset === true || Object.keys(c.set ?? {}).length > 0 || (c.toggle ?? []).length > 0);
|
|
370
|
+
return !!c && (c.reset === true || Object.keys(c.set ?? {}).length > 0 || (c.toggle ?? []).length > 0 || !!c.run);
|
|
121
371
|
}
|
|
122
372
|
function applyClick(c, state, initial = {}) {
|
|
123
373
|
let out = { ...state };
|
|
@@ -126,12 +376,15 @@ function applyClick(c, state, initial = {}) {
|
|
|
126
376
|
for (const k of Object.keys(state)) out[k] = false;
|
|
127
377
|
Object.assign(out, initial);
|
|
128
378
|
}
|
|
129
|
-
|
|
130
|
-
for (const k of c.
|
|
379
|
+
const before = { ...out };
|
|
380
|
+
for (const [k, v] of Object.entries(c.set ?? {})) out[k] = clickValue(v, before, before[k]);
|
|
381
|
+
for (const k of c.toggle ?? []) out[k] = !truthy(out[k] ?? false);
|
|
131
382
|
return out;
|
|
132
383
|
}
|
|
133
|
-
function
|
|
134
|
-
|
|
384
|
+
function clickValue(v, state, current) {
|
|
385
|
+
if (typeof v !== "string") return v;
|
|
386
|
+
const e = parseExpr(v);
|
|
387
|
+
return e ? evaluate(e, state) : current ?? false;
|
|
135
388
|
}
|
|
136
389
|
|
|
137
390
|
// src/theme.ts
|
|
@@ -193,12 +446,12 @@ function resolvingPalette(page) {
|
|
|
193
446
|
}
|
|
194
447
|
|
|
195
448
|
// src/model.ts
|
|
196
|
-
var SUPPORTED_VERSION =
|
|
449
|
+
var SUPPORTED_VERSION = 3;
|
|
197
450
|
var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
|
|
198
451
|
var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
|
|
199
452
|
var KNOWN_FIELDS = {
|
|
200
453
|
file: ["version", "pages", "images"],
|
|
201
|
-
page: ["id", "name", "layers", "elements", "theme", "customTheme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
454
|
+
page: ["id", "name", "layers", "elements", "theme", "customTheme", "logic", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
202
455
|
flow: ["id", "from", "to", "every", "speed", "color", "size", "when", "spread", "volume", "overrides"],
|
|
203
456
|
flowOverride: ["when", "volume"],
|
|
204
457
|
symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
|
|
@@ -267,7 +520,7 @@ var KNOWN_FIELDS = {
|
|
|
267
520
|
repeat: ["start", "end", "count"],
|
|
268
521
|
cameraKey: ["t", "x", "y", "width", "height", "ease"],
|
|
269
522
|
marker: ["t", "name", "loop"],
|
|
270
|
-
click: ["scene", "url", "set", "toggle", "reset"],
|
|
523
|
+
click: ["scene", "url", "set", "toggle", "reset", "run"],
|
|
271
524
|
hover: ["stroke", "fill", "opacity"],
|
|
272
525
|
override: ["when", "stroke", "fill", "textColor", "opacity", "strokeWidth", "dash", "text", "reroute", "offline", "cost", "capacity", "delay"]
|
|
273
526
|
};
|
|
@@ -281,7 +534,7 @@ var inkOnDark = { r: 0.93, g: 0.93, b: 0.94, a: 1 };
|
|
|
281
534
|
var DottieFormatError = class extends Error {
|
|
282
535
|
};
|
|
283
536
|
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
284
|
-
var
|
|
537
|
+
var num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
285
538
|
var str = (v) => typeof v === "string" ? v : void 0;
|
|
286
539
|
var strings = (v) => isObj(v) && Object.values(v).every((x) => typeof x === "string") ? { ...v } : null;
|
|
287
540
|
var bool = (v) => typeof v === "boolean" ? v : void 0;
|
|
@@ -305,9 +558,9 @@ function webLink(url) {
|
|
|
305
558
|
}
|
|
306
559
|
function rgba(v) {
|
|
307
560
|
if (!isObj(v)) return void 0;
|
|
308
|
-
const r =
|
|
561
|
+
const r = num2(v.r), g = num2(v.g), b = num2(v.b);
|
|
309
562
|
if (r === void 0 || g === void 0 || b === void 0) return void 0;
|
|
310
|
-
return { r, g, b, a:
|
|
563
|
+
return { r, g, b, a: num2(v.a) ?? 1 };
|
|
311
564
|
}
|
|
312
565
|
var Reader = class {
|
|
313
566
|
constructor() {
|
|
@@ -336,15 +589,15 @@ var Reader = class {
|
|
|
336
589
|
stroke: rgba(s.stroke) ?? { ...black },
|
|
337
590
|
fill: rgba(s.fill) ?? { ...clear },
|
|
338
591
|
fillStyle: ["solid", "hachure", "crossHatch", "grid"].includes(fillStyle) ? fillStyle : "solid",
|
|
339
|
-
strokeWidth:
|
|
592
|
+
strokeWidth: num2(s.strokeWidth) ?? 2,
|
|
340
593
|
dash: ["solid", "dashed", "dotted"].includes(dash) ? dash : "solid",
|
|
341
|
-
roughness:
|
|
342
|
-
bowing:
|
|
343
|
-
fontSize:
|
|
594
|
+
roughness: num2(s.roughness) ?? 1,
|
|
595
|
+
bowing: num2(s.bowing) ?? 1,
|
|
596
|
+
fontSize: num2(s.fontSize) ?? 18,
|
|
344
597
|
font: str(s.font) || null,
|
|
345
|
-
gridSize:
|
|
346
|
-
opacity:
|
|
347
|
-
dashOffset:
|
|
598
|
+
gridSize: num2(s.gridSize) ?? null,
|
|
599
|
+
opacity: num2(s.opacity) ?? null,
|
|
600
|
+
dashOffset: num2(s.dashOffset) ?? null,
|
|
348
601
|
textColor: rgba(s.textColor) ?? null,
|
|
349
602
|
named: this.colorNames(s.named)
|
|
350
603
|
};
|
|
@@ -378,13 +631,13 @@ var Reader = class {
|
|
|
378
631
|
endpoint(v) {
|
|
379
632
|
const e = isObj(v) ? v : {};
|
|
380
633
|
const port = str(e.port);
|
|
381
|
-
const portIndex =
|
|
634
|
+
const portIndex = num2(e.portIndex);
|
|
382
635
|
return {
|
|
383
636
|
elementID: uuid(e.elementID) ?? null,
|
|
384
637
|
port: port && ["top", "right", "bottom", "left"].includes(port) ? port : null,
|
|
385
638
|
portIndex: portIndex !== void 0 && Number.isInteger(portIndex) && portIndex >= 0 ? portIndex : null,
|
|
386
|
-
x:
|
|
387
|
-
y:
|
|
639
|
+
x: num2(e.x) ?? 0,
|
|
640
|
+
y: num2(e.y) ?? 0
|
|
388
641
|
};
|
|
389
642
|
}
|
|
390
643
|
connector(v) {
|
|
@@ -403,7 +656,7 @@ var Reader = class {
|
|
|
403
656
|
endHead: head(v.endHead, "arrow"),
|
|
404
657
|
waypoints: Array.isArray(v.waypoints) ? v.waypoints.filter(isObj).map((w) => {
|
|
405
658
|
this.fields(w, "waypoint");
|
|
406
|
-
return { x:
|
|
659
|
+
return { x: num2(w.x) ?? 0, y: num2(w.y) ?? 0 };
|
|
407
660
|
}) : null,
|
|
408
661
|
hops: typeof v.hops === "boolean" ? v.hops : null,
|
|
409
662
|
avoid: typeof v.avoid === "boolean" ? v.avoid : null,
|
|
@@ -439,20 +692,20 @@ var Reader = class {
|
|
|
439
692
|
id,
|
|
440
693
|
kind,
|
|
441
694
|
layerID,
|
|
442
|
-
x:
|
|
443
|
-
y:
|
|
444
|
-
width:
|
|
445
|
-
height:
|
|
695
|
+
x: num2(v.x) ?? 0,
|
|
696
|
+
y: num2(v.y) ?? 0,
|
|
697
|
+
width: num2(v.width) ?? 0,
|
|
698
|
+
height: num2(v.height) ?? 0,
|
|
446
699
|
style: this.style(v.style),
|
|
447
|
-
seed: (
|
|
700
|
+
seed: (num2(v.seed) ?? 1) >>> 0,
|
|
448
701
|
text: str(v.text) ?? null,
|
|
449
702
|
connector: this.connector(v.connector),
|
|
450
703
|
groupID: uuid(v.groupID) ?? null,
|
|
451
|
-
points: Array.isArray(v.points) ? v.points.filter(isObj).map((p) => ({ x:
|
|
704
|
+
points: Array.isArray(v.points) ? v.points.filter(isObj).map((p) => ({ x: num2(p.x) ?? 0, y: num2(p.y) ?? 0 })) : null,
|
|
452
705
|
path: Array.isArray(v.path) ? v.path.filter((c) => typeof c === "number") : null,
|
|
453
706
|
subpaths,
|
|
454
707
|
evenOdd: bool(v.evenOdd) ?? null,
|
|
455
|
-
rotation:
|
|
708
|
+
rotation: num2(v.rotation) ?? null,
|
|
456
709
|
link: uuid(v.link) ?? null,
|
|
457
710
|
url: str(v.url) ?? null,
|
|
458
711
|
anchor: str(v.anchor) ?? null,
|
|
@@ -462,18 +715,18 @@ var Reader = class {
|
|
|
462
715
|
overrides: this.overrides(v.overrides),
|
|
463
716
|
symbol: uuid(v.symbol) ?? null,
|
|
464
717
|
playback: this.playback(v.playback),
|
|
465
|
-
offset:
|
|
718
|
+
offset: num2(v.offset) ?? null,
|
|
466
719
|
args: strings(v.args),
|
|
467
720
|
image: this.image(v.image, images),
|
|
468
|
-
reveal:
|
|
721
|
+
reveal: num2(v.reveal) ?? null,
|
|
469
722
|
ports: Array.isArray(v.ports) ? v.ports.map((p) => {
|
|
470
723
|
const o = isObj(p) ? p : {};
|
|
471
|
-
return { x:
|
|
724
|
+
return { x: num2(o.x) ?? 0, y: num2(o.y) ?? 0 };
|
|
472
725
|
}) : null,
|
|
473
726
|
labelRect: this.labelRect(v.labelRect),
|
|
474
727
|
extension: this.extension(v.extension),
|
|
475
|
-
capacity:
|
|
476
|
-
delay:
|
|
728
|
+
capacity: num2(v.capacity) ?? null,
|
|
729
|
+
delay: num2(v.delay) ?? null,
|
|
477
730
|
queue: v.queue === true ? true : null,
|
|
478
731
|
gauge: this.gauge(v.gauge),
|
|
479
732
|
containerID: uuid(v.containerID) ?? null,
|
|
@@ -498,7 +751,7 @@ var Reader = class {
|
|
|
498
751
|
labelRect(v) {
|
|
499
752
|
if (!isObj(v)) return null;
|
|
500
753
|
this.fields(v, "normalizedRect");
|
|
501
|
-
return { x:
|
|
754
|
+
return { x: num2(v.x) ?? 0, y: num2(v.y) ?? 0, width: num2(v.width) ?? 0, height: num2(v.height) ?? 0 };
|
|
502
755
|
}
|
|
503
756
|
extension(v) {
|
|
504
757
|
if (!isObj(v)) return null;
|
|
@@ -519,7 +772,7 @@ var Reader = class {
|
|
|
519
772
|
variables(v) {
|
|
520
773
|
if (!isObj(v)) return null;
|
|
521
774
|
const out = {};
|
|
522
|
-
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean") out[k] = b;
|
|
775
|
+
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean" || typeof b === "number" && Number.isFinite(b)) out[k] = b;
|
|
523
776
|
return Object.keys(out).length > 0 ? out : null;
|
|
524
777
|
}
|
|
525
778
|
click(v) {
|
|
@@ -531,12 +784,22 @@ var Reader = class {
|
|
|
531
784
|
let set = null;
|
|
532
785
|
if (isObj(v.set)) {
|
|
533
786
|
set = {};
|
|
534
|
-
for (const [k, b] of Object.entries(v.set))
|
|
787
|
+
for (const [k, b] of Object.entries(v.set)) {
|
|
788
|
+
if (isName(k) && (typeof b === "boolean" || typeof b === "string" || typeof b === "number" && Number.isFinite(b))) set[k] = b;
|
|
789
|
+
}
|
|
535
790
|
if (Object.keys(set).length === 0) set = null;
|
|
536
791
|
}
|
|
537
792
|
const raw = typeof v.toggle === "string" ? [v.toggle] : Array.isArray(v.toggle) && v.toggle.every((x) => typeof x === "string") ? v.toggle : null;
|
|
538
793
|
const toggle = raw ? raw.filter(isName) : null;
|
|
539
|
-
|
|
794
|
+
const run = str(v.run);
|
|
795
|
+
return {
|
|
796
|
+
scene: str(v.scene) ?? null,
|
|
797
|
+
url: safe,
|
|
798
|
+
set,
|
|
799
|
+
toggle: toggle && toggle.length > 0 ? toggle : null,
|
|
800
|
+
reset: v.reset === true ? true : null,
|
|
801
|
+
run: run !== void 0 && isName(run) ? run : null
|
|
802
|
+
};
|
|
540
803
|
}
|
|
541
804
|
playback(v) {
|
|
542
805
|
const s = str(v);
|
|
@@ -552,7 +815,7 @@ var Reader = class {
|
|
|
552
815
|
return null;
|
|
553
816
|
}
|
|
554
817
|
const size = (x) => {
|
|
555
|
-
const n =
|
|
818
|
+
const n = num2(x);
|
|
556
819
|
return Math.max(1, n ?? 100);
|
|
557
820
|
};
|
|
558
821
|
const layers = (Array.isArray(v.layers) ? v.layers : []).filter(isObj).flatMap((l) => {
|
|
@@ -589,8 +852,8 @@ var Reader = class {
|
|
|
589
852
|
stroke: rgba(o.stroke) ?? null,
|
|
590
853
|
fill: rgba(o.fill) ?? null,
|
|
591
854
|
textColor: rgba(o.textColor) ?? null,
|
|
592
|
-
opacity:
|
|
593
|
-
strokeWidth:
|
|
855
|
+
opacity: num2(o.opacity) ?? null,
|
|
856
|
+
strokeWidth: num2(o.strokeWidth) ?? null,
|
|
594
857
|
dash: dash !== void 0 && ["solid", "dashed", "dotted"].includes(dash) ? dash : null,
|
|
595
858
|
text: str(o.text) ?? null,
|
|
596
859
|
reroute,
|
|
@@ -604,7 +867,7 @@ var Reader = class {
|
|
|
604
867
|
hover(v) {
|
|
605
868
|
if (!isObj(v)) return null;
|
|
606
869
|
this.fields(v, "hover");
|
|
607
|
-
return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity:
|
|
870
|
+
return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity: num2(v.opacity) ?? null };
|
|
608
871
|
}
|
|
609
872
|
ease(v) {
|
|
610
873
|
const s = str(v);
|
|
@@ -618,11 +881,11 @@ var Reader = class {
|
|
|
618
881
|
keyframe(v) {
|
|
619
882
|
if (!isObj(v)) return null;
|
|
620
883
|
this.fields(v, "keyframe");
|
|
621
|
-
const t =
|
|
884
|
+
const t = num2(v.t);
|
|
622
885
|
if (t === void 0) return null;
|
|
623
886
|
const k = { t };
|
|
624
887
|
for (const p of ["x", "y", "width", "height", "rotation", "strokeWidth", "reveal", "opacity"]) {
|
|
625
|
-
const n =
|
|
888
|
+
const n = num2(v[p]);
|
|
626
889
|
if (n !== void 0) k[p] = n;
|
|
627
890
|
}
|
|
628
891
|
const stroke = rgba(v.stroke), fill = rgba(v.fill), visible = bool(v.visible), ease = this.ease(v.ease);
|
|
@@ -640,39 +903,39 @@ var Reader = class {
|
|
|
640
903
|
const keys = (Array.isArray(v.keys) ? v.keys : []).map((k) => this.keyframe(k)).filter((k) => k !== null && k.t >= 0 && k.t <= MAX_DURATION);
|
|
641
904
|
stableSortBy(keys, (k) => k.t);
|
|
642
905
|
const follow = Array.isArray(v.follow) ? v.follow.filter(isObj).flatMap((f) => {
|
|
643
|
-
const path = uuid(f.path), start2 =
|
|
906
|
+
const path = uuid(f.path), start2 = num2(f.start), end = num2(f.end);
|
|
644
907
|
if (!path || start2 === void 0 || end === void 0 || start2 < 0 || end > MAX_DURATION) return [];
|
|
645
|
-
return [{ path, start: start2, end, ease: this.ease(f.ease), reverse: bool(f.reverse) ?? null, orient: bool(f.orient) ?? null, laps:
|
|
908
|
+
return [{ path, start: start2, end, ease: this.ease(f.ease), reverse: bool(f.reverse) ?? null, orient: bool(f.orient) ?? null, laps: num2(f.laps) ?? null }];
|
|
646
909
|
}) : null;
|
|
647
910
|
const repeats = Array.isArray(v.repeats) ? v.repeats.filter(isObj).flatMap((r) => {
|
|
648
|
-
const start2 =
|
|
911
|
+
const start2 = num2(r.start), end = num2(r.end);
|
|
649
912
|
if (start2 === void 0 || end === void 0) return [];
|
|
650
|
-
const count =
|
|
913
|
+
const count = num2(r.count);
|
|
651
914
|
const rep = { start: start2, end, count: count === void 0 ? null : Math.trunc(count) };
|
|
652
915
|
return repeatIsValid(rep) ? [rep] : [];
|
|
653
916
|
}) : null;
|
|
654
|
-
return { element, keys, boil: bool(v.boil) ?? null, follow, repeats, march: clampMarch(
|
|
917
|
+
return { element, keys, boil: bool(v.boil) ?? null, follow, repeats, march: clampMarch(num2(v.march)) };
|
|
655
918
|
}
|
|
656
919
|
timeline(v) {
|
|
657
920
|
if (!isObj(v)) return null;
|
|
658
921
|
this.fields(v, "timeline");
|
|
659
|
-
const boil =
|
|
922
|
+
const boil = num2(v.boil);
|
|
660
923
|
const camera = Array.isArray(v.camera) ? v.camera.filter(isObj).flatMap((c) => {
|
|
661
|
-
const t =
|
|
924
|
+
const t = num2(c.t), x = num2(c.x), y = num2(c.y), width = num2(c.width), height = num2(c.height);
|
|
662
925
|
if (t === void 0 || x === void 0 || y === void 0 || width === void 0 || height === void 0) return [];
|
|
663
926
|
if (!(width > 1 && height > 1 && t >= 0)) return [];
|
|
664
927
|
return [{ t, x, y, width, height, ease: this.ease(c.ease) }];
|
|
665
928
|
}) : null;
|
|
666
929
|
if (camera) stableSortBy(camera, (c) => c.t);
|
|
667
930
|
const markers = Array.isArray(v.markers) ? v.markers.filter(isObj).flatMap((m) => {
|
|
668
|
-
const t =
|
|
931
|
+
const t = num2(m.t);
|
|
669
932
|
return t !== void 0 && t >= 0 ? [{ t, name: str(m.name) ?? "", loop: bool(m.loop) ?? null }] : [];
|
|
670
933
|
}) : null;
|
|
671
934
|
if (markers) stableSortBy(markers, (m) => m.t);
|
|
672
|
-
const poster =
|
|
935
|
+
const poster = num2(v.poster);
|
|
673
936
|
return {
|
|
674
|
-
fps: clampFPS(
|
|
675
|
-
duration: clampTime(
|
|
937
|
+
fps: clampFPS(num2(v.fps) ?? 12),
|
|
938
|
+
duration: clampTime(num2(v.duration) ?? 0),
|
|
676
939
|
boil: boil !== void 0 && Number.isInteger(boil) && boil > 0 ? Math.min(boil, 1e4) : null,
|
|
677
940
|
settle: bool(v.settle) ?? null,
|
|
678
941
|
poster: poster === void 0 ? null : clampTime(poster),
|
|
@@ -699,6 +962,7 @@ var Reader = class {
|
|
|
699
962
|
elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
|
|
700
963
|
theme: str(v.theme) ?? null,
|
|
701
964
|
customTheme: this.customTheme(v.customTheme),
|
|
965
|
+
logic: typeof v.logic === "string" && v.logic.length > 0 ? v.logic : null,
|
|
702
966
|
timeline: this.timeline(v.timeline),
|
|
703
967
|
symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null,
|
|
704
968
|
variables: this.variables(v.variables),
|
|
@@ -710,7 +974,7 @@ var Reader = class {
|
|
|
710
974
|
this.fields(f, "flow");
|
|
711
975
|
const id = (x) => typeof x === "string" && UUID.test(x) ? x.toUpperCase() : NO_ID;
|
|
712
976
|
const finite = (x) => {
|
|
713
|
-
const n =
|
|
977
|
+
const n = num2(x);
|
|
714
978
|
return n !== void 0 && Number.isFinite(n) ? n : null;
|
|
715
979
|
};
|
|
716
980
|
return {
|
|
@@ -750,7 +1014,7 @@ function parseDottie(source) {
|
|
|
750
1014
|
}
|
|
751
1015
|
}
|
|
752
1016
|
if (!isObj(json)) throw new DottieFormatError("Not a Dottie drawing");
|
|
753
|
-
const version =
|
|
1017
|
+
const version = num2(json.version);
|
|
754
1018
|
if (version === void 0) throw new DottieFormatError("Not a Dottie drawing (no version)");
|
|
755
1019
|
if (!Array.isArray(json.pages) || json.pages.length === 0) throw new DottieFormatError("A drawing needs at least one page");
|
|
756
1020
|
const reader = new Reader();
|
|
@@ -2998,7 +3262,7 @@ function hopCrossings(e, r, page, index) {
|
|
|
2998
3262
|
for (const samples of segments) {
|
|
2999
3263
|
const along = runningLengths(samples);
|
|
3000
3264
|
const length = along[along.length - 1];
|
|
3001
|
-
const
|
|
3265
|
+
const found2 = [];
|
|
3002
3266
|
const near = [];
|
|
3003
3267
|
const whole = boxOf(samples);
|
|
3004
3268
|
for (let n = 0; n < count; n++) if (touching(whole, lines[n].box)) near.push(lines[n]);
|
|
@@ -3015,12 +3279,12 @@ function hopCrossings(e, r, page, index) {
|
|
|
3015
3279
|
if (Math.abs(det) < 1e-9) continue;
|
|
3016
3280
|
const wx = a.x - p.x, wy = a.y - p.y;
|
|
3017
3281
|
const t = (wx * ey - wy * ex) / det, s = (wx * dy - wy * dx) / det;
|
|
3018
|
-
if (t > 0 && t < 1 && s >= 0 && s <= 1)
|
|
3282
|
+
if (t > 0 && t < 1 && s >= 0 && s <= 1) found2.push(along[i] + t * (along[i + 1] - along[i]));
|
|
3019
3283
|
}
|
|
3020
3284
|
}
|
|
3021
3285
|
}
|
|
3022
3286
|
const kept = [];
|
|
3023
|
-
for (const at of
|
|
3287
|
+
for (const at of found2.sort((x, y) => x - y)) {
|
|
3024
3288
|
if (at < rad + 2 || at > length - rad - 2) continue;
|
|
3025
3289
|
if (kept.length > 0 && at - kept[kept.length - 1] < 2 * rad + 2) continue;
|
|
3026
3290
|
kept.push(at);
|
|
@@ -3234,7 +3498,7 @@ var lerpColor = (a, b, u) => ({
|
|
|
3234
3498
|
b: mix01(a.b, b.b, u),
|
|
3235
3499
|
a: mix01(a.a, b.a, u)
|
|
3236
3500
|
});
|
|
3237
|
-
function
|
|
3501
|
+
function num3(track, t, p, base2) {
|
|
3238
3502
|
return value(track, t, (k) => k[p], base2, lerpNum);
|
|
3239
3503
|
}
|
|
3240
3504
|
function isChanging(track, t) {
|
|
@@ -3260,20 +3524,20 @@ function isVisible(track, t) {
|
|
|
3260
3524
|
function applyTrack(track, e, t) {
|
|
3261
3525
|
e = cloneElement(e);
|
|
3262
3526
|
if (track.keys.some((k) => k.reveal !== void 0)) {
|
|
3263
|
-
const r =
|
|
3527
|
+
const r = num3(track, t, "reveal", e.reveal ?? 1);
|
|
3264
3528
|
e.reveal = r >= 1 ? null : Math.max(0, r);
|
|
3265
3529
|
}
|
|
3266
3530
|
if (track.keys.some((k) => k.opacity !== void 0)) {
|
|
3267
|
-
const o = Math.min(Math.max(
|
|
3531
|
+
const o = Math.min(Math.max(num3(track, t, "opacity", e.style.opacity ?? 1), 0), 1);
|
|
3268
3532
|
e.style.opacity = o >= 1 ? null : o;
|
|
3269
3533
|
}
|
|
3270
3534
|
if (e.kind === "connector") return e;
|
|
3271
|
-
e.x =
|
|
3272
|
-
e.y =
|
|
3273
|
-
e.width = Math.max(0,
|
|
3274
|
-
e.height = Math.max(0,
|
|
3275
|
-
if (track.keys.some((k) => k.rotation !== void 0)) e.rotation =
|
|
3276
|
-
e.style.strokeWidth = Math.max(0,
|
|
3535
|
+
e.x = num3(track, t, "x", e.x);
|
|
3536
|
+
e.y = num3(track, t, "y", e.y);
|
|
3537
|
+
e.width = Math.max(0, num3(track, t, "width", e.width));
|
|
3538
|
+
e.height = Math.max(0, num3(track, t, "height", e.height));
|
|
3539
|
+
if (track.keys.some((k) => k.rotation !== void 0)) e.rotation = num3(track, t, "rotation", angle(e));
|
|
3540
|
+
e.style.strokeWidth = Math.max(0, num3(track, t, "strokeWidth", e.style.strokeWidth));
|
|
3277
3541
|
e.style.stroke = value(track, t, (k) => k.stroke, e.style.stroke, lerpColor);
|
|
3278
3542
|
e.style.fill = value(track, t, (k) => k.fill, e.style.fill, lerpColor);
|
|
3279
3543
|
return e;
|
|
@@ -3349,7 +3613,8 @@ function posedWithMetrics(page, t, state = "initial", since = null, carried = {}
|
|
|
3349
3613
|
}
|
|
3350
3614
|
const flowing = (page.flows ?? []).length > 0;
|
|
3351
3615
|
const measuring = flowing || page.elements.some((e) => e.gauge || (e.overrides ?? []).some((o) => mentionsMetric(o.when)));
|
|
3352
|
-
const
|
|
3616
|
+
const fills = values !== null && Object.keys(values).length > 0 && page.elements.some((e) => e.text?.includes("{") || (e.overrides ?? []).some((o) => o.text?.includes("{")));
|
|
3617
|
+
const timeline = page.timeline ?? (off.size === 0 && active.size === 0 && !measuring && !fills ? null : EMPTY_TIMELINE);
|
|
3353
3618
|
if (!timeline) return { frame: page, metrics: emptyMetrics() };
|
|
3354
3619
|
t = clampTime(t);
|
|
3355
3620
|
const tracks = trackMap(timeline);
|
|
@@ -3385,6 +3650,10 @@ function posedWithMetrics(page, t, state = "initial", since = null, carried = {}
|
|
|
3385
3650
|
const track = tracks.get(e.id);
|
|
3386
3651
|
let out = track ? applyTrack(track, e, t) : e;
|
|
3387
3652
|
for (const o of active.get(e.id) ?? []) out = applyOverride(o, out);
|
|
3653
|
+
if (fills && values && out.text) {
|
|
3654
|
+
const text = filling(out.text, values);
|
|
3655
|
+
if (text !== out.text) out = { ...out, text };
|
|
3656
|
+
}
|
|
3388
3657
|
const list = active.get(e.id);
|
|
3389
3658
|
if (list) {
|
|
3390
3659
|
const caps = list.map((o) => o.capacity).filter((c) => c !== null && Number.isFinite(c));
|
|
@@ -3702,7 +3971,12 @@ function clickTargets(page, file) {
|
|
|
3702
3971
|
let pageOf = null;
|
|
3703
3972
|
for (const e of page.elements) {
|
|
3704
3973
|
const c = e.click;
|
|
3705
|
-
const state = c && changesState(c) ? {
|
|
3974
|
+
const state = c && changesState(c) ? {
|
|
3975
|
+
...c.set ? { set: c.set } : {},
|
|
3976
|
+
...c.toggle ? { toggle: c.toggle } : {},
|
|
3977
|
+
...c.reset ? { reset: true } : {},
|
|
3978
|
+
...c.run ? { run: c.run } : {}
|
|
3979
|
+
} : null;
|
|
3706
3980
|
if (c?.scene) {
|
|
3707
3981
|
const i = sceneNamed(c.scene, markers);
|
|
3708
3982
|
if (i >= 0) {
|
|
@@ -4725,6 +4999,40 @@ function start() {
|
|
|
4725
4999
|
return w;
|
|
4726
5000
|
}
|
|
4727
5001
|
|
|
5002
|
+
// src/logic-loader.ts
|
|
5003
|
+
var scriptSrc = typeof document !== "undefined" ? document.currentScript?.src ?? null : null;
|
|
5004
|
+
var found = null;
|
|
5005
|
+
var present = () => globalThis.DottieLogic;
|
|
5006
|
+
function logicEngine() {
|
|
5007
|
+
found ?? (found = (async () => {
|
|
5008
|
+
if (!present()) {
|
|
5009
|
+
const here = import.meta.url;
|
|
5010
|
+
const base2 = here ?? scriptSrc;
|
|
5011
|
+
if (!base2) throw new Error("No dottie-logic.min.js on the page, and no address to find it from");
|
|
5012
|
+
const url = new URL("./dottie-logic.min.js", base2).href;
|
|
5013
|
+
if (here) await import(
|
|
5014
|
+
/* @vite-ignore */
|
|
5015
|
+
url
|
|
5016
|
+
);
|
|
5017
|
+
else await new Promise((resolve2, reject) => {
|
|
5018
|
+
const s = document.createElement("script");
|
|
5019
|
+
s.src = url;
|
|
5020
|
+
s.onload = () => resolve2();
|
|
5021
|
+
s.onerror = () => reject(new Error(`Couldn't load ${url}`));
|
|
5022
|
+
document.head.append(s);
|
|
5023
|
+
});
|
|
5024
|
+
}
|
|
5025
|
+
const engine = present();
|
|
5026
|
+
if (!engine) throw new Error("dottie-logic.min.js didn't load");
|
|
5027
|
+
await engine.loadLogic();
|
|
5028
|
+
return engine;
|
|
5029
|
+
})());
|
|
5030
|
+
found.catch(() => {
|
|
5031
|
+
found = null;
|
|
5032
|
+
});
|
|
5033
|
+
return found;
|
|
5034
|
+
}
|
|
5035
|
+
|
|
4728
5036
|
// src/player.ts
|
|
4729
5037
|
var WHITE = { r: 1, g: 1, b: 1, a: 1 };
|
|
4730
5038
|
function pageIndex(file, spec) {
|
|
@@ -5066,6 +5374,9 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5066
5374
|
__publicField(this, "stillTime", null);
|
|
5067
5375
|
__publicField(this, "visible", true);
|
|
5068
5376
|
__publicField(this, "observer", null);
|
|
5377
|
+
/** Runs handler `handler` from the page's logic for a click on `id` (DottieDocument.clicked). Clicks take turns, so
|
|
5378
|
+
* each handler sees the state the one before left. */
|
|
5379
|
+
__publicField(this, "logicQueue", Promise.resolve());
|
|
5069
5380
|
this.root = this.attachShadow({ mode: "open" });
|
|
5070
5381
|
this.root.innerHTML = `<style>${STYLE}</style><div class="stage" part="stage" role="img"></div>
|
|
5071
5382
|
<div class="bar" part="controls" hidden>
|
|
@@ -5292,7 +5603,9 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5292
5603
|
this.scheduleReplay();
|
|
5293
5604
|
}
|
|
5294
5605
|
if (!click) return;
|
|
5295
|
-
if (click.
|
|
5606
|
+
if (click.run && p.page.logic && this.hasAttribute("allow-logic")) {
|
|
5607
|
+
this.runLogic(click.run, id, click.reset === true);
|
|
5608
|
+
} else if (click.set || click.toggle || click.reset) {
|
|
5296
5609
|
this.setState(applyClick(
|
|
5297
5610
|
{ scene: null, url: null, set: click.set ?? null, toggle: click.toggle ?? null, reset: click.reset ?? null },
|
|
5298
5611
|
p.state,
|
|
@@ -5314,6 +5627,35 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5314
5627
|
}
|
|
5315
5628
|
}
|
|
5316
5629
|
}
|
|
5630
|
+
runLogic(handler, id, reset) {
|
|
5631
|
+
const p = this.playback;
|
|
5632
|
+
if (!p) return;
|
|
5633
|
+
const e = p.page.elements.find((x) => x.id === id);
|
|
5634
|
+
const s = p.sceneAt(p.frame);
|
|
5635
|
+
const event = {
|
|
5636
|
+
id,
|
|
5637
|
+
anchor: e?.anchor ?? null,
|
|
5638
|
+
text: e?.text?.split("\n")[0] ?? null,
|
|
5639
|
+
time: this.currentTime,
|
|
5640
|
+
scene: s < 0 ? null : p.sceneNames[s]
|
|
5641
|
+
};
|
|
5642
|
+
this.logicQueue = this.logicQueue.then(async () => {
|
|
5643
|
+
const engine = await logicEngine();
|
|
5644
|
+
const now = this.playback;
|
|
5645
|
+
if (!now || now !== p || !now.page.logic) return;
|
|
5646
|
+
let start2 = now.state;
|
|
5647
|
+
if (reset) {
|
|
5648
|
+
start2 = {};
|
|
5649
|
+
for (const k of Object.keys(now.state)) start2[k] = false;
|
|
5650
|
+
Object.assign(start2, now.page.variables ?? {});
|
|
5651
|
+
}
|
|
5652
|
+
const r = engine.runLogic(now.page.logic, handler, start2, event);
|
|
5653
|
+
if (r.ok) this.setState({ ...start2, ...r.set });
|
|
5654
|
+
else this.dispatchEvent(new CustomEvent("logicerror", { detail: { element: id, handler, error: r.error } }));
|
|
5655
|
+
}).catch((err) => {
|
|
5656
|
+
this.dispatchEvent(new CustomEvent("logicerror", { detail: { element: id, handler, error: err.message } }));
|
|
5657
|
+
});
|
|
5658
|
+
}
|
|
5317
5659
|
/** The page's variables now. */
|
|
5318
5660
|
get variables() {
|
|
5319
5661
|
return { ...this.playback?.state ?? {} };
|
|
@@ -5420,7 +5762,7 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5420
5762
|
if (handled) e.preventDefault();
|
|
5421
5763
|
}
|
|
5422
5764
|
};
|
|
5423
|
-
__publicField(DottiePlayer, "observedAttributes", ["src", "page", "at", "clean", "no-controls", "label"]);
|
|
5765
|
+
__publicField(DottiePlayer, "observedAttributes", ["src", "page", "at", "clean", "no-controls", "label", "allow-logic"]);
|
|
5424
5766
|
function clickable(target) {
|
|
5425
5767
|
const el = target;
|
|
5426
5768
|
return el && typeof el.closest === "function" ? el.closest(".dottie-a") : null;
|
|
@@ -5525,7 +5867,7 @@ function render(source, options = {}) {
|
|
|
5525
5867
|
function isFile(v) {
|
|
5526
5868
|
return typeof v === "object" && v !== null && Array.isArray(v.pages) && Array.isArray(v.warnings);
|
|
5527
5869
|
}
|
|
5528
|
-
var VERSION = "0.
|
|
5870
|
+
var VERSION = "0.11.0";
|
|
5529
5871
|
export {
|
|
5530
5872
|
BOIL_DRAWINGS,
|
|
5531
5873
|
BUNDLED_FONTS,
|