@phin-tech/dottie 0.6.3 → 0.8.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 +6 -0
- package/README.md +9 -3
- package/dist/compile.js +122 -12
- package/dist/dottie-compile.mjs +123 -13
- package/dist/dottie-player.js +1006 -52
- package/dist/dottie-player.min.js +55 -53
- package/dist/dottie.js +1006 -52
- package/dist/types/clicks.d.ts +9 -2
- package/dist/types/flows.d.ts +26 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/measure.d.ts +58 -0
- package/dist/types/model.d.ts +76 -1
- package/dist/types/player.d.ts +31 -3
- package/dist/types/state.d.ts +40 -0
- package/dist/types/timeline.d.ts +13 -3
- package/package.json +1 -1
package/PARITY.md
CHANGED
|
@@ -47,6 +47,12 @@ fixture, so the gap is visible rather than silent.
|
|
|
47
47
|
| Symbol parameters (`{{name}}` in a symbol's text, filled per copy) | Supported | [edge/params](../fixtures/edge/params.dottie) | Defaults, undeclared names left as written, values passed into nested copies, filled before text types out |
|
|
48
48
|
| Copies that play when clicked | Supported | [edge/buttons](../fixtures/edge/buttons.dottie) | `<dottie-player>` only: the HTML export is drawn ahead of time and shows them at rest |
|
|
49
49
|
| Clicks: play a scene, open a link, hover looks, keyboard | Supported | [edge/clicks](../fixtures/edge/clicks.dottie) | As the HTML export does; only http, https and mailto links are followed |
|
|
50
|
+
| Page variables and conditions (`variables`, `when`) | Supported | [edge/state](../fixtures/edge/state.dottie) | Starting values in every still and export; connectors go with a shape the state hides |
|
|
51
|
+
| Overrides (`overrides`: restyle and reroute under a condition) | Supported | [edge/state](../fixtures/edge/state.dottie) | Opacity multiplies; a reroute or key that isn't an id is dropped as DottieKit drops it |
|
|
52
|
+
| Traffic flows (`flows`) | Supported | [edge/flows](../fixtures/edge/flows.dottie) | Routed each frame over the connectors drawn, cheapest by connector `cost` (ADR 0002); packet ids derive from the flow's |
|
|
53
|
+
| Volume, capacity, delay, metrics, gauges | Supported | [edge/flows](../fixtures/edge/flows.dottie) (Capacity page) | `measure.ts` mirrors `Metrics.swift` (ADR 0003): assignment in page order, backlog piles, conditions on metrics, gauges expanded to `.path` elements |
|
|
54
|
+
| Clicks that set and toggle variables | Supported | [edge/state](../fixtures/edge/state.dottie) | Change what shows without touching the playhead. The HTML export of a page with state is this player |
|
|
55
|
+
| Looping scenes (`loop` on a marker) | Supported | [edge/state](../fixtures/edge/state.dottie) | As the HTML export plays them: no stop on entering, round from the scene's end to its start |
|
|
50
56
|
| Links out of the drawing (`url`) | Supported | [edge/links](../fixtures/edge/links.dottie) | A web or mail `url` opens when clicked if there is no `click`, and SVG export wraps it in `<a>`; a link to another drawing does nothing on the web |
|
|
51
57
|
| Links between shapes and pages | Partial | [edge/clicks](../fixtures/edge/clicks.dottie) | A link to a shape on another page shows that page; one on the same page does nothing. The HTML export holds one page, so it has neither |
|
|
52
58
|
| Editing | Missing | | |
|
package/README.md
CHANGED
|
@@ -33,11 +33,17 @@ Elements the drawing makes clickable work with the mouse and the keyboard (Tab,
|
|
|
33
33
|
a named scene on to the next one, opens a link in a new tab (only `http`, `https` and `mailto`), or shows the page a
|
|
34
34
|
shape's link points at. Each shows its hover look under the pointer; button symbols show their over and down looks
|
|
35
35
|
and take clicks on their hit area, and a symbol's copy set to play when clicked plays its animation through. Every
|
|
36
|
-
click first fires a cancelable `action` event whose `detail` is `{element, scene | url | page,
|
|
37
|
-
`preventDefault()` to handle it yourself.
|
|
36
|
+
click first fires a cancelable `action` event whose `detail` is `{element, scene | url | page | state, set?, toggle?,
|
|
37
|
+
replay?}`; call `preventDefault()` to handle it yourself.
|
|
38
|
+
|
|
39
|
+
A click can also set or toggle the page's variables, which show and hide the elements with a `when` while the
|
|
40
|
+
animation keeps playing: a diagram you leave running that people poke. Elements can restyle themselves under a
|
|
41
|
+
condition (`overrides`), and a page's `flows` send traffic over whatever connectors are drawn, so it goes round a
|
|
42
|
+
region a click took down. A looping scene plays round until a click or next leaves it, instead of stopping.
|
|
38
43
|
|
|
39
44
|
The element has `play()`, `pause()`, `nextScene()`, `prevScene()`, `activate(elementId)`, `currentTime`
|
|
40
|
-
(settable), `duration`, `paused`, and fires `load`, `frame`, `action
|
|
45
|
+
(settable), `duration`, `paused`, `variables` and `setState({name: value})`, and fires `load`, `frame`, `action`,
|
|
46
|
+
`state` (with `{variables}`, after a change) and `error` events. Its page color comes from the drawing's theme; set
|
|
41
47
|
`--dottie-radius` to round it, and style its parts with `::part(stage)` and `::part(controls)`.
|
|
42
48
|
|
|
43
49
|
Without modules, `dist/dottie-player.min.js` is a classic script that registers the element and sets
|
package/dist/compile.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! dottie.js 0.
|
|
1
|
+
/*! dottie.js 0.8.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
4
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -44,13 +44,21 @@ function defaultBase() {
|
|
|
44
44
|
}
|
|
45
45
|
var base = defaultBase();
|
|
46
46
|
|
|
47
|
+
// src/state.ts
|
|
48
|
+
var MAX_NAME = 64;
|
|
49
|
+
function isName(name) {
|
|
50
|
+
return name.length <= MAX_NAME && /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
|
|
51
|
+
}
|
|
52
|
+
|
|
47
53
|
// src/model.ts
|
|
48
54
|
var SUPPORTED_VERSION = 2;
|
|
49
55
|
var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
|
|
50
56
|
var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
|
|
51
57
|
var KNOWN_FIELDS = {
|
|
52
58
|
file: ["version", "pages", "images"],
|
|
53
|
-
page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols"],
|
|
59
|
+
page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
60
|
+
flow: ["id", "from", "to", "every", "speed", "color", "size", "when", "spread", "volume", "overrides"],
|
|
61
|
+
flowOverride: ["when", "volume"],
|
|
54
62
|
symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
|
|
55
63
|
layer: ["id", "name", "visible", "locked"],
|
|
56
64
|
rgba: ["r", "g", "b", "a"],
|
|
@@ -78,6 +86,8 @@ var KNOWN_FIELDS = {
|
|
|
78
86
|
"anchor",
|
|
79
87
|
"click",
|
|
80
88
|
"hover",
|
|
89
|
+
"when",
|
|
90
|
+
"overrides",
|
|
81
91
|
"symbol",
|
|
82
92
|
"playback",
|
|
83
93
|
"offset",
|
|
@@ -86,8 +96,13 @@ var KNOWN_FIELDS = {
|
|
|
86
96
|
"reveal",
|
|
87
97
|
"ports",
|
|
88
98
|
"labelRect",
|
|
89
|
-
"extension"
|
|
99
|
+
"extension",
|
|
100
|
+
"capacity",
|
|
101
|
+
"delay",
|
|
102
|
+
"queue",
|
|
103
|
+
"gauge"
|
|
90
104
|
],
|
|
105
|
+
gauge: ["metric", "of", "max", "warn", "alarm", "style", "label"],
|
|
91
106
|
strokePoint: ["x", "y"],
|
|
92
107
|
subpath: ["closed", "coords"],
|
|
93
108
|
inlineImage: ["data", "format"],
|
|
@@ -95,7 +110,7 @@ var KNOWN_FIELDS = {
|
|
|
95
110
|
extensionRef: ["id", "shape", "version", "params"],
|
|
96
111
|
normalizedPoint: ["x", "y"],
|
|
97
112
|
normalizedRect: ["x", "y", "width", "height"],
|
|
98
|
-
connector: ["start", "end", "routing", "startHead", "endHead", "waypoints", "hops"],
|
|
113
|
+
connector: ["start", "end", "routing", "startHead", "endHead", "waypoints", "hops", "cost"],
|
|
99
114
|
waypoint: ["x", "y"],
|
|
100
115
|
timeline: ["fps", "duration", "boil", "settle", "poster", "tracks", "camera", "markers"],
|
|
101
116
|
track: ["element", "keys", "boil", "follow", "repeats", "march"],
|
|
@@ -103,9 +118,10 @@ var KNOWN_FIELDS = {
|
|
|
103
118
|
follow: ["path", "start", "end", "ease", "reverse", "orient", "laps"],
|
|
104
119
|
repeat: ["start", "end", "count"],
|
|
105
120
|
cameraKey: ["t", "x", "y", "width", "height", "ease"],
|
|
106
|
-
marker: ["t", "name"],
|
|
107
|
-
click: ["scene", "url"],
|
|
108
|
-
hover: ["stroke", "fill", "opacity"]
|
|
121
|
+
marker: ["t", "name", "loop"],
|
|
122
|
+
click: ["scene", "url", "set", "toggle", "reset"],
|
|
123
|
+
hover: ["stroke", "fill", "opacity"],
|
|
124
|
+
override: ["when", "stroke", "fill", "textColor", "opacity", "strokeWidth", "dash", "text", "reroute", "offline", "cost", "capacity", "delay"]
|
|
109
125
|
};
|
|
110
126
|
var MAX_DURATION = 3600;
|
|
111
127
|
var MAX_MARCH = 1e4;
|
|
@@ -119,6 +135,8 @@ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
|
119
135
|
var str = (v) => typeof v === "string" ? v : void 0;
|
|
120
136
|
var strings = (v) => isObj(v) && Object.values(v).every((x) => typeof x === "string") ? { ...v } : null;
|
|
121
137
|
var bool = (v) => typeof v === "boolean" ? v : void 0;
|
|
138
|
+
var NO_ID = "00000000-0000-0000-0000-000000000000";
|
|
139
|
+
var UUID = /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/;
|
|
122
140
|
var uuid = (v) => typeof v === "string" ? v.toUpperCase() : void 0;
|
|
123
141
|
function clampTime(t) {
|
|
124
142
|
if (!Number.isFinite(t)) return t === Infinity ? MAX_DURATION : 0;
|
|
@@ -207,7 +225,8 @@ var Reader = class {
|
|
|
207
225
|
this.fields(w, "waypoint");
|
|
208
226
|
return { x: num(w.x) ?? 0, y: num(w.y) ?? 0 };
|
|
209
227
|
}) : null,
|
|
210
|
-
hops: typeof v.hops === "boolean" ? v.hops : null
|
|
228
|
+
hops: typeof v.hops === "boolean" ? v.hops : null,
|
|
229
|
+
cost: typeof v.cost === "number" && Number.isFinite(v.cost) ? v.cost : null
|
|
211
230
|
};
|
|
212
231
|
}
|
|
213
232
|
image(v, table) {
|
|
@@ -258,6 +277,8 @@ var Reader = class {
|
|
|
258
277
|
anchor: str(v.anchor) ?? null,
|
|
259
278
|
click: this.click(v.click),
|
|
260
279
|
hover: this.hover(v.hover),
|
|
280
|
+
when: str(v.when) ?? null,
|
|
281
|
+
overrides: this.overrides(v.overrides),
|
|
261
282
|
symbol: uuid(v.symbol) ?? null,
|
|
262
283
|
playback: this.playback(v.playback),
|
|
263
284
|
offset: num(v.offset) ?? null,
|
|
@@ -269,7 +290,26 @@ var Reader = class {
|
|
|
269
290
|
return { x: num(o.x) ?? 0, y: num(o.y) ?? 0 };
|
|
270
291
|
}) : null,
|
|
271
292
|
labelRect: this.labelRect(v.labelRect),
|
|
272
|
-
extension: this.extension(v.extension)
|
|
293
|
+
extension: this.extension(v.extension),
|
|
294
|
+
capacity: num(v.capacity) ?? null,
|
|
295
|
+
delay: num(v.delay) ?? null,
|
|
296
|
+
queue: v.queue === true ? true : null,
|
|
297
|
+
gauge: this.gauge(v.gauge)
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
gauge(v) {
|
|
301
|
+
if (!isObj(v)) return null;
|
|
302
|
+
this.fields(v, "gauge");
|
|
303
|
+
const finite = (x) => typeof x === "number" && Number.isFinite(x) ? x : null;
|
|
304
|
+
const style = str(v.style);
|
|
305
|
+
return {
|
|
306
|
+
metric: str(v.metric) ?? "",
|
|
307
|
+
of: typeof v.of === "string" && UUID.test(v.of) ? v.of.toUpperCase() : NO_ID,
|
|
308
|
+
max: finite(v.max),
|
|
309
|
+
warn: finite(v.warn),
|
|
310
|
+
alarm: finite(v.alarm),
|
|
311
|
+
style: style === "number" || style === "bar" || style === "dial" ? style : null,
|
|
312
|
+
label: str(v.label) ?? null
|
|
273
313
|
};
|
|
274
314
|
}
|
|
275
315
|
labelRect(v) {
|
|
@@ -293,13 +333,27 @@ var Reader = class {
|
|
|
293
333
|
}
|
|
294
334
|
return { id, shape, version, params };
|
|
295
335
|
}
|
|
336
|
+
variables(v) {
|
|
337
|
+
if (!isObj(v)) return null;
|
|
338
|
+
const out = {};
|
|
339
|
+
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean") out[k] = b;
|
|
340
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
341
|
+
}
|
|
296
342
|
click(v) {
|
|
297
343
|
if (!isObj(v)) return null;
|
|
298
344
|
this.fields(v, "click");
|
|
299
345
|
const url = str(v.url);
|
|
300
346
|
const safe = url === void 0 ? null : allowedURL(url);
|
|
301
347
|
if (url !== void 0 && safe === null) this.warn("Skipped a click link that isn't http, https or mailto");
|
|
302
|
-
|
|
348
|
+
let set = null;
|
|
349
|
+
if (isObj(v.set)) {
|
|
350
|
+
set = {};
|
|
351
|
+
for (const [k, b] of Object.entries(v.set)) if (typeof b === "boolean" && isName(k)) set[k] = b;
|
|
352
|
+
if (Object.keys(set).length === 0) set = null;
|
|
353
|
+
}
|
|
354
|
+
const raw = typeof v.toggle === "string" ? [v.toggle] : Array.isArray(v.toggle) && v.toggle.every((x) => typeof x === "string") ? v.toggle : null;
|
|
355
|
+
const toggle = raw ? raw.filter(isName) : null;
|
|
356
|
+
return { scene: str(v.scene) ?? null, url: safe, set, toggle: toggle && toggle.length > 0 ? toggle : null, reset: v.reset === true ? true : null };
|
|
303
357
|
}
|
|
304
358
|
playback(v) {
|
|
305
359
|
const s = str(v);
|
|
@@ -335,6 +389,35 @@ var Reader = class {
|
|
|
335
389
|
params: strings(v.params)
|
|
336
390
|
};
|
|
337
391
|
}
|
|
392
|
+
/** As DottieKit reads them: a reroute whose targets aren't all ids is dropped, as are keys that aren't ids. */
|
|
393
|
+
overrides(v) {
|
|
394
|
+
if (!Array.isArray(v)) return null;
|
|
395
|
+
return v.filter(isObj).map((o) => {
|
|
396
|
+
this.fields(o, "override");
|
|
397
|
+
const dash = str(o.dash);
|
|
398
|
+
let reroute = null;
|
|
399
|
+
if (isObj(o.reroute) && Object.values(o.reroute).every((x) => typeof x === "string" && UUID.test(x))) {
|
|
400
|
+
reroute = {};
|
|
401
|
+
for (const [k, x] of Object.entries(o.reroute)) if (UUID.test(k)) reroute[k.toUpperCase()] = x.toUpperCase();
|
|
402
|
+
if (Object.keys(reroute).length === 0) reroute = null;
|
|
403
|
+
}
|
|
404
|
+
return {
|
|
405
|
+
when: str(o.when) ?? "",
|
|
406
|
+
stroke: rgba(o.stroke) ?? null,
|
|
407
|
+
fill: rgba(o.fill) ?? null,
|
|
408
|
+
textColor: rgba(o.textColor) ?? null,
|
|
409
|
+
opacity: num(o.opacity) ?? null,
|
|
410
|
+
strokeWidth: num(o.strokeWidth) ?? null,
|
|
411
|
+
dash: dash !== void 0 && ["solid", "dashed", "dotted"].includes(dash) ? dash : null,
|
|
412
|
+
text: str(o.text) ?? null,
|
|
413
|
+
reroute,
|
|
414
|
+
offline: o.offline === true ? true : null,
|
|
415
|
+
cost: typeof o.cost === "number" && Number.isFinite(o.cost) ? o.cost : null,
|
|
416
|
+
capacity: typeof o.capacity === "number" && Number.isFinite(o.capacity) ? o.capacity : null,
|
|
417
|
+
delay: typeof o.delay === "number" && Number.isFinite(o.delay) ? o.delay : null
|
|
418
|
+
};
|
|
419
|
+
});
|
|
420
|
+
}
|
|
338
421
|
hover(v) {
|
|
339
422
|
if (!isObj(v)) return null;
|
|
340
423
|
this.fields(v, "hover");
|
|
@@ -400,7 +483,7 @@ var Reader = class {
|
|
|
400
483
|
if (camera) stableSortBy(camera, (c) => c.t);
|
|
401
484
|
const markers = Array.isArray(v.markers) ? v.markers.filter(isObj).flatMap((m) => {
|
|
402
485
|
const t = num(m.t);
|
|
403
|
-
return t !== void 0 && t >= 0 ? [{ t, name: str(m.name) ?? "" }] : [];
|
|
486
|
+
return t !== void 0 && t >= 0 ? [{ t, name: str(m.name) ?? "", loop: bool(m.loop) ?? null }] : [];
|
|
404
487
|
}) : null;
|
|
405
488
|
if (markers) stableSortBy(markers, (m) => m.t);
|
|
406
489
|
const poster = num(v.poster);
|
|
@@ -433,7 +516,34 @@ var Reader = class {
|
|
|
433
516
|
elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
|
|
434
517
|
theme: str(v.theme) ?? null,
|
|
435
518
|
timeline: this.timeline(v.timeline),
|
|
436
|
-
symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null
|
|
519
|
+
symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null,
|
|
520
|
+
variables: this.variables(v.variables),
|
|
521
|
+
flows: Array.isArray(v.flows) ? v.flows.filter(isObj).map((f) => this.flow(f)) : null
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
/** As DottieKit reads one: a missing or bad id, source or destination sends nothing; bad destinations are dropped. */
|
|
525
|
+
flow(f) {
|
|
526
|
+
this.fields(f, "flow");
|
|
527
|
+
const id = (x) => typeof x === "string" && UUID.test(x) ? x.toUpperCase() : NO_ID;
|
|
528
|
+
const finite = (x) => {
|
|
529
|
+
const n = num(x);
|
|
530
|
+
return n !== void 0 && Number.isFinite(n) ? n : null;
|
|
531
|
+
};
|
|
532
|
+
return {
|
|
533
|
+
id: id(f.id),
|
|
534
|
+
from: id(f.from),
|
|
535
|
+
to: Array.isArray(f.to) && f.to.every((x) => typeof x === "string") ? f.to.filter((x) => UUID.test(x)).map((x) => x.toUpperCase()) : [],
|
|
536
|
+
every: finite(f.every),
|
|
537
|
+
speed: finite(f.speed),
|
|
538
|
+
size: finite(f.size),
|
|
539
|
+
color: isObj(f.color) && ["r", "g", "b", "a"].every((k) => typeof f.color[k] === "number") ? rgba(f.color) ?? null : null,
|
|
540
|
+
when: str(f.when) ?? null,
|
|
541
|
+
spread: f.spread === true ? true : null,
|
|
542
|
+
volume: finite(f.volume),
|
|
543
|
+
overrides: Array.isArray(f.overrides) && f.overrides.length > 0 ? f.overrides.filter(isObj).map((o) => {
|
|
544
|
+
this.fields(o, "flowOverride");
|
|
545
|
+
return { when: str(o.when) ?? "", volume: finite(o.volume) };
|
|
546
|
+
}) : null
|
|
437
547
|
};
|
|
438
548
|
}
|
|
439
549
|
};
|
package/dist/dottie-compile.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/*! dottie.js 0.
|
|
2
|
+
/*! dottie.js 0.8.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5
5
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -52,13 +52,21 @@ function defaultBase() {
|
|
|
52
52
|
}
|
|
53
53
|
var base = defaultBase();
|
|
54
54
|
|
|
55
|
+
// src/state.ts
|
|
56
|
+
var MAX_NAME = 64;
|
|
57
|
+
function isName(name) {
|
|
58
|
+
return name.length <= MAX_NAME && /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
|
|
59
|
+
}
|
|
60
|
+
|
|
55
61
|
// src/model.ts
|
|
56
62
|
var SUPPORTED_VERSION = 2;
|
|
57
63
|
var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
|
|
58
64
|
var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
|
|
59
65
|
var KNOWN_FIELDS = {
|
|
60
66
|
file: ["version", "pages", "images"],
|
|
61
|
-
page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols"],
|
|
67
|
+
page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
68
|
+
flow: ["id", "from", "to", "every", "speed", "color", "size", "when", "spread", "volume", "overrides"],
|
|
69
|
+
flowOverride: ["when", "volume"],
|
|
62
70
|
symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
|
|
63
71
|
layer: ["id", "name", "visible", "locked"],
|
|
64
72
|
rgba: ["r", "g", "b", "a"],
|
|
@@ -86,6 +94,8 @@ var KNOWN_FIELDS = {
|
|
|
86
94
|
"anchor",
|
|
87
95
|
"click",
|
|
88
96
|
"hover",
|
|
97
|
+
"when",
|
|
98
|
+
"overrides",
|
|
89
99
|
"symbol",
|
|
90
100
|
"playback",
|
|
91
101
|
"offset",
|
|
@@ -94,8 +104,13 @@ var KNOWN_FIELDS = {
|
|
|
94
104
|
"reveal",
|
|
95
105
|
"ports",
|
|
96
106
|
"labelRect",
|
|
97
|
-
"extension"
|
|
107
|
+
"extension",
|
|
108
|
+
"capacity",
|
|
109
|
+
"delay",
|
|
110
|
+
"queue",
|
|
111
|
+
"gauge"
|
|
98
112
|
],
|
|
113
|
+
gauge: ["metric", "of", "max", "warn", "alarm", "style", "label"],
|
|
99
114
|
strokePoint: ["x", "y"],
|
|
100
115
|
subpath: ["closed", "coords"],
|
|
101
116
|
inlineImage: ["data", "format"],
|
|
@@ -103,7 +118,7 @@ var KNOWN_FIELDS = {
|
|
|
103
118
|
extensionRef: ["id", "shape", "version", "params"],
|
|
104
119
|
normalizedPoint: ["x", "y"],
|
|
105
120
|
normalizedRect: ["x", "y", "width", "height"],
|
|
106
|
-
connector: ["start", "end", "routing", "startHead", "endHead", "waypoints", "hops"],
|
|
121
|
+
connector: ["start", "end", "routing", "startHead", "endHead", "waypoints", "hops", "cost"],
|
|
107
122
|
waypoint: ["x", "y"],
|
|
108
123
|
timeline: ["fps", "duration", "boil", "settle", "poster", "tracks", "camera", "markers"],
|
|
109
124
|
track: ["element", "keys", "boil", "follow", "repeats", "march"],
|
|
@@ -111,9 +126,10 @@ var KNOWN_FIELDS = {
|
|
|
111
126
|
follow: ["path", "start", "end", "ease", "reverse", "orient", "laps"],
|
|
112
127
|
repeat: ["start", "end", "count"],
|
|
113
128
|
cameraKey: ["t", "x", "y", "width", "height", "ease"],
|
|
114
|
-
marker: ["t", "name"],
|
|
115
|
-
click: ["scene", "url"],
|
|
116
|
-
hover: ["stroke", "fill", "opacity"]
|
|
129
|
+
marker: ["t", "name", "loop"],
|
|
130
|
+
click: ["scene", "url", "set", "toggle", "reset"],
|
|
131
|
+
hover: ["stroke", "fill", "opacity"],
|
|
132
|
+
override: ["when", "stroke", "fill", "textColor", "opacity", "strokeWidth", "dash", "text", "reroute", "offline", "cost", "capacity", "delay"]
|
|
117
133
|
};
|
|
118
134
|
var MAX_DURATION = 3600;
|
|
119
135
|
var MAX_MARCH = 1e4;
|
|
@@ -127,6 +143,8 @@ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
|
127
143
|
var str = (v) => typeof v === "string" ? v : void 0;
|
|
128
144
|
var strings = (v) => isObj(v) && Object.values(v).every((x) => typeof x === "string") ? { ...v } : null;
|
|
129
145
|
var bool = (v) => typeof v === "boolean" ? v : void 0;
|
|
146
|
+
var NO_ID = "00000000-0000-0000-0000-000000000000";
|
|
147
|
+
var UUID = /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/;
|
|
130
148
|
var uuid = (v) => typeof v === "string" ? v.toUpperCase() : void 0;
|
|
131
149
|
function clampTime(t) {
|
|
132
150
|
if (!Number.isFinite(t)) return t === Infinity ? MAX_DURATION : 0;
|
|
@@ -215,7 +233,8 @@ var Reader = class {
|
|
|
215
233
|
this.fields(w, "waypoint");
|
|
216
234
|
return { x: num(w.x) ?? 0, y: num(w.y) ?? 0 };
|
|
217
235
|
}) : null,
|
|
218
|
-
hops: typeof v.hops === "boolean" ? v.hops : null
|
|
236
|
+
hops: typeof v.hops === "boolean" ? v.hops : null,
|
|
237
|
+
cost: typeof v.cost === "number" && Number.isFinite(v.cost) ? v.cost : null
|
|
219
238
|
};
|
|
220
239
|
}
|
|
221
240
|
image(v, table) {
|
|
@@ -266,6 +285,8 @@ var Reader = class {
|
|
|
266
285
|
anchor: str(v.anchor) ?? null,
|
|
267
286
|
click: this.click(v.click),
|
|
268
287
|
hover: this.hover(v.hover),
|
|
288
|
+
when: str(v.when) ?? null,
|
|
289
|
+
overrides: this.overrides(v.overrides),
|
|
269
290
|
symbol: uuid(v.symbol) ?? null,
|
|
270
291
|
playback: this.playback(v.playback),
|
|
271
292
|
offset: num(v.offset) ?? null,
|
|
@@ -277,7 +298,26 @@ var Reader = class {
|
|
|
277
298
|
return { x: num(o.x) ?? 0, y: num(o.y) ?? 0 };
|
|
278
299
|
}) : null,
|
|
279
300
|
labelRect: this.labelRect(v.labelRect),
|
|
280
|
-
extension: this.extension(v.extension)
|
|
301
|
+
extension: this.extension(v.extension),
|
|
302
|
+
capacity: num(v.capacity) ?? null,
|
|
303
|
+
delay: num(v.delay) ?? null,
|
|
304
|
+
queue: v.queue === true ? true : null,
|
|
305
|
+
gauge: this.gauge(v.gauge)
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
gauge(v) {
|
|
309
|
+
if (!isObj(v)) return null;
|
|
310
|
+
this.fields(v, "gauge");
|
|
311
|
+
const finite = (x) => typeof x === "number" && Number.isFinite(x) ? x : null;
|
|
312
|
+
const style = str(v.style);
|
|
313
|
+
return {
|
|
314
|
+
metric: str(v.metric) ?? "",
|
|
315
|
+
of: typeof v.of === "string" && UUID.test(v.of) ? v.of.toUpperCase() : NO_ID,
|
|
316
|
+
max: finite(v.max),
|
|
317
|
+
warn: finite(v.warn),
|
|
318
|
+
alarm: finite(v.alarm),
|
|
319
|
+
style: style === "number" || style === "bar" || style === "dial" ? style : null,
|
|
320
|
+
label: str(v.label) ?? null
|
|
281
321
|
};
|
|
282
322
|
}
|
|
283
323
|
labelRect(v) {
|
|
@@ -301,13 +341,27 @@ var Reader = class {
|
|
|
301
341
|
}
|
|
302
342
|
return { id, shape, version, params };
|
|
303
343
|
}
|
|
344
|
+
variables(v) {
|
|
345
|
+
if (!isObj(v)) return null;
|
|
346
|
+
const out = {};
|
|
347
|
+
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean") out[k] = b;
|
|
348
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
349
|
+
}
|
|
304
350
|
click(v) {
|
|
305
351
|
if (!isObj(v)) return null;
|
|
306
352
|
this.fields(v, "click");
|
|
307
353
|
const url = str(v.url);
|
|
308
354
|
const safe = url === void 0 ? null : allowedURL(url);
|
|
309
355
|
if (url !== void 0 && safe === null) this.warn("Skipped a click link that isn't http, https or mailto");
|
|
310
|
-
|
|
356
|
+
let set = null;
|
|
357
|
+
if (isObj(v.set)) {
|
|
358
|
+
set = {};
|
|
359
|
+
for (const [k, b] of Object.entries(v.set)) if (typeof b === "boolean" && isName(k)) set[k] = b;
|
|
360
|
+
if (Object.keys(set).length === 0) set = null;
|
|
361
|
+
}
|
|
362
|
+
const raw = typeof v.toggle === "string" ? [v.toggle] : Array.isArray(v.toggle) && v.toggle.every((x) => typeof x === "string") ? v.toggle : null;
|
|
363
|
+
const toggle = raw ? raw.filter(isName) : null;
|
|
364
|
+
return { scene: str(v.scene) ?? null, url: safe, set, toggle: toggle && toggle.length > 0 ? toggle : null, reset: v.reset === true ? true : null };
|
|
311
365
|
}
|
|
312
366
|
playback(v) {
|
|
313
367
|
const s = str(v);
|
|
@@ -343,6 +397,35 @@ var Reader = class {
|
|
|
343
397
|
params: strings(v.params)
|
|
344
398
|
};
|
|
345
399
|
}
|
|
400
|
+
/** As DottieKit reads them: a reroute whose targets aren't all ids is dropped, as are keys that aren't ids. */
|
|
401
|
+
overrides(v) {
|
|
402
|
+
if (!Array.isArray(v)) return null;
|
|
403
|
+
return v.filter(isObj).map((o) => {
|
|
404
|
+
this.fields(o, "override");
|
|
405
|
+
const dash = str(o.dash);
|
|
406
|
+
let reroute = null;
|
|
407
|
+
if (isObj(o.reroute) && Object.values(o.reroute).every((x) => typeof x === "string" && UUID.test(x))) {
|
|
408
|
+
reroute = {};
|
|
409
|
+
for (const [k, x] of Object.entries(o.reroute)) if (UUID.test(k)) reroute[k.toUpperCase()] = x.toUpperCase();
|
|
410
|
+
if (Object.keys(reroute).length === 0) reroute = null;
|
|
411
|
+
}
|
|
412
|
+
return {
|
|
413
|
+
when: str(o.when) ?? "",
|
|
414
|
+
stroke: rgba(o.stroke) ?? null,
|
|
415
|
+
fill: rgba(o.fill) ?? null,
|
|
416
|
+
textColor: rgba(o.textColor) ?? null,
|
|
417
|
+
opacity: num(o.opacity) ?? null,
|
|
418
|
+
strokeWidth: num(o.strokeWidth) ?? null,
|
|
419
|
+
dash: dash !== void 0 && ["solid", "dashed", "dotted"].includes(dash) ? dash : null,
|
|
420
|
+
text: str(o.text) ?? null,
|
|
421
|
+
reroute,
|
|
422
|
+
offline: o.offline === true ? true : null,
|
|
423
|
+
cost: typeof o.cost === "number" && Number.isFinite(o.cost) ? o.cost : null,
|
|
424
|
+
capacity: typeof o.capacity === "number" && Number.isFinite(o.capacity) ? o.capacity : null,
|
|
425
|
+
delay: typeof o.delay === "number" && Number.isFinite(o.delay) ? o.delay : null
|
|
426
|
+
};
|
|
427
|
+
});
|
|
428
|
+
}
|
|
346
429
|
hover(v) {
|
|
347
430
|
if (!isObj(v)) return null;
|
|
348
431
|
this.fields(v, "hover");
|
|
@@ -408,7 +491,7 @@ var Reader = class {
|
|
|
408
491
|
if (camera) stableSortBy(camera, (c) => c.t);
|
|
409
492
|
const markers = Array.isArray(v.markers) ? v.markers.filter(isObj).flatMap((m) => {
|
|
410
493
|
const t = num(m.t);
|
|
411
|
-
return t !== void 0 && t >= 0 ? [{ t, name: str(m.name) ?? "" }] : [];
|
|
494
|
+
return t !== void 0 && t >= 0 ? [{ t, name: str(m.name) ?? "", loop: bool(m.loop) ?? null }] : [];
|
|
412
495
|
}) : null;
|
|
413
496
|
if (markers) stableSortBy(markers, (m) => m.t);
|
|
414
497
|
const poster = num(v.poster);
|
|
@@ -441,7 +524,34 @@ var Reader = class {
|
|
|
441
524
|
elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
|
|
442
525
|
theme: str(v.theme) ?? null,
|
|
443
526
|
timeline: this.timeline(v.timeline),
|
|
444
|
-
symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null
|
|
527
|
+
symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null,
|
|
528
|
+
variables: this.variables(v.variables),
|
|
529
|
+
flows: Array.isArray(v.flows) ? v.flows.filter(isObj).map((f) => this.flow(f)) : null
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
/** As DottieKit reads one: a missing or bad id, source or destination sends nothing; bad destinations are dropped. */
|
|
533
|
+
flow(f) {
|
|
534
|
+
this.fields(f, "flow");
|
|
535
|
+
const id = (x) => typeof x === "string" && UUID.test(x) ? x.toUpperCase() : NO_ID;
|
|
536
|
+
const finite = (x) => {
|
|
537
|
+
const n = num(x);
|
|
538
|
+
return n !== void 0 && Number.isFinite(n) ? n : null;
|
|
539
|
+
};
|
|
540
|
+
return {
|
|
541
|
+
id: id(f.id),
|
|
542
|
+
from: id(f.from),
|
|
543
|
+
to: Array.isArray(f.to) && f.to.every((x) => typeof x === "string") ? f.to.filter((x) => UUID.test(x)).map((x) => x.toUpperCase()) : [],
|
|
544
|
+
every: finite(f.every),
|
|
545
|
+
speed: finite(f.speed),
|
|
546
|
+
size: finite(f.size),
|
|
547
|
+
color: isObj(f.color) && ["r", "g", "b", "a"].every((k) => typeof f.color[k] === "number") ? rgba(f.color) ?? null : null,
|
|
548
|
+
when: str(f.when) ?? null,
|
|
549
|
+
spread: f.spread === true ? true : null,
|
|
550
|
+
volume: finite(f.volume),
|
|
551
|
+
overrides: Array.isArray(f.overrides) && f.overrides.length > 0 ? f.overrides.filter(isObj).map((o) => {
|
|
552
|
+
this.fields(o, "flowOverride");
|
|
553
|
+
return { when: str(o.when) ?? "", volume: finite(o.volume) };
|
|
554
|
+
}) : null
|
|
445
555
|
};
|
|
446
556
|
}
|
|
447
557
|
};
|
|
@@ -588,7 +698,7 @@ function compile(source2, runtime, options2 = {}) {
|
|
|
588
698
|
}
|
|
589
699
|
|
|
590
700
|
// src/index.ts
|
|
591
|
-
var VERSION = "0.
|
|
701
|
+
var VERSION = "0.8.0";
|
|
592
702
|
|
|
593
703
|
// src/cli.ts
|
|
594
704
|
var usage = `dottie-compile ${VERSION}: a Dottie drawing as one script that plays it on any page.
|