@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-player.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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 __export = (target, all) => {
|
|
@@ -68,82 +68,332 @@ __export(index_exports, {
|
|
|
68
68
|
withRoughness: () => withRoughness
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
// src/
|
|
71
|
+
// src/expression.ts
|
|
72
|
+
var METRIC_NAMES = ["throughput", "load", "delay", "backlog", "depth", "inFlight", "transit", "backup"];
|
|
73
|
+
var FUNCTIONS = { min: null, max: null, abs: 1, floor: 1, ceil: 1, round: 1, clamp: 3 };
|
|
72
74
|
var MAX_NAME = 64;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
var truthy = (v) => typeof v === "boolean" ? v : v !== 0 && !Number.isNaN(v);
|
|
76
|
+
var num = (v) => typeof v === "boolean" ? v ? 1 : 0 : v;
|
|
77
|
+
var parsed = /* @__PURE__ */ new Map();
|
|
78
|
+
function parseExpr(text) {
|
|
79
|
+
const hit = parsed.get(text);
|
|
80
|
+
if (hit !== void 0) return hit;
|
|
81
|
+
const p = new Parser(text);
|
|
82
|
+
let e = p.or();
|
|
83
|
+
if (e && !p.atEnd()) e = null;
|
|
84
|
+
if (parsed.size > 4096) parsed.clear();
|
|
85
|
+
parsed.set(text, e);
|
|
86
|
+
return e;
|
|
75
87
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
88
|
+
function evaluate(e, state, metric = null) {
|
|
89
|
+
switch (e.k) {
|
|
90
|
+
case "lit":
|
|
91
|
+
return e.v;
|
|
92
|
+
case "name":
|
|
93
|
+
return state[e.name] ?? false;
|
|
94
|
+
case "metric":
|
|
95
|
+
return metric?.(e.metric, e.target) ?? NaN;
|
|
96
|
+
case "not":
|
|
97
|
+
return !truthy(evaluate(e.e, state, metric));
|
|
98
|
+
case "neg":
|
|
99
|
+
return -num(evaluate(e.e, state, metric));
|
|
100
|
+
case "call": {
|
|
101
|
+
const xs = e.args.map((a) => num(evaluate(a, state, metric)));
|
|
102
|
+
const fold = (better) => {
|
|
103
|
+
if (xs.some(Number.isNaN)) return NaN;
|
|
104
|
+
let out = xs[0];
|
|
105
|
+
for (const x of xs.slice(1)) if (better(x, out)) out = x;
|
|
106
|
+
return out;
|
|
107
|
+
};
|
|
108
|
+
switch (e.fn) {
|
|
109
|
+
case "min":
|
|
110
|
+
return fold((a, b) => a < b);
|
|
111
|
+
case "max":
|
|
112
|
+
return fold((a, b) => a > b);
|
|
113
|
+
case "abs":
|
|
114
|
+
return Math.abs(xs[0]);
|
|
115
|
+
case "floor":
|
|
116
|
+
return Math.floor(xs[0]);
|
|
117
|
+
case "ceil":
|
|
118
|
+
return Math.ceil(xs[0]);
|
|
119
|
+
case "round":
|
|
120
|
+
return Math.floor(xs[0] + 0.5);
|
|
121
|
+
default:
|
|
122
|
+
if (xs.some(Number.isNaN)) return NaN;
|
|
123
|
+
return xs[0] < xs[1] ? xs[1] : xs[0] > xs[2] ? xs[2] : xs[0];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
case "bin": {
|
|
127
|
+
if (e.op === "&&") return truthy(evaluate(e.l, state, metric)) && truthy(evaluate(e.r, state, metric));
|
|
128
|
+
if (e.op === "||") return truthy(evaluate(e.l, state, metric)) || truthy(evaluate(e.r, state, metric));
|
|
129
|
+
const a = evaluate(e.l, state, metric), b = evaluate(e.r, state, metric);
|
|
130
|
+
const x = num(a), y = num(b);
|
|
131
|
+
switch (e.op) {
|
|
132
|
+
case "+":
|
|
133
|
+
return x + y;
|
|
134
|
+
case "-":
|
|
135
|
+
return x - y;
|
|
136
|
+
case "*":
|
|
137
|
+
return x * y;
|
|
138
|
+
case "/":
|
|
139
|
+
return x / y;
|
|
140
|
+
case "%":
|
|
141
|
+
return x % y;
|
|
142
|
+
}
|
|
143
|
+
if (Number.isNaN(x) || Number.isNaN(y)) return false;
|
|
144
|
+
switch (e.op) {
|
|
145
|
+
case "<":
|
|
146
|
+
return x < y;
|
|
147
|
+
case "<=":
|
|
148
|
+
return x <= y;
|
|
149
|
+
case ">":
|
|
150
|
+
return x > y;
|
|
151
|
+
case ">=":
|
|
152
|
+
return x >= y;
|
|
153
|
+
case "==":
|
|
154
|
+
return typeof a === "boolean" && typeof b === "boolean" ? a === b : x === y;
|
|
155
|
+
default:
|
|
156
|
+
return typeof a === "boolean" && typeof b === "boolean" ? a !== b : x !== y;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
113
159
|
}
|
|
114
160
|
}
|
|
115
|
-
function
|
|
116
|
-
|
|
161
|
+
function exprNames(e) {
|
|
162
|
+
const out = [];
|
|
163
|
+
const walk = (x) => {
|
|
164
|
+
switch (x.k) {
|
|
165
|
+
case "name":
|
|
166
|
+
if (!out.includes(x.name)) out.push(x.name);
|
|
167
|
+
break;
|
|
168
|
+
case "not":
|
|
169
|
+
case "neg":
|
|
170
|
+
walk(x.e);
|
|
171
|
+
break;
|
|
172
|
+
case "bin":
|
|
173
|
+
walk(x.l);
|
|
174
|
+
walk(x.r);
|
|
175
|
+
break;
|
|
176
|
+
case "call":
|
|
177
|
+
x.args.forEach(walk);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
walk(e);
|
|
182
|
+
return out;
|
|
117
183
|
}
|
|
118
|
-
function
|
|
184
|
+
function exprMetrics(e) {
|
|
119
185
|
const out = [];
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
186
|
+
const walk = (x) => {
|
|
187
|
+
switch (x.k) {
|
|
188
|
+
case "metric":
|
|
189
|
+
out.push({ metric: x.metric, target: x.target });
|
|
190
|
+
break;
|
|
191
|
+
case "not":
|
|
192
|
+
case "neg":
|
|
193
|
+
walk(x.e);
|
|
194
|
+
break;
|
|
195
|
+
case "bin":
|
|
196
|
+
walk(x.l);
|
|
197
|
+
walk(x.r);
|
|
198
|
+
break;
|
|
199
|
+
case "call":
|
|
200
|
+
x.args.forEach(walk);
|
|
201
|
+
break;
|
|
127
202
|
}
|
|
128
|
-
}
|
|
203
|
+
};
|
|
204
|
+
walk(e);
|
|
129
205
|
return out;
|
|
130
206
|
}
|
|
207
|
+
var isDigit = (c) => c !== void 0 && c >= "0" && c <= "9";
|
|
208
|
+
var isLetter = (c) => c !== void 0 && (c === "_" || c >= "a" && c <= "z" || c >= "A" && c <= "Z");
|
|
209
|
+
var Parser = class {
|
|
210
|
+
constructor(text) {
|
|
211
|
+
__publicField(this, "s");
|
|
212
|
+
__publicField(this, "i", 0);
|
|
213
|
+
this.s = [...text];
|
|
214
|
+
}
|
|
215
|
+
atEnd() {
|
|
216
|
+
this.skip();
|
|
217
|
+
return this.i === this.s.length;
|
|
218
|
+
}
|
|
219
|
+
skip() {
|
|
220
|
+
while (this.i < this.s.length && " \n\r".includes(this.s[this.i])) this.i++;
|
|
221
|
+
}
|
|
222
|
+
eat(token) {
|
|
223
|
+
this.skip();
|
|
224
|
+
const t = [...token];
|
|
225
|
+
if (this.i + t.length > this.s.length) return false;
|
|
226
|
+
for (let k = 0; k < t.length; k++) if (this.s[this.i + k] !== t[k]) return false;
|
|
227
|
+
if (t.length === 1 && this.s[this.i + 1] === "=" && "<>!=".includes(t[0])) return false;
|
|
228
|
+
this.i += t.length;
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
or() {
|
|
232
|
+
let l = this.and();
|
|
233
|
+
if (!l) return null;
|
|
234
|
+
while (this.eat("||")) {
|
|
235
|
+
const r = this.and();
|
|
236
|
+
if (!r) return null;
|
|
237
|
+
l = { k: "bin", op: "||", l, r };
|
|
238
|
+
}
|
|
239
|
+
return l;
|
|
240
|
+
}
|
|
241
|
+
and() {
|
|
242
|
+
let l = this.compare();
|
|
243
|
+
if (!l) return null;
|
|
244
|
+
while (this.eat("&&")) {
|
|
245
|
+
const r = this.compare();
|
|
246
|
+
if (!r) return null;
|
|
247
|
+
l = { k: "bin", op: "&&", l, r };
|
|
248
|
+
}
|
|
249
|
+
return l;
|
|
250
|
+
}
|
|
251
|
+
compare() {
|
|
252
|
+
const l = this.sum();
|
|
253
|
+
if (!l) return null;
|
|
254
|
+
for (const op of ["<=", ">=", "==", "!=", "<", ">"]) {
|
|
255
|
+
if (this.eat(op)) {
|
|
256
|
+
const r = this.sum();
|
|
257
|
+
return r ? { k: "bin", op, l, r } : null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return l;
|
|
261
|
+
}
|
|
262
|
+
sum() {
|
|
263
|
+
let l = this.product();
|
|
264
|
+
if (!l) return null;
|
|
265
|
+
for (; ; ) {
|
|
266
|
+
const op = this.eat("+") ? "+" : this.eat("-") ? "-" : null;
|
|
267
|
+
if (!op) return l;
|
|
268
|
+
const r = this.product();
|
|
269
|
+
if (!r) return null;
|
|
270
|
+
l = { k: "bin", op, l, r };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
product() {
|
|
274
|
+
let l = this.unary();
|
|
275
|
+
if (!l) return null;
|
|
276
|
+
for (; ; ) {
|
|
277
|
+
const op = ["*", "/", "%"].find((o) => this.eat(o));
|
|
278
|
+
if (!op) return l;
|
|
279
|
+
const r = this.unary();
|
|
280
|
+
if (!r) return null;
|
|
281
|
+
l = { k: "bin", op, l, r };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
unary() {
|
|
285
|
+
if (this.eat("!")) {
|
|
286
|
+
const e = this.unary();
|
|
287
|
+
return e ? { k: "not", e } : null;
|
|
288
|
+
}
|
|
289
|
+
if (this.eat("-")) {
|
|
290
|
+
const e = this.unary();
|
|
291
|
+
return e ? { k: "neg", e } : null;
|
|
292
|
+
}
|
|
293
|
+
return this.primary();
|
|
294
|
+
}
|
|
295
|
+
primary() {
|
|
296
|
+
this.skip();
|
|
297
|
+
if (this.i >= this.s.length) return null;
|
|
298
|
+
if (this.eat("(")) {
|
|
299
|
+
const e = this.or();
|
|
300
|
+
return e && this.eat(")") ? e : null;
|
|
301
|
+
}
|
|
302
|
+
const s = this.s;
|
|
303
|
+
const c = s[this.i];
|
|
304
|
+
if (isDigit(c)) {
|
|
305
|
+
const start3 = this.i;
|
|
306
|
+
while (isDigit(s[this.i])) this.i++;
|
|
307
|
+
if (s[this.i] === "." && isDigit(s[this.i + 1])) {
|
|
308
|
+
this.i++;
|
|
309
|
+
while (isDigit(s[this.i])) this.i++;
|
|
310
|
+
}
|
|
311
|
+
return { k: "lit", v: Number(s.slice(start3, this.i).join("")) };
|
|
312
|
+
}
|
|
313
|
+
if (!isLetter(c)) return null;
|
|
314
|
+
const start2 = this.i;
|
|
315
|
+
while (isLetter(s[this.i]) || isDigit(s[this.i])) this.i++;
|
|
316
|
+
const name = s.slice(start2, this.i).join("");
|
|
317
|
+
if (name.length > MAX_NAME) return null;
|
|
318
|
+
let j = this.i;
|
|
319
|
+
while (s[j] === " " || s[j] === " ") j++;
|
|
320
|
+
const called = s[j] === "(";
|
|
321
|
+
if (METRIC_NAMES.includes(name)) {
|
|
322
|
+
if (!called) return { k: "metric", metric: name, target: "" };
|
|
323
|
+
this.eat("(");
|
|
324
|
+
const from = this.i;
|
|
325
|
+
while (this.i < s.length && s[this.i] !== ")" && s[this.i] !== "(") this.i++;
|
|
326
|
+
if (s[this.i] !== ")") return null;
|
|
327
|
+
const target = s.slice(from, this.i).join("").replace(/^[ \t]+|[ \t]+$/g, "");
|
|
328
|
+
this.i++;
|
|
329
|
+
return { k: "metric", metric: name, target };
|
|
330
|
+
}
|
|
331
|
+
if (name in FUNCTIONS) {
|
|
332
|
+
if (!called || !this.eat("(")) return null;
|
|
333
|
+
const args = [];
|
|
334
|
+
if (!this.eat(")")) {
|
|
335
|
+
do {
|
|
336
|
+
const a = this.or();
|
|
337
|
+
if (!a) return null;
|
|
338
|
+
args.push(a);
|
|
339
|
+
} while (this.eat(","));
|
|
340
|
+
if (!this.eat(")")) return null;
|
|
341
|
+
}
|
|
342
|
+
const arity = FUNCTIONS[name];
|
|
343
|
+
if (arity !== null ? args.length !== arity : args.length === 0) return null;
|
|
344
|
+
return { k: "call", fn: name, args };
|
|
345
|
+
}
|
|
346
|
+
if (called) return null;
|
|
347
|
+
if (name === "true") return { k: "lit", v: true };
|
|
348
|
+
if (name === "false") return { k: "lit", v: false };
|
|
349
|
+
return { k: "name", name };
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
function describeVar(v) {
|
|
353
|
+
return typeof v === "boolean" ? v ? "true" : "false" : formatTwo(v);
|
|
354
|
+
}
|
|
355
|
+
function formatTwo(v) {
|
|
356
|
+
const n = Math.floor(Math.abs(v) * 100 + 0.5);
|
|
357
|
+
if (!(n < 1e15)) return v < 0 ? "-\u221E" : "\u221E";
|
|
358
|
+
const whole = Math.floor(n / 100), part = n - whole * 100;
|
|
359
|
+
let s = String(whole);
|
|
360
|
+
if (part > 0) s += "." + String(part).padStart(2, "0").replace(/0+$/, "");
|
|
361
|
+
return (v < 0 && n > 0 ? "-" : "") + s;
|
|
362
|
+
}
|
|
363
|
+
function filling(text, values) {
|
|
364
|
+
if (!text.includes("{")) return text;
|
|
365
|
+
let out = "", rest = text;
|
|
366
|
+
for (let open = rest.indexOf("{"); open >= 0; open = rest.indexOf("{")) {
|
|
367
|
+
out += rest.slice(0, open);
|
|
368
|
+
const close = rest.indexOf("}", open);
|
|
369
|
+
if (close < 0) {
|
|
370
|
+
out += rest.slice(open);
|
|
371
|
+
rest = "";
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
const inner = rest.slice(open + 1, close);
|
|
375
|
+
const e = inner.includes("{") ? null : parseExpr(inner);
|
|
376
|
+
const names = e ? exprNames(e) : [];
|
|
377
|
+
if (e && exprMetrics(e).length === 0 && names.length > 0 && names.every((n) => values[n] !== void 0)) out += describeVar(evaluate(e, values));
|
|
378
|
+
else out += rest.slice(open, close + 1);
|
|
379
|
+
rest = rest.slice(close + 1);
|
|
380
|
+
}
|
|
381
|
+
return out + rest;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/state.ts
|
|
385
|
+
var MAX_NAME2 = 64;
|
|
386
|
+
function isName(name) {
|
|
387
|
+
return name.length <= MAX_NAME2 && /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
|
|
388
|
+
}
|
|
389
|
+
function mentionsMetric(condition) {
|
|
390
|
+
const e = parseExpr(condition);
|
|
391
|
+
return e !== null && exprMetrics(e).length > 0;
|
|
392
|
+
}
|
|
131
393
|
function holds(condition, state, metric = null) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (m !== null) {
|
|
136
|
-
const v = metric ? metric(m) : null;
|
|
137
|
-
return v !== null && passes(m, v);
|
|
138
|
-
}
|
|
139
|
-
let term = trim(all);
|
|
140
|
-
let negated = false;
|
|
141
|
-
if (term.startsWith("!")) {
|
|
142
|
-
negated = true;
|
|
143
|
-
term = trim(term.slice(1));
|
|
144
|
-
}
|
|
145
|
-
return (state[term] ?? false) !== negated;
|
|
146
|
-
}));
|
|
394
|
+
const e = parseExpr(condition);
|
|
395
|
+
if (!e) return false;
|
|
396
|
+
return truthy(evaluate(e, state, metric ? (m, target) => metric({ metric: m, target, op: "", value: 0 }) : null));
|
|
147
397
|
}
|
|
148
398
|
function stateValues(page, choice) {
|
|
149
399
|
if (choice === "all") return null;
|
|
@@ -182,7 +432,7 @@ function applyOverride(o, e) {
|
|
|
182
432
|
return out;
|
|
183
433
|
}
|
|
184
434
|
function changesState(c) {
|
|
185
|
-
return !!c && (c.reset === true || Object.keys(c.set ?? {}).length > 0 || (c.toggle ?? []).length > 0);
|
|
435
|
+
return !!c && (c.reset === true || Object.keys(c.set ?? {}).length > 0 || (c.toggle ?? []).length > 0 || !!c.run);
|
|
186
436
|
}
|
|
187
437
|
function applyClick(c, state, initial = {}) {
|
|
188
438
|
let out = { ...state };
|
|
@@ -191,12 +441,15 @@ function applyClick(c, state, initial = {}) {
|
|
|
191
441
|
for (const k of Object.keys(state)) out[k] = false;
|
|
192
442
|
Object.assign(out, initial);
|
|
193
443
|
}
|
|
194
|
-
|
|
195
|
-
for (const k of c.
|
|
444
|
+
const before = { ...out };
|
|
445
|
+
for (const [k, v] of Object.entries(c.set ?? {})) out[k] = clickValue(v, before, before[k]);
|
|
446
|
+
for (const k of c.toggle ?? []) out[k] = !truthy(out[k] ?? false);
|
|
196
447
|
return out;
|
|
197
448
|
}
|
|
198
|
-
function
|
|
199
|
-
|
|
449
|
+
function clickValue(v, state, current) {
|
|
450
|
+
if (typeof v !== "string") return v;
|
|
451
|
+
const e = parseExpr(v);
|
|
452
|
+
return e ? evaluate(e, state) : current ?? false;
|
|
200
453
|
}
|
|
201
454
|
|
|
202
455
|
// src/theme.ts
|
|
@@ -258,12 +511,12 @@ function resolvingPalette(page) {
|
|
|
258
511
|
}
|
|
259
512
|
|
|
260
513
|
// src/model.ts
|
|
261
|
-
var SUPPORTED_VERSION =
|
|
514
|
+
var SUPPORTED_VERSION = 3;
|
|
262
515
|
var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
|
|
263
516
|
var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
|
|
264
517
|
var KNOWN_FIELDS = {
|
|
265
518
|
file: ["version", "pages", "images"],
|
|
266
|
-
page: ["id", "name", "layers", "elements", "theme", "customTheme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
519
|
+
page: ["id", "name", "layers", "elements", "theme", "customTheme", "logic", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
267
520
|
flow: ["id", "from", "to", "every", "speed", "color", "size", "when", "spread", "volume", "overrides"],
|
|
268
521
|
flowOverride: ["when", "volume"],
|
|
269
522
|
symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
|
|
@@ -332,7 +585,7 @@ var KNOWN_FIELDS = {
|
|
|
332
585
|
repeat: ["start", "end", "count"],
|
|
333
586
|
cameraKey: ["t", "x", "y", "width", "height", "ease"],
|
|
334
587
|
marker: ["t", "name", "loop"],
|
|
335
|
-
click: ["scene", "url", "set", "toggle", "reset"],
|
|
588
|
+
click: ["scene", "url", "set", "toggle", "reset", "run"],
|
|
336
589
|
hover: ["stroke", "fill", "opacity"],
|
|
337
590
|
override: ["when", "stroke", "fill", "textColor", "opacity", "strokeWidth", "dash", "text", "reroute", "offline", "cost", "capacity", "delay"]
|
|
338
591
|
};
|
|
@@ -346,7 +599,7 @@ var inkOnDark = { r: 0.93, g: 0.93, b: 0.94, a: 1 };
|
|
|
346
599
|
var DottieFormatError = class extends Error {
|
|
347
600
|
};
|
|
348
601
|
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
349
|
-
var
|
|
602
|
+
var num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
350
603
|
var str = (v) => typeof v === "string" ? v : void 0;
|
|
351
604
|
var strings = (v) => isObj(v) && Object.values(v).every((x) => typeof x === "string") ? { ...v } : null;
|
|
352
605
|
var bool = (v) => typeof v === "boolean" ? v : void 0;
|
|
@@ -370,9 +623,9 @@ function webLink(url) {
|
|
|
370
623
|
}
|
|
371
624
|
function rgba(v) {
|
|
372
625
|
if (!isObj(v)) return void 0;
|
|
373
|
-
const r =
|
|
626
|
+
const r = num2(v.r), g = num2(v.g), b = num2(v.b);
|
|
374
627
|
if (r === void 0 || g === void 0 || b === void 0) return void 0;
|
|
375
|
-
return { r, g, b, a:
|
|
628
|
+
return { r, g, b, a: num2(v.a) ?? 1 };
|
|
376
629
|
}
|
|
377
630
|
var Reader = class {
|
|
378
631
|
constructor() {
|
|
@@ -401,15 +654,15 @@ var Reader = class {
|
|
|
401
654
|
stroke: rgba(s.stroke) ?? { ...black },
|
|
402
655
|
fill: rgba(s.fill) ?? { ...clear },
|
|
403
656
|
fillStyle: ["solid", "hachure", "crossHatch", "grid"].includes(fillStyle) ? fillStyle : "solid",
|
|
404
|
-
strokeWidth:
|
|
657
|
+
strokeWidth: num2(s.strokeWidth) ?? 2,
|
|
405
658
|
dash: ["solid", "dashed", "dotted"].includes(dash) ? dash : "solid",
|
|
406
|
-
roughness:
|
|
407
|
-
bowing:
|
|
408
|
-
fontSize:
|
|
659
|
+
roughness: num2(s.roughness) ?? 1,
|
|
660
|
+
bowing: num2(s.bowing) ?? 1,
|
|
661
|
+
fontSize: num2(s.fontSize) ?? 18,
|
|
409
662
|
font: str(s.font) || null,
|
|
410
|
-
gridSize:
|
|
411
|
-
opacity:
|
|
412
|
-
dashOffset:
|
|
663
|
+
gridSize: num2(s.gridSize) ?? null,
|
|
664
|
+
opacity: num2(s.opacity) ?? null,
|
|
665
|
+
dashOffset: num2(s.dashOffset) ?? null,
|
|
413
666
|
textColor: rgba(s.textColor) ?? null,
|
|
414
667
|
named: this.colorNames(s.named)
|
|
415
668
|
};
|
|
@@ -443,13 +696,13 @@ var Reader = class {
|
|
|
443
696
|
endpoint(v) {
|
|
444
697
|
const e = isObj(v) ? v : {};
|
|
445
698
|
const port = str(e.port);
|
|
446
|
-
const portIndex =
|
|
699
|
+
const portIndex = num2(e.portIndex);
|
|
447
700
|
return {
|
|
448
701
|
elementID: uuid(e.elementID) ?? null,
|
|
449
702
|
port: port && ["top", "right", "bottom", "left"].includes(port) ? port : null,
|
|
450
703
|
portIndex: portIndex !== void 0 && Number.isInteger(portIndex) && portIndex >= 0 ? portIndex : null,
|
|
451
|
-
x:
|
|
452
|
-
y:
|
|
704
|
+
x: num2(e.x) ?? 0,
|
|
705
|
+
y: num2(e.y) ?? 0
|
|
453
706
|
};
|
|
454
707
|
}
|
|
455
708
|
connector(v) {
|
|
@@ -468,7 +721,7 @@ var Reader = class {
|
|
|
468
721
|
endHead: head(v.endHead, "arrow"),
|
|
469
722
|
waypoints: Array.isArray(v.waypoints) ? v.waypoints.filter(isObj).map((w) => {
|
|
470
723
|
this.fields(w, "waypoint");
|
|
471
|
-
return { x:
|
|
724
|
+
return { x: num2(w.x) ?? 0, y: num2(w.y) ?? 0 };
|
|
472
725
|
}) : null,
|
|
473
726
|
hops: typeof v.hops === "boolean" ? v.hops : null,
|
|
474
727
|
avoid: typeof v.avoid === "boolean" ? v.avoid : null,
|
|
@@ -504,20 +757,20 @@ var Reader = class {
|
|
|
504
757
|
id,
|
|
505
758
|
kind,
|
|
506
759
|
layerID,
|
|
507
|
-
x:
|
|
508
|
-
y:
|
|
509
|
-
width:
|
|
510
|
-
height:
|
|
760
|
+
x: num2(v.x) ?? 0,
|
|
761
|
+
y: num2(v.y) ?? 0,
|
|
762
|
+
width: num2(v.width) ?? 0,
|
|
763
|
+
height: num2(v.height) ?? 0,
|
|
511
764
|
style: this.style(v.style),
|
|
512
|
-
seed: (
|
|
765
|
+
seed: (num2(v.seed) ?? 1) >>> 0,
|
|
513
766
|
text: str(v.text) ?? null,
|
|
514
767
|
connector: this.connector(v.connector),
|
|
515
768
|
groupID: uuid(v.groupID) ?? null,
|
|
516
|
-
points: Array.isArray(v.points) ? v.points.filter(isObj).map((p) => ({ x:
|
|
769
|
+
points: Array.isArray(v.points) ? v.points.filter(isObj).map((p) => ({ x: num2(p.x) ?? 0, y: num2(p.y) ?? 0 })) : null,
|
|
517
770
|
path: Array.isArray(v.path) ? v.path.filter((c) => typeof c === "number") : null,
|
|
518
771
|
subpaths,
|
|
519
772
|
evenOdd: bool(v.evenOdd) ?? null,
|
|
520
|
-
rotation:
|
|
773
|
+
rotation: num2(v.rotation) ?? null,
|
|
521
774
|
link: uuid(v.link) ?? null,
|
|
522
775
|
url: str(v.url) ?? null,
|
|
523
776
|
anchor: str(v.anchor) ?? null,
|
|
@@ -527,18 +780,18 @@ var Reader = class {
|
|
|
527
780
|
overrides: this.overrides(v.overrides),
|
|
528
781
|
symbol: uuid(v.symbol) ?? null,
|
|
529
782
|
playback: this.playback(v.playback),
|
|
530
|
-
offset:
|
|
783
|
+
offset: num2(v.offset) ?? null,
|
|
531
784
|
args: strings(v.args),
|
|
532
785
|
image: this.image(v.image, images),
|
|
533
|
-
reveal:
|
|
786
|
+
reveal: num2(v.reveal) ?? null,
|
|
534
787
|
ports: Array.isArray(v.ports) ? v.ports.map((p) => {
|
|
535
788
|
const o = isObj(p) ? p : {};
|
|
536
|
-
return { x:
|
|
789
|
+
return { x: num2(o.x) ?? 0, y: num2(o.y) ?? 0 };
|
|
537
790
|
}) : null,
|
|
538
791
|
labelRect: this.labelRect(v.labelRect),
|
|
539
792
|
extension: this.extension(v.extension),
|
|
540
|
-
capacity:
|
|
541
|
-
delay:
|
|
793
|
+
capacity: num2(v.capacity) ?? null,
|
|
794
|
+
delay: num2(v.delay) ?? null,
|
|
542
795
|
queue: v.queue === true ? true : null,
|
|
543
796
|
gauge: this.gauge(v.gauge),
|
|
544
797
|
containerID: uuid(v.containerID) ?? null,
|
|
@@ -563,7 +816,7 @@ var Reader = class {
|
|
|
563
816
|
labelRect(v) {
|
|
564
817
|
if (!isObj(v)) return null;
|
|
565
818
|
this.fields(v, "normalizedRect");
|
|
566
|
-
return { x:
|
|
819
|
+
return { x: num2(v.x) ?? 0, y: num2(v.y) ?? 0, width: num2(v.width) ?? 0, height: num2(v.height) ?? 0 };
|
|
567
820
|
}
|
|
568
821
|
extension(v) {
|
|
569
822
|
if (!isObj(v)) return null;
|
|
@@ -584,7 +837,7 @@ var Reader = class {
|
|
|
584
837
|
variables(v) {
|
|
585
838
|
if (!isObj(v)) return null;
|
|
586
839
|
const out = {};
|
|
587
|
-
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean") out[k] = b;
|
|
840
|
+
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean" || typeof b === "number" && Number.isFinite(b)) out[k] = b;
|
|
588
841
|
return Object.keys(out).length > 0 ? out : null;
|
|
589
842
|
}
|
|
590
843
|
click(v) {
|
|
@@ -596,12 +849,22 @@ var Reader = class {
|
|
|
596
849
|
let set = null;
|
|
597
850
|
if (isObj(v.set)) {
|
|
598
851
|
set = {};
|
|
599
|
-
for (const [k, b] of Object.entries(v.set))
|
|
852
|
+
for (const [k, b] of Object.entries(v.set)) {
|
|
853
|
+
if (isName(k) && (typeof b === "boolean" || typeof b === "string" || typeof b === "number" && Number.isFinite(b))) set[k] = b;
|
|
854
|
+
}
|
|
600
855
|
if (Object.keys(set).length === 0) set = null;
|
|
601
856
|
}
|
|
602
857
|
const raw = typeof v.toggle === "string" ? [v.toggle] : Array.isArray(v.toggle) && v.toggle.every((x) => typeof x === "string") ? v.toggle : null;
|
|
603
858
|
const toggle = raw ? raw.filter(isName) : null;
|
|
604
|
-
|
|
859
|
+
const run = str(v.run);
|
|
860
|
+
return {
|
|
861
|
+
scene: str(v.scene) ?? null,
|
|
862
|
+
url: safe,
|
|
863
|
+
set,
|
|
864
|
+
toggle: toggle && toggle.length > 0 ? toggle : null,
|
|
865
|
+
reset: v.reset === true ? true : null,
|
|
866
|
+
run: run !== void 0 && isName(run) ? run : null
|
|
867
|
+
};
|
|
605
868
|
}
|
|
606
869
|
playback(v) {
|
|
607
870
|
const s = str(v);
|
|
@@ -617,7 +880,7 @@ var Reader = class {
|
|
|
617
880
|
return null;
|
|
618
881
|
}
|
|
619
882
|
const size = (x) => {
|
|
620
|
-
const n =
|
|
883
|
+
const n = num2(x);
|
|
621
884
|
return Math.max(1, n ?? 100);
|
|
622
885
|
};
|
|
623
886
|
const layers = (Array.isArray(v.layers) ? v.layers : []).filter(isObj).flatMap((l) => {
|
|
@@ -654,8 +917,8 @@ var Reader = class {
|
|
|
654
917
|
stroke: rgba(o.stroke) ?? null,
|
|
655
918
|
fill: rgba(o.fill) ?? null,
|
|
656
919
|
textColor: rgba(o.textColor) ?? null,
|
|
657
|
-
opacity:
|
|
658
|
-
strokeWidth:
|
|
920
|
+
opacity: num2(o.opacity) ?? null,
|
|
921
|
+
strokeWidth: num2(o.strokeWidth) ?? null,
|
|
659
922
|
dash: dash !== void 0 && ["solid", "dashed", "dotted"].includes(dash) ? dash : null,
|
|
660
923
|
text: str(o.text) ?? null,
|
|
661
924
|
reroute,
|
|
@@ -669,7 +932,7 @@ var Reader = class {
|
|
|
669
932
|
hover(v) {
|
|
670
933
|
if (!isObj(v)) return null;
|
|
671
934
|
this.fields(v, "hover");
|
|
672
|
-
return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity:
|
|
935
|
+
return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity: num2(v.opacity) ?? null };
|
|
673
936
|
}
|
|
674
937
|
ease(v) {
|
|
675
938
|
const s = str(v);
|
|
@@ -683,11 +946,11 @@ var Reader = class {
|
|
|
683
946
|
keyframe(v) {
|
|
684
947
|
if (!isObj(v)) return null;
|
|
685
948
|
this.fields(v, "keyframe");
|
|
686
|
-
const t =
|
|
949
|
+
const t = num2(v.t);
|
|
687
950
|
if (t === void 0) return null;
|
|
688
951
|
const k = { t };
|
|
689
952
|
for (const p of ["x", "y", "width", "height", "rotation", "strokeWidth", "reveal", "opacity"]) {
|
|
690
|
-
const n =
|
|
953
|
+
const n = num2(v[p]);
|
|
691
954
|
if (n !== void 0) k[p] = n;
|
|
692
955
|
}
|
|
693
956
|
const stroke = rgba(v.stroke), fill = rgba(v.fill), visible = bool(v.visible), ease = this.ease(v.ease);
|
|
@@ -705,39 +968,39 @@ var Reader = class {
|
|
|
705
968
|
const keys = (Array.isArray(v.keys) ? v.keys : []).map((k) => this.keyframe(k)).filter((k) => k !== null && k.t >= 0 && k.t <= MAX_DURATION);
|
|
706
969
|
stableSortBy(keys, (k) => k.t);
|
|
707
970
|
const follow = Array.isArray(v.follow) ? v.follow.filter(isObj).flatMap((f) => {
|
|
708
|
-
const path = uuid(f.path), start2 =
|
|
971
|
+
const path = uuid(f.path), start2 = num2(f.start), end = num2(f.end);
|
|
709
972
|
if (!path || start2 === void 0 || end === void 0 || start2 < 0 || end > MAX_DURATION) return [];
|
|
710
|
-
return [{ path, start: start2, end, ease: this.ease(f.ease), reverse: bool(f.reverse) ?? null, orient: bool(f.orient) ?? null, laps:
|
|
973
|
+
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 }];
|
|
711
974
|
}) : null;
|
|
712
975
|
const repeats = Array.isArray(v.repeats) ? v.repeats.filter(isObj).flatMap((r) => {
|
|
713
|
-
const start2 =
|
|
976
|
+
const start2 = num2(r.start), end = num2(r.end);
|
|
714
977
|
if (start2 === void 0 || end === void 0) return [];
|
|
715
|
-
const count =
|
|
978
|
+
const count = num2(r.count);
|
|
716
979
|
const rep = { start: start2, end, count: count === void 0 ? null : Math.trunc(count) };
|
|
717
980
|
return repeatIsValid(rep) ? [rep] : [];
|
|
718
981
|
}) : null;
|
|
719
|
-
return { element, keys, boil: bool(v.boil) ?? null, follow, repeats, march: clampMarch(
|
|
982
|
+
return { element, keys, boil: bool(v.boil) ?? null, follow, repeats, march: clampMarch(num2(v.march)) };
|
|
720
983
|
}
|
|
721
984
|
timeline(v) {
|
|
722
985
|
if (!isObj(v)) return null;
|
|
723
986
|
this.fields(v, "timeline");
|
|
724
|
-
const boil =
|
|
987
|
+
const boil = num2(v.boil);
|
|
725
988
|
const camera = Array.isArray(v.camera) ? v.camera.filter(isObj).flatMap((c) => {
|
|
726
|
-
const t =
|
|
989
|
+
const t = num2(c.t), x = num2(c.x), y = num2(c.y), width = num2(c.width), height = num2(c.height);
|
|
727
990
|
if (t === void 0 || x === void 0 || y === void 0 || width === void 0 || height === void 0) return [];
|
|
728
991
|
if (!(width > 1 && height > 1 && t >= 0)) return [];
|
|
729
992
|
return [{ t, x, y, width, height, ease: this.ease(c.ease) }];
|
|
730
993
|
}) : null;
|
|
731
994
|
if (camera) stableSortBy(camera, (c) => c.t);
|
|
732
995
|
const markers = Array.isArray(v.markers) ? v.markers.filter(isObj).flatMap((m) => {
|
|
733
|
-
const t =
|
|
996
|
+
const t = num2(m.t);
|
|
734
997
|
return t !== void 0 && t >= 0 ? [{ t, name: str(m.name) ?? "", loop: bool(m.loop) ?? null }] : [];
|
|
735
998
|
}) : null;
|
|
736
999
|
if (markers) stableSortBy(markers, (m) => m.t);
|
|
737
|
-
const poster =
|
|
1000
|
+
const poster = num2(v.poster);
|
|
738
1001
|
return {
|
|
739
|
-
fps: clampFPS(
|
|
740
|
-
duration: clampTime(
|
|
1002
|
+
fps: clampFPS(num2(v.fps) ?? 12),
|
|
1003
|
+
duration: clampTime(num2(v.duration) ?? 0),
|
|
741
1004
|
boil: boil !== void 0 && Number.isInteger(boil) && boil > 0 ? Math.min(boil, 1e4) : null,
|
|
742
1005
|
settle: bool(v.settle) ?? null,
|
|
743
1006
|
poster: poster === void 0 ? null : clampTime(poster),
|
|
@@ -764,6 +1027,7 @@ var Reader = class {
|
|
|
764
1027
|
elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
|
|
765
1028
|
theme: str(v.theme) ?? null,
|
|
766
1029
|
customTheme: this.customTheme(v.customTheme),
|
|
1030
|
+
logic: typeof v.logic === "string" && v.logic.length > 0 ? v.logic : null,
|
|
767
1031
|
timeline: this.timeline(v.timeline),
|
|
768
1032
|
symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null,
|
|
769
1033
|
variables: this.variables(v.variables),
|
|
@@ -775,7 +1039,7 @@ var Reader = class {
|
|
|
775
1039
|
this.fields(f, "flow");
|
|
776
1040
|
const id = (x) => typeof x === "string" && UUID.test(x) ? x.toUpperCase() : NO_ID;
|
|
777
1041
|
const finite = (x) => {
|
|
778
|
-
const n =
|
|
1042
|
+
const n = num2(x);
|
|
779
1043
|
return n !== void 0 && Number.isFinite(n) ? n : null;
|
|
780
1044
|
};
|
|
781
1045
|
return {
|
|
@@ -815,7 +1079,7 @@ function parseDottie(source) {
|
|
|
815
1079
|
}
|
|
816
1080
|
}
|
|
817
1081
|
if (!isObj(json)) throw new DottieFormatError("Not a Dottie drawing");
|
|
818
|
-
const version =
|
|
1082
|
+
const version = num2(json.version);
|
|
819
1083
|
if (version === void 0) throw new DottieFormatError("Not a Dottie drawing (no version)");
|
|
820
1084
|
if (!Array.isArray(json.pages) || json.pages.length === 0) throw new DottieFormatError("A drawing needs at least one page");
|
|
821
1085
|
const reader = new Reader();
|
|
@@ -3063,7 +3327,7 @@ function hopCrossings(e, r, page, index) {
|
|
|
3063
3327
|
for (const samples of segments) {
|
|
3064
3328
|
const along = runningLengths(samples);
|
|
3065
3329
|
const length = along[along.length - 1];
|
|
3066
|
-
const
|
|
3330
|
+
const found2 = [];
|
|
3067
3331
|
const near = [];
|
|
3068
3332
|
const whole = boxOf(samples);
|
|
3069
3333
|
for (let n = 0; n < count; n++) if (touching(whole, lines[n].box)) near.push(lines[n]);
|
|
@@ -3080,12 +3344,12 @@ function hopCrossings(e, r, page, index) {
|
|
|
3080
3344
|
if (Math.abs(det) < 1e-9) continue;
|
|
3081
3345
|
const wx = a.x - p.x, wy = a.y - p.y;
|
|
3082
3346
|
const t = (wx * ey - wy * ex) / det, s = (wx * dy - wy * dx) / det;
|
|
3083
|
-
if (t > 0 && t < 1 && s >= 0 && s <= 1)
|
|
3347
|
+
if (t > 0 && t < 1 && s >= 0 && s <= 1) found2.push(along[i] + t * (along[i + 1] - along[i]));
|
|
3084
3348
|
}
|
|
3085
3349
|
}
|
|
3086
3350
|
}
|
|
3087
3351
|
const kept = [];
|
|
3088
|
-
for (const at of
|
|
3352
|
+
for (const at of found2.sort((x, y) => x - y)) {
|
|
3089
3353
|
if (at < rad + 2 || at > length - rad - 2) continue;
|
|
3090
3354
|
if (kept.length > 0 && at - kept[kept.length - 1] < 2 * rad + 2) continue;
|
|
3091
3355
|
kept.push(at);
|
|
@@ -3299,7 +3563,7 @@ var lerpColor = (a, b, u) => ({
|
|
|
3299
3563
|
b: mix01(a.b, b.b, u),
|
|
3300
3564
|
a: mix01(a.a, b.a, u)
|
|
3301
3565
|
});
|
|
3302
|
-
function
|
|
3566
|
+
function num3(track, t, p, base2) {
|
|
3303
3567
|
return value(track, t, (k) => k[p], base2, lerpNum);
|
|
3304
3568
|
}
|
|
3305
3569
|
function isChanging(track, t) {
|
|
@@ -3325,20 +3589,20 @@ function isVisible(track, t) {
|
|
|
3325
3589
|
function applyTrack(track, e, t) {
|
|
3326
3590
|
e = cloneElement(e);
|
|
3327
3591
|
if (track.keys.some((k) => k.reveal !== void 0)) {
|
|
3328
|
-
const r =
|
|
3592
|
+
const r = num3(track, t, "reveal", e.reveal ?? 1);
|
|
3329
3593
|
e.reveal = r >= 1 ? null : Math.max(0, r);
|
|
3330
3594
|
}
|
|
3331
3595
|
if (track.keys.some((k) => k.opacity !== void 0)) {
|
|
3332
|
-
const o = Math.min(Math.max(
|
|
3596
|
+
const o = Math.min(Math.max(num3(track, t, "opacity", e.style.opacity ?? 1), 0), 1);
|
|
3333
3597
|
e.style.opacity = o >= 1 ? null : o;
|
|
3334
3598
|
}
|
|
3335
3599
|
if (e.kind === "connector") return e;
|
|
3336
|
-
e.x =
|
|
3337
|
-
e.y =
|
|
3338
|
-
e.width = Math.max(0,
|
|
3339
|
-
e.height = Math.max(0,
|
|
3340
|
-
if (track.keys.some((k) => k.rotation !== void 0)) e.rotation =
|
|
3341
|
-
e.style.strokeWidth = Math.max(0,
|
|
3600
|
+
e.x = num3(track, t, "x", e.x);
|
|
3601
|
+
e.y = num3(track, t, "y", e.y);
|
|
3602
|
+
e.width = Math.max(0, num3(track, t, "width", e.width));
|
|
3603
|
+
e.height = Math.max(0, num3(track, t, "height", e.height));
|
|
3604
|
+
if (track.keys.some((k) => k.rotation !== void 0)) e.rotation = num3(track, t, "rotation", angle(e));
|
|
3605
|
+
e.style.strokeWidth = Math.max(0, num3(track, t, "strokeWidth", e.style.strokeWidth));
|
|
3342
3606
|
e.style.stroke = value(track, t, (k) => k.stroke, e.style.stroke, lerpColor);
|
|
3343
3607
|
e.style.fill = value(track, t, (k) => k.fill, e.style.fill, lerpColor);
|
|
3344
3608
|
return e;
|
|
@@ -3414,7 +3678,8 @@ function posedWithMetrics(page, t, state = "initial", since = null, carried = {}
|
|
|
3414
3678
|
}
|
|
3415
3679
|
const flowing = (page.flows ?? []).length > 0;
|
|
3416
3680
|
const measuring = flowing || page.elements.some((e) => e.gauge || (e.overrides ?? []).some((o) => mentionsMetric(o.when)));
|
|
3417
|
-
const
|
|
3681
|
+
const fills = values !== null && Object.keys(values).length > 0 && page.elements.some((e) => e.text?.includes("{") || (e.overrides ?? []).some((o) => o.text?.includes("{")));
|
|
3682
|
+
const timeline = page.timeline ?? (off.size === 0 && active.size === 0 && !measuring && !fills ? null : EMPTY_TIMELINE);
|
|
3418
3683
|
if (!timeline) return { frame: page, metrics: emptyMetrics() };
|
|
3419
3684
|
t = clampTime(t);
|
|
3420
3685
|
const tracks = trackMap(timeline);
|
|
@@ -3450,6 +3715,10 @@ function posedWithMetrics(page, t, state = "initial", since = null, carried = {}
|
|
|
3450
3715
|
const track = tracks.get(e.id);
|
|
3451
3716
|
let out = track ? applyTrack(track, e, t) : e;
|
|
3452
3717
|
for (const o of active.get(e.id) ?? []) out = applyOverride(o, out);
|
|
3718
|
+
if (fills && values && out.text) {
|
|
3719
|
+
const text = filling(out.text, values);
|
|
3720
|
+
if (text !== out.text) out = { ...out, text };
|
|
3721
|
+
}
|
|
3453
3722
|
const list = active.get(e.id);
|
|
3454
3723
|
if (list) {
|
|
3455
3724
|
const caps = list.map((o) => o.capacity).filter((c) => c !== null && Number.isFinite(c));
|
|
@@ -3767,7 +4036,12 @@ function clickTargets(page, file) {
|
|
|
3767
4036
|
let pageOf = null;
|
|
3768
4037
|
for (const e of page.elements) {
|
|
3769
4038
|
const c = e.click;
|
|
3770
|
-
const state = c && changesState(c) ? {
|
|
4039
|
+
const state = c && changesState(c) ? {
|
|
4040
|
+
...c.set ? { set: c.set } : {},
|
|
4041
|
+
...c.toggle ? { toggle: c.toggle } : {},
|
|
4042
|
+
...c.reset ? { reset: true } : {},
|
|
4043
|
+
...c.run ? { run: c.run } : {}
|
|
4044
|
+
} : null;
|
|
3771
4045
|
if (c?.scene) {
|
|
3772
4046
|
const i = sceneNamed(c.scene, markers);
|
|
3773
4047
|
if (i >= 0) {
|
|
@@ -4805,6 +5079,40 @@ function servePlans() {
|
|
|
4805
5079
|
});
|
|
4806
5080
|
}
|
|
4807
5081
|
|
|
5082
|
+
// src/logic-loader.ts
|
|
5083
|
+
var scriptSrc = typeof document !== "undefined" ? document.currentScript?.src ?? null : null;
|
|
5084
|
+
var found = null;
|
|
5085
|
+
var present = () => globalThis.DottieLogic;
|
|
5086
|
+
function logicEngine() {
|
|
5087
|
+
found ?? (found = (async () => {
|
|
5088
|
+
if (!present()) {
|
|
5089
|
+
const here = import.meta.url;
|
|
5090
|
+
const base2 = here ?? scriptSrc;
|
|
5091
|
+
if (!base2) throw new Error("No dottie-logic.min.js on the page, and no address to find it from");
|
|
5092
|
+
const url = new URL("./dottie-logic.min.js", base2).href;
|
|
5093
|
+
if (here) await import(
|
|
5094
|
+
/* @vite-ignore */
|
|
5095
|
+
url
|
|
5096
|
+
);
|
|
5097
|
+
else await new Promise((resolve2, reject) => {
|
|
5098
|
+
const s = document.createElement("script");
|
|
5099
|
+
s.src = url;
|
|
5100
|
+
s.onload = () => resolve2();
|
|
5101
|
+
s.onerror = () => reject(new Error(`Couldn't load ${url}`));
|
|
5102
|
+
document.head.append(s);
|
|
5103
|
+
});
|
|
5104
|
+
}
|
|
5105
|
+
const engine = present();
|
|
5106
|
+
if (!engine) throw new Error("dottie-logic.min.js didn't load");
|
|
5107
|
+
await engine.loadLogic();
|
|
5108
|
+
return engine;
|
|
5109
|
+
})());
|
|
5110
|
+
found.catch(() => {
|
|
5111
|
+
found = null;
|
|
5112
|
+
});
|
|
5113
|
+
return found;
|
|
5114
|
+
}
|
|
5115
|
+
|
|
4808
5116
|
// src/player.ts
|
|
4809
5117
|
var WHITE = { r: 1, g: 1, b: 1, a: 1 };
|
|
4810
5118
|
function pageIndex(file, spec) {
|
|
@@ -5146,6 +5454,9 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5146
5454
|
__publicField(this, "stillTime", null);
|
|
5147
5455
|
__publicField(this, "visible", true);
|
|
5148
5456
|
__publicField(this, "observer", null);
|
|
5457
|
+
/** Runs handler `handler` from the page's logic for a click on `id` (DottieDocument.clicked). Clicks take turns, so
|
|
5458
|
+
* each handler sees the state the one before left. */
|
|
5459
|
+
__publicField(this, "logicQueue", Promise.resolve());
|
|
5149
5460
|
this.root = this.attachShadow({ mode: "open" });
|
|
5150
5461
|
this.root.innerHTML = `<style>${STYLE}</style><div class="stage" part="stage" role="img"></div>
|
|
5151
5462
|
<div class="bar" part="controls" hidden>
|
|
@@ -5372,7 +5683,9 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5372
5683
|
this.scheduleReplay();
|
|
5373
5684
|
}
|
|
5374
5685
|
if (!click) return;
|
|
5375
|
-
if (click.
|
|
5686
|
+
if (click.run && p.page.logic && this.hasAttribute("allow-logic")) {
|
|
5687
|
+
this.runLogic(click.run, id, click.reset === true);
|
|
5688
|
+
} else if (click.set || click.toggle || click.reset) {
|
|
5376
5689
|
this.setState(applyClick(
|
|
5377
5690
|
{ scene: null, url: null, set: click.set ?? null, toggle: click.toggle ?? null, reset: click.reset ?? null },
|
|
5378
5691
|
p.state,
|
|
@@ -5394,6 +5707,35 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5394
5707
|
}
|
|
5395
5708
|
}
|
|
5396
5709
|
}
|
|
5710
|
+
runLogic(handler, id, reset) {
|
|
5711
|
+
const p = this.playback;
|
|
5712
|
+
if (!p) return;
|
|
5713
|
+
const e = p.page.elements.find((x) => x.id === id);
|
|
5714
|
+
const s = p.sceneAt(p.frame);
|
|
5715
|
+
const event = {
|
|
5716
|
+
id,
|
|
5717
|
+
anchor: e?.anchor ?? null,
|
|
5718
|
+
text: e?.text?.split("\n")[0] ?? null,
|
|
5719
|
+
time: this.currentTime,
|
|
5720
|
+
scene: s < 0 ? null : p.sceneNames[s]
|
|
5721
|
+
};
|
|
5722
|
+
this.logicQueue = this.logicQueue.then(async () => {
|
|
5723
|
+
const engine = await logicEngine();
|
|
5724
|
+
const now = this.playback;
|
|
5725
|
+
if (!now || now !== p || !now.page.logic) return;
|
|
5726
|
+
let start2 = now.state;
|
|
5727
|
+
if (reset) {
|
|
5728
|
+
start2 = {};
|
|
5729
|
+
for (const k of Object.keys(now.state)) start2[k] = false;
|
|
5730
|
+
Object.assign(start2, now.page.variables ?? {});
|
|
5731
|
+
}
|
|
5732
|
+
const r = engine.runLogic(now.page.logic, handler, start2, event);
|
|
5733
|
+
if (r.ok) this.setState({ ...start2, ...r.set });
|
|
5734
|
+
else this.dispatchEvent(new CustomEvent("logicerror", { detail: { element: id, handler, error: r.error } }));
|
|
5735
|
+
}).catch((err) => {
|
|
5736
|
+
this.dispatchEvent(new CustomEvent("logicerror", { detail: { element: id, handler, error: err.message } }));
|
|
5737
|
+
});
|
|
5738
|
+
}
|
|
5397
5739
|
/** The page's variables now. */
|
|
5398
5740
|
get variables() {
|
|
5399
5741
|
return { ...this.playback?.state ?? {} };
|
|
@@ -5500,7 +5842,7 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5500
5842
|
if (handled) e.preventDefault();
|
|
5501
5843
|
}
|
|
5502
5844
|
};
|
|
5503
|
-
__publicField(DottiePlayer, "observedAttributes", ["src", "page", "at", "clean", "no-controls", "label"]);
|
|
5845
|
+
__publicField(DottiePlayer, "observedAttributes", ["src", "page", "at", "clean", "no-controls", "label", "allow-logic"]);
|
|
5504
5846
|
function clickable(target) {
|
|
5505
5847
|
const el = target;
|
|
5506
5848
|
return el && typeof el.closest === "function" ? el.closest(".dottie-a") : null;
|
|
@@ -5605,7 +5947,7 @@ function render(source, options = {}) {
|
|
|
5605
5947
|
function isFile(v) {
|
|
5606
5948
|
return typeof v === "object" && v !== null && Array.isArray(v.pages) && Array.isArray(v.warnings);
|
|
5607
5949
|
}
|
|
5608
|
-
var VERSION = "0.
|
|
5950
|
+
var VERSION = "0.11.0";
|
|
5609
5951
|
|
|
5610
5952
|
// src/player-entry.ts
|
|
5611
5953
|
servePlans();
|