@phin-tech/dottie 0.1.0 → 0.2.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/README.md CHANGED
@@ -36,6 +36,25 @@ The element has `play()`, `pause()`, `nextScene()`, `prevScene()`, `currentTime`
36
36
  Without modules, `dist/dottie-player.min.js` is a classic script that registers the element and sets
37
37
  `window.Dottie`.
38
38
 
39
+ ## Compile a drawing into one script
40
+
41
+ ```sh
42
+ npx -p @phin-tech/dottie dottie-compile tour.dottie # writes tour.js
43
+ ```
44
+
45
+ `tour.js` holds the player and the drawing (about 75 KB plus the drawing; around 25 KB gzipped), so one file
46
+ shows it anywhere you can put a script tag:
47
+
48
+ ```html
49
+ <script src="tour.js"></script> <!-- a player right here -->
50
+ <script src="tour.js" data-target="#demo"></script> <!-- a player inside #demo -->
51
+ <script src="tour.js" data-clean data-no-loop></script> <!-- data-* set the player's attributes -->
52
+ ```
53
+
54
+ `dottie-compile` takes `--page`, `--at`, `--clean`, `--no-controls`, `--no-autoplay`, `--no-loop` and `--label`;
55
+ only the chosen page (and its pictures) goes in. The Dottie Mac app's command-line tool writes the same file:
56
+ `dottie render tour.dottie -f js`. From code: `import { compile } from "@phin-tech/dottie/compile"`.
57
+
39
58
  ## Use the library
40
59
 
41
60
  ```js
@@ -0,0 +1,391 @@
1
+ /*! dottie.js 0.2.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
2
+ var __defProp = Object.defineProperty;
3
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
+
6
+ // src/model.ts
7
+ var SUPPORTED_VERSION = 2;
8
+ var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image"];
9
+ var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold"];
10
+ var KNOWN_FIELDS = {
11
+ file: ["version", "pages", "images"],
12
+ page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline"],
13
+ layer: ["id", "name", "visible", "locked"],
14
+ rgba: ["r", "g", "b", "a"],
15
+ style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "gridSize", "opacity"],
16
+ element: [
17
+ "id",
18
+ "kind",
19
+ "x",
20
+ "y",
21
+ "width",
22
+ "height",
23
+ "style",
24
+ "seed",
25
+ "layerID",
26
+ "text",
27
+ "connector",
28
+ "groupID",
29
+ "points",
30
+ "path",
31
+ "subpaths",
32
+ "evenOdd",
33
+ "rotation",
34
+ "link",
35
+ "anchor",
36
+ "image",
37
+ "reveal"
38
+ ],
39
+ strokePoint: ["x", "y"],
40
+ subpath: ["closed", "coords"],
41
+ inlineImage: ["data", "format"],
42
+ endpoint: ["elementID", "port", "x", "y"],
43
+ connector: ["start", "end", "routing", "startHead", "endHead"],
44
+ timeline: ["fps", "duration", "boil", "settle", "poster", "tracks", "camera", "markers"],
45
+ track: ["element", "keys", "boil", "follow", "repeats"],
46
+ keyframe: ["t", "x", "y", "width", "height", "rotation", "strokeWidth", "stroke", "fill", "visible", "reveal", "opacity", "ease"],
47
+ follow: ["path", "start", "end", "ease", "reverse", "orient", "laps"],
48
+ repeat: ["start", "end", "count"],
49
+ cameraKey: ["t", "x", "y", "width", "height", "ease"],
50
+ marker: ["t", "name"]
51
+ };
52
+ var MAX_DURATION = 3600;
53
+ var black = { r: 0.12, g: 0.12, b: 0.13, a: 1 };
54
+ var clear = { r: 0, g: 0, b: 0, a: 0 };
55
+ var DottieFormatError = class extends Error {
56
+ };
57
+ var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
58
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
59
+ var str = (v) => typeof v === "string" ? v : void 0;
60
+ var bool = (v) => typeof v === "boolean" ? v : void 0;
61
+ var uuid = (v) => typeof v === "string" ? v.toUpperCase() : void 0;
62
+ function clampTime(t) {
63
+ if (!Number.isFinite(t)) return t === Infinity ? MAX_DURATION : 0;
64
+ return Math.min(Math.max(t, 0), MAX_DURATION);
65
+ }
66
+ function clampFPS(f) {
67
+ return Number.isFinite(f) ? Math.min(Math.max(f, 1), 60) : 12;
68
+ }
69
+ function rgba(v) {
70
+ if (!isObj(v)) return void 0;
71
+ const r = num(v.r), g = num(v.g), b = num(v.b);
72
+ if (r === void 0 || g === void 0 || b === void 0) return void 0;
73
+ return { r, g, b, a: num(v.a) ?? 1 };
74
+ }
75
+ var Reader = class {
76
+ constructor() {
77
+ __publicField(this, "warnings", []);
78
+ __publicField(this, "seen", /* @__PURE__ */ new Set());
79
+ }
80
+ warn(message) {
81
+ if (!this.seen.has(message)) {
82
+ this.seen.add(message);
83
+ this.warnings.push(message);
84
+ }
85
+ }
86
+ /** Reports fields this version doesn't read (they're ignored, as Dottie ignores them). */
87
+ fields(v, kind) {
88
+ const known = KNOWN_FIELDS[kind];
89
+ for (const k of Object.keys(v)) if (!known.includes(k)) this.warn(`Unknown field "${k}" on ${kind}, ignored`);
90
+ }
91
+ style(v) {
92
+ const s = isObj(v) ? v : {};
93
+ this.fields(s, "style");
94
+ const fillStyle = str(s.fillStyle) ?? "solid";
95
+ const dash = str(s.dash) ?? "solid";
96
+ if (!["solid", "hachure", "crossHatch", "grid"].includes(fillStyle)) this.warn(`Unknown fill style "${fillStyle}", drawn solid`);
97
+ if (!["solid", "dashed", "dotted"].includes(dash)) this.warn(`Unknown dash "${dash}", drawn solid`);
98
+ return {
99
+ stroke: rgba(s.stroke) ?? { ...black },
100
+ fill: rgba(s.fill) ?? { ...clear },
101
+ fillStyle: ["solid", "hachure", "crossHatch", "grid"].includes(fillStyle) ? fillStyle : "solid",
102
+ strokeWidth: num(s.strokeWidth) ?? 2,
103
+ dash: ["solid", "dashed", "dotted"].includes(dash) ? dash : "solid",
104
+ roughness: num(s.roughness) ?? 1,
105
+ bowing: num(s.bowing) ?? 1,
106
+ fontSize: num(s.fontSize) ?? 18,
107
+ gridSize: num(s.gridSize) ?? null,
108
+ opacity: num(s.opacity) ?? null
109
+ };
110
+ }
111
+ endpoint(v) {
112
+ const e = isObj(v) ? v : {};
113
+ const port = str(e.port);
114
+ return {
115
+ elementID: uuid(e.elementID) ?? null,
116
+ port: port && ["top", "right", "bottom", "left"].includes(port) ? port : null,
117
+ x: num(e.x) ?? 0,
118
+ y: num(e.y) ?? 0
119
+ };
120
+ }
121
+ connector(v) {
122
+ if (!isObj(v)) return null;
123
+ this.fields(v, "connector");
124
+ const head = (h, d) => {
125
+ const s = str(h);
126
+ return s && ["none", "arrow", "triangle", "dot", "bar"].includes(s) ? s : d;
127
+ };
128
+ const routing = str(v.routing);
129
+ return {
130
+ start: this.endpoint(v.start),
131
+ end: this.endpoint(v.end),
132
+ routing: routing && ["straight", "curved", "elbow"].includes(routing) ? routing : "straight",
133
+ startHead: head(v.startHead, "none"),
134
+ endHead: head(v.endHead, "arrow")
135
+ };
136
+ }
137
+ image(v, table) {
138
+ if (!isObj(v)) return null;
139
+ const ref = str(v.ref);
140
+ if (ref !== void 0) return table[ref] ?? { data: "", format: "png" };
141
+ const data = str(v.data);
142
+ return data === void 0 ? null : this.picture(data, str(v.format));
143
+ }
144
+ /** A picture's bytes, only if they really are base64: they go into an attribute of the drawn SVG. */
145
+ picture(data, format) {
146
+ if (!BASE64.test(data)) {
147
+ this.warn("Skipped a picture whose data isn't base64");
148
+ return { data: "", format: "png" };
149
+ }
150
+ return { data: data.replace(/\s+/g, ""), format: format === "jpeg" ? "jpeg" : "png" };
151
+ }
152
+ element(v, images) {
153
+ if (!isObj(v)) return null;
154
+ this.fields(v, "element");
155
+ const id = uuid(v.id), kind = str(v.kind), layerID = uuid(v.layerID);
156
+ if (!id || !kind || !layerID) {
157
+ this.warn("Skipped an element without an id, kind or layer");
158
+ return null;
159
+ }
160
+ if (!KINDS.includes(kind)) this.warn(`Unknown element kind "${kind}", drawn as a placeholder`);
161
+ const subpaths = Array.isArray(v.subpaths) ? v.subpaths.filter(isObj).map((s) => ({ closed: bool(s.closed) ?? false, coords: Array.isArray(s.coords) ? s.coords.filter((c) => typeof c === "number") : [] })) : null;
162
+ return {
163
+ id,
164
+ kind,
165
+ layerID,
166
+ x: num(v.x) ?? 0,
167
+ y: num(v.y) ?? 0,
168
+ width: num(v.width) ?? 0,
169
+ height: num(v.height) ?? 0,
170
+ style: this.style(v.style),
171
+ seed: (num(v.seed) ?? 1) >>> 0,
172
+ text: str(v.text) ?? null,
173
+ connector: this.connector(v.connector),
174
+ groupID: uuid(v.groupID) ?? null,
175
+ points: Array.isArray(v.points) ? v.points.filter(isObj).map((p) => ({ x: num(p.x) ?? 0, y: num(p.y) ?? 0 })) : null,
176
+ path: Array.isArray(v.path) ? v.path.filter((c) => typeof c === "number") : null,
177
+ subpaths,
178
+ evenOdd: bool(v.evenOdd) ?? null,
179
+ rotation: num(v.rotation) ?? null,
180
+ link: uuid(v.link) ?? null,
181
+ anchor: str(v.anchor) ?? null,
182
+ image: this.image(v.image, images),
183
+ reveal: num(v.reveal) ?? null
184
+ };
185
+ }
186
+ ease(v) {
187
+ const s = str(v);
188
+ if (s === void 0) return null;
189
+ if (!EASES.includes(s)) {
190
+ this.warn(`Unknown ease "${s}", played linear`);
191
+ return null;
192
+ }
193
+ return s;
194
+ }
195
+ keyframe(v) {
196
+ if (!isObj(v)) return null;
197
+ this.fields(v, "keyframe");
198
+ const t = num(v.t);
199
+ if (t === void 0) return null;
200
+ const k = { t };
201
+ for (const p of ["x", "y", "width", "height", "rotation", "strokeWidth", "reveal", "opacity"]) {
202
+ const n = num(v[p]);
203
+ if (n !== void 0) k[p] = n;
204
+ }
205
+ const stroke = rgba(v.stroke), fill = rgba(v.fill), visible = bool(v.visible), ease = this.ease(v.ease);
206
+ if (stroke) k.stroke = stroke;
207
+ if (fill) k.fill = fill;
208
+ if (visible !== void 0) k.visible = visible;
209
+ if (ease) k.ease = ease;
210
+ return k;
211
+ }
212
+ track(v) {
213
+ if (!isObj(v)) return null;
214
+ this.fields(v, "track");
215
+ const element = uuid(v.element);
216
+ if (!element) return null;
217
+ const keys = (Array.isArray(v.keys) ? v.keys : []).map((k) => this.keyframe(k)).filter((k) => k !== null && k.t >= 0 && k.t <= MAX_DURATION);
218
+ stableSortBy(keys, (k) => k.t);
219
+ const follow = Array.isArray(v.follow) ? v.follow.filter(isObj).flatMap((f) => {
220
+ const path = uuid(f.path), start = num(f.start), end = num(f.end);
221
+ if (!path || start === void 0 || end === void 0 || start < 0 || end > MAX_DURATION) return [];
222
+ return [{ path, start, end, ease: this.ease(f.ease), reverse: bool(f.reverse) ?? null, orient: bool(f.orient) ?? null, laps: num(f.laps) ?? null }];
223
+ }) : null;
224
+ const repeats = Array.isArray(v.repeats) ? v.repeats.filter(isObj).flatMap((r) => {
225
+ const start = num(r.start), end = num(r.end);
226
+ if (start === void 0 || end === void 0) return [];
227
+ const count = num(r.count);
228
+ const rep = { start, end, count: count === void 0 ? null : Math.trunc(count) };
229
+ return repeatIsValid(rep) ? [rep] : [];
230
+ }) : null;
231
+ return { element, keys, boil: bool(v.boil) ?? null, follow, repeats };
232
+ }
233
+ timeline(v) {
234
+ if (!isObj(v)) return null;
235
+ this.fields(v, "timeline");
236
+ const boil = num(v.boil);
237
+ const camera = Array.isArray(v.camera) ? v.camera.filter(isObj).flatMap((c) => {
238
+ const t = num(c.t), x = num(c.x), y = num(c.y), width = num(c.width), height = num(c.height);
239
+ if (t === void 0 || x === void 0 || y === void 0 || width === void 0 || height === void 0) return [];
240
+ if (!(width > 1 && height > 1 && t >= 0)) return [];
241
+ return [{ t, x, y, width, height, ease: this.ease(c.ease) }];
242
+ }) : null;
243
+ if (camera) stableSortBy(camera, (c) => c.t);
244
+ const markers = Array.isArray(v.markers) ? v.markers.filter(isObj).flatMap((m) => {
245
+ const t = num(m.t);
246
+ return t !== void 0 && t >= 0 ? [{ t, name: str(m.name) ?? "" }] : [];
247
+ }) : null;
248
+ if (markers) stableSortBy(markers, (m) => m.t);
249
+ const poster = num(v.poster);
250
+ return {
251
+ fps: clampFPS(num(v.fps) ?? 12),
252
+ duration: clampTime(num(v.duration) ?? 0),
253
+ boil: boil !== void 0 && Number.isInteger(boil) && boil > 0 ? Math.min(boil, 1e4) : null,
254
+ settle: bool(v.settle) ?? null,
255
+ poster: poster === void 0 ? null : clampTime(poster),
256
+ tracks: (Array.isArray(v.tracks) ? v.tracks : []).map((t) => this.track(t)).filter((t) => t !== null),
257
+ camera,
258
+ markers
259
+ };
260
+ }
261
+ page(v, images, n) {
262
+ if (!isObj(v)) throw new DottieFormatError(`Page ${n} is not an object`);
263
+ this.fields(v, "page");
264
+ const layers = (Array.isArray(v.layers) ? v.layers : []).filter(isObj).flatMap((l) => {
265
+ const id = uuid(l.id);
266
+ return id ? [{ id, name: str(l.name) ?? "", visible: bool(l.visible) ?? true, locked: bool(l.locked) ?? false }] : [];
267
+ });
268
+ if (layers.length === 0) {
269
+ this.warn(`Page ${n} has no layers`);
270
+ layers.push({ id: "LAYER", name: "Layer 1", visible: true, locked: false });
271
+ }
272
+ return {
273
+ id: uuid(v.id) ?? `PAGE-${n}`,
274
+ name: str(v.name) ?? `Page ${n}`,
275
+ layers,
276
+ elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
277
+ theme: str(v.theme) ?? null,
278
+ timeline: this.timeline(v.timeline)
279
+ };
280
+ }
281
+ };
282
+ var BASE64 = /^[A-Za-z0-9+/=\s]*$/;
283
+ function repeatIsValid(r) {
284
+ return Number.isFinite(r.start) && Number.isFinite(r.end) && r.start >= 0 && r.end - r.start > 1e-3 && r.end <= MAX_DURATION && (r.count === null || r.count >= 1 && r.count <= 1e4);
285
+ }
286
+ function stableSortBy(a, key) {
287
+ const indexed = a.map((v, i) => [v, i]);
288
+ indexed.sort((p, q) => key(p[0]) - key(q[0]) || p[1] - q[1]);
289
+ for (let i = 0; i < a.length; i++) a[i] = indexed[i][0];
290
+ }
291
+ function parseDottie(source) {
292
+ let json = source;
293
+ if (typeof source === "string") {
294
+ try {
295
+ json = JSON.parse(source);
296
+ } catch (e) {
297
+ throw new DottieFormatError(`Not JSON: ${e.message}`);
298
+ }
299
+ }
300
+ if (!isObj(json)) throw new DottieFormatError("Not a Dottie drawing");
301
+ const version = num(json.version);
302
+ if (version === void 0) throw new DottieFormatError("Not a Dottie drawing (no version)");
303
+ if (!Array.isArray(json.pages) || json.pages.length === 0) throw new DottieFormatError("A drawing needs at least one page");
304
+ const reader = new Reader();
305
+ reader.fields(json, "file");
306
+ if (version > SUPPORTED_VERSION) reader.warn(`File version ${version} is newer than this player knows (${SUPPORTED_VERSION}); some things may not show`);
307
+ const images = {};
308
+ if (isObj(json.images)) {
309
+ for (const [k, v] of Object.entries(json.images)) {
310
+ if (isObj(v) && typeof v.data === "string") images[k] = reader.picture(v.data, str(v.format));
311
+ }
312
+ }
313
+ const pages = json.pages.map((p, i) => reader.page(p, images, i + 1));
314
+ if (new Set(pages.map((p) => p.id)).size !== pages.length) throw new DottieFormatError("Two pages share an id");
315
+ return { version, pages, warnings: reader.warnings };
316
+ }
317
+
318
+ // src/player.ts
319
+ function pageIndex(file, spec) {
320
+ const s = (spec ?? "").trim();
321
+ if (!s) return 0;
322
+ const byName = file.pages.findIndex((p) => p.name.toLowerCase() === s.toLowerCase());
323
+ if (byName >= 0) return byName;
324
+ const n = Number(s);
325
+ return Number.isInteger(n) && n >= 1 && n <= file.pages.length ? n - 1 : 0;
326
+ }
327
+
328
+ // src/compile.ts
329
+ var LOADER = `;(function () {
330
+ var data = DOTTIE_DATA, options = DOTTIE_OPTIONS;
331
+ var script = document.currentScript;
332
+ function attr(el, name, value) { el.setAttribute(name.replace(/[A-Z]/g, function (c) { return "-" + c.toLowerCase(); }), value); }
333
+ function mount() {
334
+ var player = document.createElement("dottie-player"), k;
335
+ for (k in options) attr(player, k, options[k]);
336
+ if (script) for (k in script.dataset) if (k !== "target") attr(player, k, script.dataset[k]);
337
+ var target = script && script.dataset.target ? document.querySelector(script.dataset.target) : null;
338
+ if (target) target.appendChild(player);
339
+ else if (script && script.parentNode && script.parentNode !== document.head) script.parentNode.insertBefore(player, script);
340
+ else document.body.appendChild(player);
341
+ player.setFile(window.Dottie.parseDottie(data));
342
+ }
343
+ if (document.readyState === "loading" && (!script || script.parentNode === document.head || script.dataset.target)) {
344
+ document.addEventListener("DOMContentLoaded", mount);
345
+ } else {
346
+ mount();
347
+ }
348
+ })();
349
+ `;
350
+ function scriptJSON(value) {
351
+ return JSON.stringify(value).replace(/<\//g, "<\\/").replace(/<!--/g, "<\\!--").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
352
+ }
353
+ function playerAttributes(options) {
354
+ const out = {};
355
+ if (options.at !== void 0) out.at = String(options.at);
356
+ if (options.clean) out.clean = "";
357
+ if (options.noControls) out["no-controls"] = "";
358
+ if (options.noAutoplay) out["no-autoplay"] = "";
359
+ if (options.noLoop) out["no-loop"] = "";
360
+ if (options.label !== void 0) out.label = options.label;
361
+ return out;
362
+ }
363
+ function compile(source, runtime, options = {}) {
364
+ let raw = source;
365
+ if (typeof source === "string") {
366
+ try {
367
+ raw = JSON.parse(source);
368
+ } catch (e) {
369
+ throw new DottieFormatError(`Not JSON: ${e.message}`);
370
+ }
371
+ }
372
+ const file = parseDottie(raw);
373
+ const json = raw;
374
+ const i = pageIndex(file, options.page === void 0 ? null : String(options.page));
375
+ const page = json.pages[i];
376
+ const refs = /* @__PURE__ */ new Set();
377
+ JSON.stringify(page, (k, v) => {
378
+ if (k === "ref" && typeof v === "string") refs.add(v);
379
+ return v;
380
+ });
381
+ const images = Object.fromEntries(Object.entries(json.images ?? {}).filter(([k]) => refs.has(k)));
382
+ const data = { version: json.version, pages: [page] };
383
+ if (Object.keys(images).length) data.images = images;
384
+ return runtime.trimEnd() + "\n" + LOADER.replace("DOTTIE_DATA", () => scriptJSON(data)).replace("DOTTIE_OPTIONS", () => scriptJSON(playerAttributes(options)));
385
+ }
386
+ export {
387
+ LOADER,
388
+ compile,
389
+ playerAttributes,
390
+ scriptJSON
391
+ };
@@ -0,0 +1,493 @@
1
+ #!/usr/bin/env node
2
+ /*! dottie.js 0.2.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
3
+ var __defProp = Object.defineProperty;
4
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
5
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
6
+
7
+ // src/cli.ts
8
+ import { readFileSync, writeFileSync } from "node:fs";
9
+ import { basename } from "node:path";
10
+
11
+ // src/model.ts
12
+ var SUPPORTED_VERSION = 2;
13
+ var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image"];
14
+ var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold"];
15
+ var KNOWN_FIELDS = {
16
+ file: ["version", "pages", "images"],
17
+ page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline"],
18
+ layer: ["id", "name", "visible", "locked"],
19
+ rgba: ["r", "g", "b", "a"],
20
+ style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "gridSize", "opacity"],
21
+ element: [
22
+ "id",
23
+ "kind",
24
+ "x",
25
+ "y",
26
+ "width",
27
+ "height",
28
+ "style",
29
+ "seed",
30
+ "layerID",
31
+ "text",
32
+ "connector",
33
+ "groupID",
34
+ "points",
35
+ "path",
36
+ "subpaths",
37
+ "evenOdd",
38
+ "rotation",
39
+ "link",
40
+ "anchor",
41
+ "image",
42
+ "reveal"
43
+ ],
44
+ strokePoint: ["x", "y"],
45
+ subpath: ["closed", "coords"],
46
+ inlineImage: ["data", "format"],
47
+ endpoint: ["elementID", "port", "x", "y"],
48
+ connector: ["start", "end", "routing", "startHead", "endHead"],
49
+ timeline: ["fps", "duration", "boil", "settle", "poster", "tracks", "camera", "markers"],
50
+ track: ["element", "keys", "boil", "follow", "repeats"],
51
+ keyframe: ["t", "x", "y", "width", "height", "rotation", "strokeWidth", "stroke", "fill", "visible", "reveal", "opacity", "ease"],
52
+ follow: ["path", "start", "end", "ease", "reverse", "orient", "laps"],
53
+ repeat: ["start", "end", "count"],
54
+ cameraKey: ["t", "x", "y", "width", "height", "ease"],
55
+ marker: ["t", "name"]
56
+ };
57
+ var MAX_DURATION = 3600;
58
+ var black = { r: 0.12, g: 0.12, b: 0.13, a: 1 };
59
+ var clear = { r: 0, g: 0, b: 0, a: 0 };
60
+ var DottieFormatError = class extends Error {
61
+ };
62
+ var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
63
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
64
+ var str = (v) => typeof v === "string" ? v : void 0;
65
+ var bool = (v) => typeof v === "boolean" ? v : void 0;
66
+ var uuid = (v) => typeof v === "string" ? v.toUpperCase() : void 0;
67
+ function clampTime(t) {
68
+ if (!Number.isFinite(t)) return t === Infinity ? MAX_DURATION : 0;
69
+ return Math.min(Math.max(t, 0), MAX_DURATION);
70
+ }
71
+ function clampFPS(f) {
72
+ return Number.isFinite(f) ? Math.min(Math.max(f, 1), 60) : 12;
73
+ }
74
+ function rgba(v) {
75
+ if (!isObj(v)) return void 0;
76
+ const r = num(v.r), g = num(v.g), b = num(v.b);
77
+ if (r === void 0 || g === void 0 || b === void 0) return void 0;
78
+ return { r, g, b, a: num(v.a) ?? 1 };
79
+ }
80
+ var Reader = class {
81
+ constructor() {
82
+ __publicField(this, "warnings", []);
83
+ __publicField(this, "seen", /* @__PURE__ */ new Set());
84
+ }
85
+ warn(message) {
86
+ if (!this.seen.has(message)) {
87
+ this.seen.add(message);
88
+ this.warnings.push(message);
89
+ }
90
+ }
91
+ /** Reports fields this version doesn't read (they're ignored, as Dottie ignores them). */
92
+ fields(v, kind) {
93
+ const known = KNOWN_FIELDS[kind];
94
+ for (const k of Object.keys(v)) if (!known.includes(k)) this.warn(`Unknown field "${k}" on ${kind}, ignored`);
95
+ }
96
+ style(v) {
97
+ const s = isObj(v) ? v : {};
98
+ this.fields(s, "style");
99
+ const fillStyle = str(s.fillStyle) ?? "solid";
100
+ const dash = str(s.dash) ?? "solid";
101
+ if (!["solid", "hachure", "crossHatch", "grid"].includes(fillStyle)) this.warn(`Unknown fill style "${fillStyle}", drawn solid`);
102
+ if (!["solid", "dashed", "dotted"].includes(dash)) this.warn(`Unknown dash "${dash}", drawn solid`);
103
+ return {
104
+ stroke: rgba(s.stroke) ?? { ...black },
105
+ fill: rgba(s.fill) ?? { ...clear },
106
+ fillStyle: ["solid", "hachure", "crossHatch", "grid"].includes(fillStyle) ? fillStyle : "solid",
107
+ strokeWidth: num(s.strokeWidth) ?? 2,
108
+ dash: ["solid", "dashed", "dotted"].includes(dash) ? dash : "solid",
109
+ roughness: num(s.roughness) ?? 1,
110
+ bowing: num(s.bowing) ?? 1,
111
+ fontSize: num(s.fontSize) ?? 18,
112
+ gridSize: num(s.gridSize) ?? null,
113
+ opacity: num(s.opacity) ?? null
114
+ };
115
+ }
116
+ endpoint(v) {
117
+ const e = isObj(v) ? v : {};
118
+ const port = str(e.port);
119
+ return {
120
+ elementID: uuid(e.elementID) ?? null,
121
+ port: port && ["top", "right", "bottom", "left"].includes(port) ? port : null,
122
+ x: num(e.x) ?? 0,
123
+ y: num(e.y) ?? 0
124
+ };
125
+ }
126
+ connector(v) {
127
+ if (!isObj(v)) return null;
128
+ this.fields(v, "connector");
129
+ const head = (h, d) => {
130
+ const s = str(h);
131
+ return s && ["none", "arrow", "triangle", "dot", "bar"].includes(s) ? s : d;
132
+ };
133
+ const routing = str(v.routing);
134
+ return {
135
+ start: this.endpoint(v.start),
136
+ end: this.endpoint(v.end),
137
+ routing: routing && ["straight", "curved", "elbow"].includes(routing) ? routing : "straight",
138
+ startHead: head(v.startHead, "none"),
139
+ endHead: head(v.endHead, "arrow")
140
+ };
141
+ }
142
+ image(v, table) {
143
+ if (!isObj(v)) return null;
144
+ const ref = str(v.ref);
145
+ if (ref !== void 0) return table[ref] ?? { data: "", format: "png" };
146
+ const data = str(v.data);
147
+ return data === void 0 ? null : this.picture(data, str(v.format));
148
+ }
149
+ /** A picture's bytes, only if they really are base64: they go into an attribute of the drawn SVG. */
150
+ picture(data, format) {
151
+ if (!BASE64.test(data)) {
152
+ this.warn("Skipped a picture whose data isn't base64");
153
+ return { data: "", format: "png" };
154
+ }
155
+ return { data: data.replace(/\s+/g, ""), format: format === "jpeg" ? "jpeg" : "png" };
156
+ }
157
+ element(v, images) {
158
+ if (!isObj(v)) return null;
159
+ this.fields(v, "element");
160
+ const id = uuid(v.id), kind = str(v.kind), layerID = uuid(v.layerID);
161
+ if (!id || !kind || !layerID) {
162
+ this.warn("Skipped an element without an id, kind or layer");
163
+ return null;
164
+ }
165
+ if (!KINDS.includes(kind)) this.warn(`Unknown element kind "${kind}", drawn as a placeholder`);
166
+ const subpaths = Array.isArray(v.subpaths) ? v.subpaths.filter(isObj).map((s) => ({ closed: bool(s.closed) ?? false, coords: Array.isArray(s.coords) ? s.coords.filter((c) => typeof c === "number") : [] })) : null;
167
+ return {
168
+ id,
169
+ kind,
170
+ layerID,
171
+ x: num(v.x) ?? 0,
172
+ y: num(v.y) ?? 0,
173
+ width: num(v.width) ?? 0,
174
+ height: num(v.height) ?? 0,
175
+ style: this.style(v.style),
176
+ seed: (num(v.seed) ?? 1) >>> 0,
177
+ text: str(v.text) ?? null,
178
+ connector: this.connector(v.connector),
179
+ groupID: uuid(v.groupID) ?? null,
180
+ points: Array.isArray(v.points) ? v.points.filter(isObj).map((p) => ({ x: num(p.x) ?? 0, y: num(p.y) ?? 0 })) : null,
181
+ path: Array.isArray(v.path) ? v.path.filter((c) => typeof c === "number") : null,
182
+ subpaths,
183
+ evenOdd: bool(v.evenOdd) ?? null,
184
+ rotation: num(v.rotation) ?? null,
185
+ link: uuid(v.link) ?? null,
186
+ anchor: str(v.anchor) ?? null,
187
+ image: this.image(v.image, images),
188
+ reveal: num(v.reveal) ?? null
189
+ };
190
+ }
191
+ ease(v) {
192
+ const s = str(v);
193
+ if (s === void 0) return null;
194
+ if (!EASES.includes(s)) {
195
+ this.warn(`Unknown ease "${s}", played linear`);
196
+ return null;
197
+ }
198
+ return s;
199
+ }
200
+ keyframe(v) {
201
+ if (!isObj(v)) return null;
202
+ this.fields(v, "keyframe");
203
+ const t = num(v.t);
204
+ if (t === void 0) return null;
205
+ const k = { t };
206
+ for (const p of ["x", "y", "width", "height", "rotation", "strokeWidth", "reveal", "opacity"]) {
207
+ const n = num(v[p]);
208
+ if (n !== void 0) k[p] = n;
209
+ }
210
+ const stroke = rgba(v.stroke), fill = rgba(v.fill), visible = bool(v.visible), ease = this.ease(v.ease);
211
+ if (stroke) k.stroke = stroke;
212
+ if (fill) k.fill = fill;
213
+ if (visible !== void 0) k.visible = visible;
214
+ if (ease) k.ease = ease;
215
+ return k;
216
+ }
217
+ track(v) {
218
+ if (!isObj(v)) return null;
219
+ this.fields(v, "track");
220
+ const element = uuid(v.element);
221
+ if (!element) return null;
222
+ const keys = (Array.isArray(v.keys) ? v.keys : []).map((k) => this.keyframe(k)).filter((k) => k !== null && k.t >= 0 && k.t <= MAX_DURATION);
223
+ stableSortBy(keys, (k) => k.t);
224
+ const follow = Array.isArray(v.follow) ? v.follow.filter(isObj).flatMap((f) => {
225
+ const path = uuid(f.path), start = num(f.start), end = num(f.end);
226
+ if (!path || start === void 0 || end === void 0 || start < 0 || end > MAX_DURATION) return [];
227
+ return [{ path, start, end, ease: this.ease(f.ease), reverse: bool(f.reverse) ?? null, orient: bool(f.orient) ?? null, laps: num(f.laps) ?? null }];
228
+ }) : null;
229
+ const repeats = Array.isArray(v.repeats) ? v.repeats.filter(isObj).flatMap((r) => {
230
+ const start = num(r.start), end = num(r.end);
231
+ if (start === void 0 || end === void 0) return [];
232
+ const count = num(r.count);
233
+ const rep = { start, end, count: count === void 0 ? null : Math.trunc(count) };
234
+ return repeatIsValid(rep) ? [rep] : [];
235
+ }) : null;
236
+ return { element, keys, boil: bool(v.boil) ?? null, follow, repeats };
237
+ }
238
+ timeline(v) {
239
+ if (!isObj(v)) return null;
240
+ this.fields(v, "timeline");
241
+ const boil = num(v.boil);
242
+ const camera = Array.isArray(v.camera) ? v.camera.filter(isObj).flatMap((c) => {
243
+ const t = num(c.t), x = num(c.x), y = num(c.y), width = num(c.width), height = num(c.height);
244
+ if (t === void 0 || x === void 0 || y === void 0 || width === void 0 || height === void 0) return [];
245
+ if (!(width > 1 && height > 1 && t >= 0)) return [];
246
+ return [{ t, x, y, width, height, ease: this.ease(c.ease) }];
247
+ }) : null;
248
+ if (camera) stableSortBy(camera, (c) => c.t);
249
+ const markers = Array.isArray(v.markers) ? v.markers.filter(isObj).flatMap((m) => {
250
+ const t = num(m.t);
251
+ return t !== void 0 && t >= 0 ? [{ t, name: str(m.name) ?? "" }] : [];
252
+ }) : null;
253
+ if (markers) stableSortBy(markers, (m) => m.t);
254
+ const poster = num(v.poster);
255
+ return {
256
+ fps: clampFPS(num(v.fps) ?? 12),
257
+ duration: clampTime(num(v.duration) ?? 0),
258
+ boil: boil !== void 0 && Number.isInteger(boil) && boil > 0 ? Math.min(boil, 1e4) : null,
259
+ settle: bool(v.settle) ?? null,
260
+ poster: poster === void 0 ? null : clampTime(poster),
261
+ tracks: (Array.isArray(v.tracks) ? v.tracks : []).map((t) => this.track(t)).filter((t) => t !== null),
262
+ camera,
263
+ markers
264
+ };
265
+ }
266
+ page(v, images, n) {
267
+ if (!isObj(v)) throw new DottieFormatError(`Page ${n} is not an object`);
268
+ this.fields(v, "page");
269
+ const layers = (Array.isArray(v.layers) ? v.layers : []).filter(isObj).flatMap((l) => {
270
+ const id = uuid(l.id);
271
+ return id ? [{ id, name: str(l.name) ?? "", visible: bool(l.visible) ?? true, locked: bool(l.locked) ?? false }] : [];
272
+ });
273
+ if (layers.length === 0) {
274
+ this.warn(`Page ${n} has no layers`);
275
+ layers.push({ id: "LAYER", name: "Layer 1", visible: true, locked: false });
276
+ }
277
+ return {
278
+ id: uuid(v.id) ?? `PAGE-${n}`,
279
+ name: str(v.name) ?? `Page ${n}`,
280
+ layers,
281
+ elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
282
+ theme: str(v.theme) ?? null,
283
+ timeline: this.timeline(v.timeline)
284
+ };
285
+ }
286
+ };
287
+ var BASE64 = /^[A-Za-z0-9+/=\s]*$/;
288
+ function repeatIsValid(r) {
289
+ return Number.isFinite(r.start) && Number.isFinite(r.end) && r.start >= 0 && r.end - r.start > 1e-3 && r.end <= MAX_DURATION && (r.count === null || r.count >= 1 && r.count <= 1e4);
290
+ }
291
+ function stableSortBy(a, key) {
292
+ const indexed = a.map((v, i) => [v, i]);
293
+ indexed.sort((p, q) => key(p[0]) - key(q[0]) || p[1] - q[1]);
294
+ for (let i = 0; i < a.length; i++) a[i] = indexed[i][0];
295
+ }
296
+ function parseDottie(source2) {
297
+ let json = source2;
298
+ if (typeof source2 === "string") {
299
+ try {
300
+ json = JSON.parse(source2);
301
+ } catch (e) {
302
+ throw new DottieFormatError(`Not JSON: ${e.message}`);
303
+ }
304
+ }
305
+ if (!isObj(json)) throw new DottieFormatError("Not a Dottie drawing");
306
+ const version = num(json.version);
307
+ if (version === void 0) throw new DottieFormatError("Not a Dottie drawing (no version)");
308
+ if (!Array.isArray(json.pages) || json.pages.length === 0) throw new DottieFormatError("A drawing needs at least one page");
309
+ const reader = new Reader();
310
+ reader.fields(json, "file");
311
+ if (version > SUPPORTED_VERSION) reader.warn(`File version ${version} is newer than this player knows (${SUPPORTED_VERSION}); some things may not show`);
312
+ const images = {};
313
+ if (isObj(json.images)) {
314
+ for (const [k, v] of Object.entries(json.images)) {
315
+ if (isObj(v) && typeof v.data === "string") images[k] = reader.picture(v.data, str(v.format));
316
+ }
317
+ }
318
+ const pages = json.pages.map((p, i) => reader.page(p, images, i + 1));
319
+ if (new Set(pages.map((p) => p.id)).size !== pages.length) throw new DottieFormatError("Two pages share an id");
320
+ return { version, pages, warnings: reader.warnings };
321
+ }
322
+
323
+ // src/player.ts
324
+ function pageIndex(file, spec) {
325
+ const s = (spec ?? "").trim();
326
+ if (!s) return 0;
327
+ const byName = file.pages.findIndex((p) => p.name.toLowerCase() === s.toLowerCase());
328
+ if (byName >= 0) return byName;
329
+ const n = Number(s);
330
+ return Number.isInteger(n) && n >= 1 && n <= file.pages.length ? n - 1 : 0;
331
+ }
332
+
333
+ // src/compile.ts
334
+ var LOADER = `;(function () {
335
+ var data = DOTTIE_DATA, options = DOTTIE_OPTIONS;
336
+ var script = document.currentScript;
337
+ function attr(el, name, value) { el.setAttribute(name.replace(/[A-Z]/g, function (c) { return "-" + c.toLowerCase(); }), value); }
338
+ function mount() {
339
+ var player = document.createElement("dottie-player"), k;
340
+ for (k in options) attr(player, k, options[k]);
341
+ if (script) for (k in script.dataset) if (k !== "target") attr(player, k, script.dataset[k]);
342
+ var target = script && script.dataset.target ? document.querySelector(script.dataset.target) : null;
343
+ if (target) target.appendChild(player);
344
+ else if (script && script.parentNode && script.parentNode !== document.head) script.parentNode.insertBefore(player, script);
345
+ else document.body.appendChild(player);
346
+ player.setFile(window.Dottie.parseDottie(data));
347
+ }
348
+ if (document.readyState === "loading" && (!script || script.parentNode === document.head || script.dataset.target)) {
349
+ document.addEventListener("DOMContentLoaded", mount);
350
+ } else {
351
+ mount();
352
+ }
353
+ })();
354
+ `;
355
+ function scriptJSON(value) {
356
+ return JSON.stringify(value).replace(/<\//g, "<\\/").replace(/<!--/g, "<\\!--").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
357
+ }
358
+ function playerAttributes(options2) {
359
+ const out = {};
360
+ if (options2.at !== void 0) out.at = String(options2.at);
361
+ if (options2.clean) out.clean = "";
362
+ if (options2.noControls) out["no-controls"] = "";
363
+ if (options2.noAutoplay) out["no-autoplay"] = "";
364
+ if (options2.noLoop) out["no-loop"] = "";
365
+ if (options2.label !== void 0) out.label = options2.label;
366
+ return out;
367
+ }
368
+ function compile(source2, runtime, options2 = {}) {
369
+ let raw = source2;
370
+ if (typeof source2 === "string") {
371
+ try {
372
+ raw = JSON.parse(source2);
373
+ } catch (e) {
374
+ throw new DottieFormatError(`Not JSON: ${e.message}`);
375
+ }
376
+ }
377
+ const file = parseDottie(raw);
378
+ const json = raw;
379
+ const i = pageIndex(file, options2.page === void 0 ? null : String(options2.page));
380
+ const page = json.pages[i];
381
+ const refs = /* @__PURE__ */ new Set();
382
+ JSON.stringify(page, (k, v) => {
383
+ if (k === "ref" && typeof v === "string") refs.add(v);
384
+ return v;
385
+ });
386
+ const images = Object.fromEntries(Object.entries(json.images ?? {}).filter(([k]) => refs.has(k)));
387
+ const data = { version: json.version, pages: [page] };
388
+ if (Object.keys(images).length) data.images = images;
389
+ return runtime.trimEnd() + "\n" + LOADER.replace("DOTTIE_DATA", () => scriptJSON(data)).replace("DOTTIE_OPTIONS", () => scriptJSON(playerAttributes(options2)));
390
+ }
391
+
392
+ // src/index.ts
393
+ var VERSION = "0.2.0";
394
+
395
+ // src/cli.ts
396
+ var usage = `dottie-compile ${VERSION}: a Dottie drawing as one script that plays it on any page.
397
+
398
+ USAGE
399
+ dottie-compile <file.dottie> [-o <out.js>] [options]
400
+
401
+ Then: <script src="drawing.js"></script> shows a player where the tag is. Add data-target="#id" to put it
402
+ inside another element, and data-* attributes (data-clean, data-no-loop, ...) to set the player's attributes.
403
+
404
+ OPTIONS
405
+ -o, --output <path> Where to write it ("-" for stdout); default drawing.js next to the input
406
+ -p, --page <page> Page name or 1-based number; only that page is included (default the first)
407
+ --at <seconds> Show that moment as a still
408
+ --clean Draw precisely instead of sketchy
409
+ --no-controls, --no-autoplay, --no-loop
410
+ --label <text> The player's accessible name
411
+ `;
412
+ function fail(message, code = 2) {
413
+ process.stderr.write(`dottie-compile: ${message}
414
+ `);
415
+ process.exit(code);
416
+ }
417
+ var args = process.argv.slice(2);
418
+ var options = {};
419
+ var input;
420
+ var output;
421
+ for (let i = 0; i < args.length; i++) {
422
+ const a = args[i];
423
+ const value = () => ++i < args.length ? args[i] : fail(`${a} needs a value`);
424
+ switch (a) {
425
+ case "-h":
426
+ case "--help":
427
+ process.stdout.write(usage);
428
+ process.exit(0);
429
+ case "-v":
430
+ case "--version":
431
+ process.stdout.write(`dottie-compile ${VERSION}
432
+ `);
433
+ process.exit(0);
434
+ case "-o":
435
+ case "--output":
436
+ output = value();
437
+ break;
438
+ case "-p":
439
+ case "--page":
440
+ options.page = value();
441
+ break;
442
+ case "--at": {
443
+ const t = Number(value());
444
+ if (!Number.isFinite(t) || t < 0) fail("--at must be a time in seconds");
445
+ options.at = t;
446
+ break;
447
+ }
448
+ case "--clean":
449
+ options.clean = true;
450
+ break;
451
+ case "--no-controls":
452
+ options.noControls = true;
453
+ break;
454
+ case "--no-autoplay":
455
+ options.noAutoplay = true;
456
+ break;
457
+ case "--no-loop":
458
+ options.noLoop = true;
459
+ break;
460
+ case "--label":
461
+ options.label = value();
462
+ break;
463
+ default:
464
+ if (a.startsWith("-") || input) fail(`unknown option ${a}
465
+
466
+ ${usage}`);
467
+ input = a;
468
+ }
469
+ }
470
+ if (!input) fail(`needs a .dottie file
471
+
472
+ ${usage}`);
473
+ var source;
474
+ try {
475
+ source = readFileSync(input, "utf8");
476
+ } catch {
477
+ fail(`${input}: cannot read file`, 1);
478
+ }
479
+ try {
480
+ for (const w of parseDottie(source).warnings) process.stderr.write(`dottie-compile: ${input}: ${w}
481
+ `);
482
+ const runtime = readFileSync(new URL("./dottie-player.min.js", import.meta.url), "utf8");
483
+ const js = compile(source, runtime, options);
484
+ if (output === "-") process.stdout.write(js);
485
+ else {
486
+ const target = output ?? input.replace(/\.dottie$/i, "") + ".js";
487
+ writeFileSync(target, js);
488
+ process.stdout.write(`${target} (${Math.round(js.length / 1024)} KB): <script src="${basename(target)}"></script>
489
+ `);
490
+ }
491
+ } catch (e) {
492
+ fail(`${input}: ${e.message}`, 1);
493
+ }
@@ -0,0 +1,20 @@
1
+ ;(function () {
2
+ var data = DOTTIE_DATA, options = DOTTIE_OPTIONS;
3
+ var script = document.currentScript;
4
+ function attr(el, name, value) { el.setAttribute(name.replace(/[A-Z]/g, function (c) { return "-" + c.toLowerCase(); }), value); }
5
+ function mount() {
6
+ var player = document.createElement("dottie-player"), k;
7
+ for (k in options) attr(player, k, options[k]);
8
+ if (script) for (k in script.dataset) if (k !== "target") attr(player, k, script.dataset[k]);
9
+ var target = script && script.dataset.target ? document.querySelector(script.dataset.target) : null;
10
+ if (target) target.appendChild(player);
11
+ else if (script && script.parentNode && script.parentNode !== document.head) script.parentNode.insertBefore(player, script);
12
+ else document.body.appendChild(player);
13
+ player.setFile(window.Dottie.parseDottie(data));
14
+ }
15
+ if (document.readyState === "loading" && (!script || script.parentNode === document.head || script.dataset.target)) {
16
+ document.addEventListener("DOMContentLoaded", mount);
17
+ } else {
18
+ mount();
19
+ }
20
+ })();
@@ -1,4 +1,4 @@
1
- /*! dottie.js 0.1.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
1
+ /*! dottie.js 0.2.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) => {
@@ -3022,7 +3022,7 @@ function render(source, options = {}) {
3022
3022
  function isFile(v) {
3023
3023
  return typeof v === "object" && v !== null && Array.isArray(v.pages) && Array.isArray(v.warnings);
3024
3024
  }
3025
- var VERSION = "0.1.0";
3025
+ var VERSION = "0.2.0";
3026
3026
 
3027
3027
  // src/player-entry.ts
3028
3028
  definePlayer();
@@ -1,4 +1,4 @@
1
- /*! dottie.js 0.1.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
1
+ /*! dottie.js 0.2.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
2
2
  "use strict";(()=>{var Oe=Object.defineProperty;var Kn=(t,e,n)=>e in t?Oe(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var Xn=(t,e)=>{for(var n in e)Oe(t,n,{get:e[n],enumerable:!0})};var b=(t,e,n)=>Kn(t,typeof e!="symbol"?e+"":e,n);var Ge={};Xn(Ge,{BOIL_DRAWINGS:()=>le,DottieFormatError:()=>q,DottiePlayer:()=>wt,KINDS:()=>Fe,KNOWN_FIELDS:()=>Ne,MAX_DURATION:()=>ht,Playback:()=>It,SUPPORTED_VERSION:()=>Zi,VERSION:()=>Xi,black:()=>ot,bounds:()=>rt,cameraView:()=>bt,clampFPS:()=>ft,clampTime:()=>dt,clear:()=>He,cloneElement:()=>_,definePlayer:()=>ne,elementIndex:()=>Jn,frameTimes:()=>Jt,inkOnDark:()=>je,inspect:()=>Un,inspectFrame:()=>ze,inspectPage:()=>Ce,isAutomaticInk:()=>ae,luminance:()=>pt,orderedElements:()=>At,pageIndex:()=>Lt,parseDottie:()=>Rt,plan:()=>kt,posterTime:()=>it,render:()=>Ui,repeatIsValid:()=>zt,resolved:()=>W,sameColor:()=>mt,sampleTimes:()=>Ye,sceneAt:()=>Hn,stableSortBy:()=>Yt,still:()=>Nn,svg:()=>ee,svgParts:()=>$t,themeNamed:()=>ut,themes:()=>Ie,transientElements:()=>jn,withRoughness:()=>Vt});var Zi=2,Fe=["rectangle","ellipse","diamond","text","connector","freehand","path","note","image"],Qn=["linear","easeIn","easeOut","easeInOut","hold"],Ne={file:["version","pages","images"],page:["id","name","layers","elements","theme","guidesX","guidesY","guidesLocked","timeline"],layer:["id","name","visible","locked"],rgba:["r","g","b","a"],style:["stroke","fill","fillStyle","strokeWidth","dash","roughness","bowing","fontSize","gridSize","opacity"],element:["id","kind","x","y","width","height","style","seed","layerID","text","connector","groupID","points","path","subpaths","evenOdd","rotation","link","anchor","image","reveal"],strokePoint:["x","y"],subpath:["closed","coords"],inlineImage:["data","format"],endpoint:["elementID","port","x","y"],connector:["start","end","routing","startHead","endHead"],timeline:["fps","duration","boil","settle","poster","tracks","camera","markers"],track:["element","keys","boil","follow","repeats"],keyframe:["t","x","y","width","height","rotation","strokeWidth","stroke","fill","visible","reveal","opacity","ease"],follow:["path","start","end","ease","reverse","orient","laps"],repeat:["start","end","count"],cameraKey:["t","x","y","width","height","ease"],marker:["t","name"]},ht=3600,le=3,ot={r:.12,g:.12,b:.13,a:1},He={r:0,g:0,b:0,a:0},je={r:.93,g:.93,b:.94,a:1},q=class extends Error{},E=t=>typeof t=="object"&&t!==null&&!Array.isArray(t),w=t=>typeof t=="number"&&Number.isFinite(t)?t:void 0,I=t=>typeof t=="string"?t:void 0,H=t=>typeof t=="boolean"?t:void 0,j=t=>typeof t=="string"?t.toUpperCase():void 0;function dt(t){return Number.isFinite(t)?Math.min(Math.max(t,0),ht):t===1/0?ht:0}function ft(t){return Number.isFinite(t)?Math.min(Math.max(t,1),60):12}function Wt(t){if(!E(t))return;let e=w(t.r),n=w(t.g),i=w(t.b);if(!(e===void 0||n===void 0||i===void 0))return{r:e,g:n,b:i,a:w(t.a)??1}}var se=class{constructor(){b(this,"warnings",[]);b(this,"seen",new Set)}warn(e){this.seen.has(e)||(this.seen.add(e),this.warnings.push(e))}fields(e,n){let i=Ne[n];for(let r of Object.keys(e))i.includes(r)||this.warn(`Unknown field "${r}" on ${n}, ignored`)}style(e){let n=E(e)?e:{};this.fields(n,"style");let i=I(n.fillStyle)??"solid",r=I(n.dash)??"solid";return["solid","hachure","crossHatch","grid"].includes(i)||this.warn(`Unknown fill style "${i}", drawn solid`),["solid","dashed","dotted"].includes(r)||this.warn(`Unknown dash "${r}", drawn solid`),{stroke:Wt(n.stroke)??{...ot},fill:Wt(n.fill)??{...He},fillStyle:["solid","hachure","crossHatch","grid"].includes(i)?i:"solid",strokeWidth:w(n.strokeWidth)??2,dash:["solid","dashed","dotted"].includes(r)?r:"solid",roughness:w(n.roughness)??1,bowing:w(n.bowing)??1,fontSize:w(n.fontSize)??18,gridSize:w(n.gridSize)??null,opacity:w(n.opacity)??null}}endpoint(e){let n=E(e)?e:{},i=I(n.port);return{elementID:j(n.elementID)??null,port:i&&["top","right","bottom","left"].includes(i)?i:null,x:w(n.x)??0,y:w(n.y)??0}}connector(e){if(!E(e))return null;this.fields(e,"connector");let n=(r,o)=>{let s=I(r);return s&&["none","arrow","triangle","dot","bar"].includes(s)?s:o},i=I(e.routing);return{start:this.endpoint(e.start),end:this.endpoint(e.end),routing:i&&["straight","curved","elbow"].includes(i)?i:"straight",startHead:n(e.startHead,"none"),endHead:n(e.endHead,"arrow")}}image(e,n){if(!E(e))return null;let i=I(e.ref);if(i!==void 0)return n[i]??{data:"",format:"png"};let r=I(e.data);return r===void 0?null:this.picture(r,I(e.format))}picture(e,n){return Zn.test(e)?{data:e.replace(/\s+/g,""),format:n==="jpeg"?"jpeg":"png"}:(this.warn("Skipped a picture whose data isn't base64"),{data:"",format:"png"})}element(e,n){if(!E(e))return null;this.fields(e,"element");let i=j(e.id),r=I(e.kind),o=j(e.layerID);if(!i||!r||!o)return this.warn("Skipped an element without an id, kind or layer"),null;Fe.includes(r)||this.warn(`Unknown element kind "${r}", drawn as a placeholder`);let s=Array.isArray(e.subpaths)?e.subpaths.filter(E).map(l=>({closed:H(l.closed)??!1,coords:Array.isArray(l.coords)?l.coords.filter(a=>typeof a=="number"):[]})):null;return{id:i,kind:r,layerID:o,x:w(e.x)??0,y:w(e.y)??0,width:w(e.width)??0,height:w(e.height)??0,style:this.style(e.style),seed:(w(e.seed)??1)>>>0,text:I(e.text)??null,connector:this.connector(e.connector),groupID:j(e.groupID)??null,points:Array.isArray(e.points)?e.points.filter(E).map(l=>({x:w(l.x)??0,y:w(l.y)??0})):null,path:Array.isArray(e.path)?e.path.filter(l=>typeof l=="number"):null,subpaths:s,evenOdd:H(e.evenOdd)??null,rotation:w(e.rotation)??null,link:j(e.link)??null,anchor:I(e.anchor)??null,image:this.image(e.image,n),reveal:w(e.reveal)??null}}ease(e){let n=I(e);return n===void 0?null:Qn.includes(n)?n:(this.warn(`Unknown ease "${n}", played linear`),null)}keyframe(e){if(!E(e))return null;this.fields(e,"keyframe");let n=w(e.t);if(n===void 0)return null;let i={t:n};for(let a of["x","y","width","height","rotation","strokeWidth","reveal","opacity"]){let c=w(e[a]);c!==void 0&&(i[a]=c)}let r=Wt(e.stroke),o=Wt(e.fill),s=H(e.visible),l=this.ease(e.ease);return r&&(i.stroke=r),o&&(i.fill=o),s!==void 0&&(i.visible=s),l&&(i.ease=l),i}track(e){if(!E(e))return null;this.fields(e,"track");let n=j(e.element);if(!n)return null;let i=(Array.isArray(e.keys)?e.keys:[]).map(s=>this.keyframe(s)).filter(s=>s!==null&&s.t>=0&&s.t<=ht);Yt(i,s=>s.t);let r=Array.isArray(e.follow)?e.follow.filter(E).flatMap(s=>{let l=j(s.path),a=w(s.start),c=w(s.end);return!l||a===void 0||c===void 0||a<0||c>ht?[]:[{path:l,start:a,end:c,ease:this.ease(s.ease),reverse:H(s.reverse)??null,orient:H(s.orient)??null,laps:w(s.laps)??null}]}):null,o=Array.isArray(e.repeats)?e.repeats.filter(E).flatMap(s=>{let l=w(s.start),a=w(s.end);if(l===void 0||a===void 0)return[];let c=w(s.count),u={start:l,end:a,count:c===void 0?null:Math.trunc(c)};return zt(u)?[u]:[]}):null;return{element:n,keys:i,boil:H(e.boil)??null,follow:r,repeats:o}}timeline(e){if(!E(e))return null;this.fields(e,"timeline");let n=w(e.boil),i=Array.isArray(e.camera)?e.camera.filter(E).flatMap(s=>{let l=w(s.t),a=w(s.x),c=w(s.y),u=w(s.width),m=w(s.height);return l===void 0||a===void 0||c===void 0||u===void 0||m===void 0?[]:u>1&&m>1&&l>=0?[{t:l,x:a,y:c,width:u,height:m,ease:this.ease(s.ease)}]:[]}):null;i&&Yt(i,s=>s.t);let r=Array.isArray(e.markers)?e.markers.filter(E).flatMap(s=>{let l=w(s.t);return l!==void 0&&l>=0?[{t:l,name:I(s.name)??""}]:[]}):null;r&&Yt(r,s=>s.t);let o=w(e.poster);return{fps:ft(w(e.fps)??12),duration:dt(w(e.duration)??0),boil:n!==void 0&&Number.isInteger(n)&&n>0?Math.min(n,1e4):null,settle:H(e.settle)??null,poster:o===void 0?null:dt(o),tracks:(Array.isArray(e.tracks)?e.tracks:[]).map(s=>this.track(s)).filter(s=>s!==null),camera:i,markers:r}}page(e,n,i){if(!E(e))throw new q(`Page ${i} is not an object`);this.fields(e,"page");let r=(Array.isArray(e.layers)?e.layers:[]).filter(E).flatMap(o=>{let s=j(o.id);return s?[{id:s,name:I(o.name)??"",visible:H(o.visible)??!0,locked:H(o.locked)??!1}]:[]});return r.length===0&&(this.warn(`Page ${i} has no layers`),r.push({id:"LAYER",name:"Layer 1",visible:!0,locked:!1})),{id:j(e.id)??`PAGE-${i}`,name:I(e.name)??`Page ${i}`,layers:r,elements:(Array.isArray(e.elements)?e.elements:[]).map(o=>this.element(o,n)).filter(o=>o!==null),theme:I(e.theme)??null,timeline:this.timeline(e.timeline)}}},Zn=/^[A-Za-z0-9+/=\s]*$/;function zt(t){return Number.isFinite(t.start)&&Number.isFinite(t.end)&&t.start>=0&&t.end-t.start>.001&&t.end<=ht&&(t.count===null||t.count>=1&&t.count<=1e4)}function Yt(t,e){let n=t.map((i,r)=>[i,r]);n.sort((i,r)=>e(i[0])-e(r[0])||i[1]-r[1]);for(let i=0;i<t.length;i++)t[i]=n[i][0]}function Rt(t){let e=t;if(typeof t=="string")try{e=JSON.parse(t)}catch(s){throw new q(`Not JSON: ${s.message}`)}if(!E(e))throw new q("Not a Dottie drawing");let n=w(e.version);if(n===void 0)throw new q("Not a Dottie drawing (no version)");if(!Array.isArray(e.pages)||e.pages.length===0)throw new q("A drawing needs at least one page");let i=new se;i.fields(e,"file"),n>2&&i.warn(`File version ${n} is newer than this player knows (2); some things may not show`);let r={};if(E(e.images))for(let[s,l]of Object.entries(e.images))E(l)&&typeof l.data=="string"&&(r[s]=i.picture(l.data,I(l.format)));let o=e.pages.map((s,l)=>i.page(s,r,l+1));if(new Set(o.map(s=>s.id)).size!==o.length)throw new q("Two pages share an id");return{version:n,pages:o,warnings:i.warnings}}function pt(t){return .2126*t.r+.7152*t.g+.0722*t.b}function mt(t,e){return t.r===e.r&&t.g===e.g&&t.b===e.b&&t.a===e.a}function ae(t){return mt(t,ot)||mt(t,je)}function _(t){return{...t,style:{...t.style,stroke:{...t.style.stroke},fill:{...t.style.fill}},connector:t.connector?{...t.connector,start:{...t.connector.start},end:{...t.connector.end}}:null}}function At(t){if(t.layers.length<=1)return t.elements;let e=new Map;t.layers.forEach((i,r)=>{e.has(i.id)||e.set(i.id,r)});let n=t.layers.map(()=>[]);for(let i of t.elements)n[e.get(i.layerID)??0].push(i);return n.flat()}function Jn(t){let e=new Map;for(let n of t.elements)e.has(n.id)||e.set(n.id,n);return e}var qe=.5522847498307936,R=class t{constructor(){b(this,"segments",[])}get isEmpty(){return this.segments.length===0}moveTo(e){return this.segments.push({op:"M",p:e}),this}lineTo(e){return this.segments.push({op:"L",p:e}),this}quadTo(e,n){return this.segments.push({op:"Q",c:n,p:e}),this}curveTo(e,n,i){return this.segments.push({op:"C",c1:n,c2:i,p:e}),this}close(){return this.segments.push({op:"Z"}),this}addPath(e){return this.segments.push(...e.segments),this}addLines(e){return e.forEach((n,i)=>i===0?this.moveTo(n):this.lineTo(n)),this}static rect(e){return new t().addLines([{x:e.x,y:e.y},{x:e.x+e.width,y:e.y},{x:e.x+e.width,y:e.y+e.height},{x:e.x,y:e.y+e.height}]).close()}addEllipse(e){let n=e.x+e.width/2,i=e.y+e.height/2,r=e.width/2,o=e.height/2,s=r*qe,l=o*qe;return this.moveTo({x:n+r,y:i}),this.curveTo({x:n,y:i+o},{x:n+r,y:i+l},{x:n+s,y:i+o}),this.curveTo({x:n-r,y:i},{x:n-s,y:i+o},{x:n-r,y:i+l}),this.curveTo({x:n,y:i-o},{x:n-r,y:i-l},{x:n-s,y:i-o}),this.curveTo({x:n+r,y:i},{x:n+s,y:i-o},{x:n+r,y:i-l}),this.close()}static ellipse(e){return new t().addEllipse(e)}translated(e,n){let i=o=>({x:o.x+e,y:o.y+n}),r=new t;return r.segments=this.segments.map(o=>{switch(o.op){case"M":case"L":return{op:o.op,p:i(o.p)};case"Q":return{op:"Q",c:i(o.c),p:i(o.p)};case"C":return{op:"C",c1:i(o.c1),c2:i(o.c2),p:i(o.p)};case"Z":return o}}),r}contours(){let e=[],n=[],i={x:0,y:0};for(let r of this.segments){let o=n.length?n[n.length-1]:i;switch(r.op){case"M":n.length>1&&e.push(n),i=r.p,n=[r.p];break;case"L":n.push(r.p);break;case"Q":for(let s=1;s<=12;s++){let l=s/12,a=1-l;n.push({x:a*a*o.x+2*a*l*r.c.x+l*l*r.p.x,y:a*a*o.y+2*a*l*r.c.y+l*l*r.p.y})}break;case"C":for(let s=1;s<=16;s++){let l=s/16,a=1-l;n.push({x:a*a*a*o.x+3*a*a*l*r.c1.x+3*a*l*l*r.c2.x+l*l*l*r.p.x,y:a*a*a*o.y+3*a*a*l*r.c1.y+3*a*l*l*r.c2.y+l*l*l*r.p.y})}break;case"Z":n.push(i),n.length>1&&e.push(n),n=[i];break}}return n.length>1&&e.push(n),e}trimmed(e){let n=this.contours().map(o=>new yt(o)),i=Math.min(Math.max(e,0),1)*n.reduce((o,s)=>o+s.length,0),r=new t;for(let o of n){if(!(i>0))break;r.moveTo(o.points[0]);for(let s=1;s<o.points.length;s++){let l=o.lengths[s]-o.lengths[s-1];if(l<=i){r.lineTo(o.points[s]),i-=l;continue}let a=o.points[s-1],c=o.points[s],u=i/l;r.lineTo({x:a.x+(c.x-a.x)*u,y:a.y+(c.y-a.y)*u}),i=0;break}}return r}data(){let e=[];for(let n of this.segments)switch(n.op){case"M":e.push(`M${y(n.p.x)} ${y(n.p.y)}`);break;case"L":e.push(`L${y(n.p.x)} ${y(n.p.y)}`);break;case"Q":e.push(`Q${y(n.c.x)} ${y(n.c.y)} ${y(n.p.x)} ${y(n.p.y)}`);break;case"C":e.push(`C${y(n.c1.x)} ${y(n.c1.y)} ${y(n.c2.x)} ${y(n.c2.y)} ${y(n.p.x)} ${y(n.p.y)}`);break;case"Z":e.push("Z");break}return e.join(" ")}};function ce(t){return t<0?-Math.round(-t):Math.round(t)}function y(t){let e=ce(t*100)/100;return Number.isInteger(e)?String(e===0?0:e):String(e)}var yt=class{constructor(e){b(this,"points");b(this,"lengths");this.points=e;let n=0;this.lengths=e.map((i,r)=>(r>0&&(n+=Math.hypot(i.x-e[r-1].x,i.y-e[r-1].y)),n))}get length(){return this.lengths.length?this.lengths[this.lengths.length-1]:0}point(e){if(this.points.length<=1||!(this.length>0))return this.points[0]??{x:0,y:0};let n=Math.min(Math.max(e,0),1)*this.length,i=1;for(;i<this.lengths.length-1&&this.lengths[i]<n;)i++;let r=this.lengths[i]-this.lengths[i-1],o=r>0?(n-this.lengths[i-1])/r:0,s=this.points[i-1],l=this.points[i];return{x:s.x+(l.x-s.x)*o,y:s.y+(l.y-s.y)*o}}},C=t=>t.x+t.width/2,G=t=>t.y+t.height/2,Y=t=>t.x+t.width,z=t=>t.y+t.height;function ue(t,e){if(!t)return{...e};let n=Math.min(t.x,e.x),i=Math.min(t.y,e.y);return{x:n,y:i,width:Math.max(Y(t),Y(e))-n,height:Math.max(z(t),z(e))-i}}function gt(t,e,n){return{x:t.x+e,y:t.y+n,width:t.width-2*e,height:t.height-2*n}}function he(t,e,n){if(n===0)return t;let i=t.x-e.x,r=t.y-e.y;return{x:e.x+i*Math.cos(n)-r*Math.sin(n),y:e.y+i*Math.sin(n)+r*Math.cos(n)}}function ti(t,e){let n=1-e;return{x:t[0].x*(n*n*n)+t[1].x*(3*e*n*n)+t[2].x*(3*e*e*n)+t[3].x*(e*e*e),y:t[0].y*(n*n*n)+t[1].y*(3*e*n*n)+t[2].y*(3*e*e*n)+t[3].y*(e*e*e)}}function _e(t,e){let n=[t[0]];for(let i=0;i<Math.floor((t.length-1)/3);i++){let r=t.slice(3*i,3*i+4),o=0;for(let l=1;l<r.length;l++)o+=Math.hypot(r[l].x-r[l-1].x,r[l].y-r[l-1].y);let s=Math.min(400,Math.max(4,Math.ceil(o/Math.max(e,.5))));for(let l=1;l<=s;l++)n.push(ti(r,l/s))}return n}var L=t=>({x:t.x,y:t.y,width:t.width,height:t.height}),O=t=>t.rotation??0,Tt=t=>({x:t.x+t.width/2,y:t.y+t.height/2}),Q=(t,e)=>he(e,Tt(t),O(t)),Ue=(t,e)=>he(e,Tt(t),-O(t));function Ke(t){let e=L(t);if(O(t)===0)return e;let n=[{x:e.x,y:e.y},{x:Y(e),y:e.y},{x:Y(e),y:z(e)},{x:e.x,y:z(e)}].map(l=>Q(t,l)),i=n.map(l=>l.x),r=n.map(l=>l.y),o=Math.min(...i),s=Math.min(...r);return{x:o,y:s,width:Math.max(...i)-o,height:Math.max(...r)-s}}function Xe(t,e){if(!ae(t.style.stroke))return t;let n=_(t),r=(t.style.fillStyle==="solid"||t.style.fillStyle==="grid")&&t.style.fill.a>=.5&&pt(t.style.fill)>.5;return n.style.stroke=pt(e)>.5&&r?{...ot}:{...e},n}function Ct(t){if(t.kind!=="path"||!t.subpaths)return[];let e=[];for(let n of t.subpaths){if(n.coords.length<8||(n.coords.length-2)%6!==0)continue;let i=[];for(let r=0;r<n.coords.length;r+=2)i.push({x:t.x+n.coords[r]*t.width,y:t.y+n.coords[r+1]*t.height});e.push({closed:n.closed,points:i})}return e}function de(t){let e=new R;for(let n of Ct(t)){e.moveTo(n.points[0]);for(let i=0;i<(n.points.length-1)/3;i++)e.curveTo(n.points[3*i+3],n.points[3*i+1],n.points[3*i+2]);n.closed&&e.close()}return e}function ei(t,e){let n=L(t),i=e==="top"?{x:C(n),y:n.y}:e==="right"?{x:Y(n),y:G(n)}:e==="bottom"?{x:C(n),y:z(n)}:{x:n.x,y:G(n)};return Q(t,i)}function ni(t,e){let n=L(t),i=Tt(t),r=Ue(t,e),o=r.x-i.x,s=r.y-i.y;if(!((o!==0||s!==0)&&n.width>0&&n.height>0))return i;let l=n.width/2,a=n.height/2,c;return t.kind==="ellipse"?c=1/Math.sqrt(o*o/(l*l)+s*s/(a*a)):t.kind==="diamond"?c=1/(Math.abs(o)/l+Math.abs(s)/a):c=Math.min(o!==0?l/Math.abs(o):1/0,s!==0?a/Math.abs(s):1/0),Q(t,{x:i.x+o*c,y:i.y+s*c})}function Qe(t,e){let n=t.elementID?e.get(t.elementID):void 0;return n?Tt(n):{x:t.x,y:t.y}}function Ze(t,e,n){let i=t.elementID?n.get(t.elementID):void 0;return i?t.port?ei(i,t.port):ni(i,e):{x:t.x,y:t.y}}function st(t,e){let n=t.connector;return n?[Ze(n.start,Qe(n.end,e),e),Ze(n.end,Qe(n.start,e),e)]:[{x:0,y:0},{x:0,y:0}]}function Z(t){if(t.kind==="polyline")return t.points;let e=[];for(let n=0;n<=24;n++){let i=n/24,r=1-i;e.push({x:r*r*r*t.a.x+3*r*r*i*t.b.x+3*r*i*i*t.c.x+i*i*i*t.d.x,y:r*r*r*t.a.y+3*r*r*i*t.b.y+3*r*i*i*t.c.y+i*i*i*t.d.y})}return e}function J(t,e){let n=t.connector;if(!n)return{kind:"polyline",points:[]};let[i,r]=st(t,e),o=r.x-i.x,s=r.y-i.y,l=Math.abs(o)>=Math.abs(s);switch(n.routing){case"straight":return{kind:"polyline",points:[i,r]};case"curved":return l?{kind:"cubic",a:i,b:{x:i.x+o/2,y:i.y},c:{x:r.x-o/2,y:r.y},d:r}:{kind:"cubic",a:i,b:{x:i.x,y:i.y+s/2},c:{x:r.x,y:r.y-s/2},d:r};case"elbow":{let a=m=>m===null?null:m==="top"||m==="bottom",c=a(n.start.port)??!l,u=a(n.end.port)??!l;if(c&&u){let m=i.y+s/2;return{kind:"polyline",points:[i,{x:i.x,y:m},{x:r.x,y:m},r]}}if(!c&&!u){let m=i.x+o/2;return{kind:"polyline",points:[i,{x:m,y:i.y},{x:m,y:r.y},r]}}return c?{kind:"polyline",points:[i,{x:i.x,y:r.y},r]}:{kind:"polyline",points:[i,{x:r.x,y:i.y},r]}}}}function Je(t,e,n){let i=Math.atan2(t.y-e.y,t.x-e.x),r=Math.PI/7,o=s=>({x:t.x-n*Math.cos(i+s),y:t.y-n*Math.sin(i+s)});return[o(r),o(-r)]}function tn(t,e){let n=Z(J(t,e));if(n.length===0)return{x:0,y:0,width:0,height:0};let i=n[0].x,r=n[0].y,o=i,s=r;for(let l of n)i=Math.min(i,l.x),r=Math.min(r,l.y),o=Math.max(o,l.x),s=Math.max(s,l.y);return{x:i,y:r,width:o-i,height:s-r}}function en(t,e){let n=Z(J(t,e));if(n.length<=1)return n[0]??{x:0,y:0};let i=[],r=0;for(let s=0;s<n.length-1;s++){let l=Math.hypot(n[s+1].x-n[s].x,n[s+1].y-n[s].y);i.push(l),r+=l}let o=r/2;for(let s=0;s<i.length;s++){let l=i[s];if(o<=l||s===i.length-1){let a=l===0?0:o/l;return{x:n[s].x+(n[s+1].x-n[s].x)*a,y:n[s].y+(n[s+1].y-n[s].y)*a}}o-=l}return n[0]}function tt(t){let e=new Map;for(let n of t.elements)e.has(n.id)||e.set(n.id,n);return e}function fe(t,e,n){let i=t[1]+n>>>0,r=i<t[1]?1:0;return[t[0]+e+r>>>0,i]}function ii(t,e){let n=t&65535,i=t>>>16,r=e&65535,o=e>>>16,s=n*r,l=n*o,a=i*r,c=i*o,u=l+a,m=s+u%65536*65536;return[c+Math.floor(u/65536)+Math.floor(m/4294967296)>>>0,m>>>0]}function pe(t,e,n){let i=ii(t[1],n);return[i[0]+Math.imul(t[0],n)+Math.imul(t[1],e)>>>0,i[1]]}function me(t,e){let n=(t[1]>>>e|t[0]<<32-e)>>>0;return[(t[0]^t[0]>>>e)>>>0,(t[1]^n)>>>0]}function nn(t){return t=pe(me(t,30),3210233709,484763065),t=pe(me(t,27),2496678331,321982955),me(t,31)}var et=class{constructor(e){b(this,"state");this.state=fe([0,e>>>0],2654435769,2135587861)}next(){return this.state=fe(this.state,2654435769,2135587861),nn(this.state)}top53(){let[e,n]=this.next();return e*2097152+(n>>>11)}signed(){return Math.floor(this.top53())/2**52-1}unit(){return this.top53()/2**53}};function rn(t,e){let n=fe([0,t>>>0],...ri(e));return n=nn(n),Math.max(1,n[1]>>>0)}function ri(t){let e=pe([0,t>>>0],2654435769,2135587861);return[e[0],e[1]]}var on=(t,e,n)=>t.signed()*e*n;function U(t,e,n,i,r,o,s){let l=Math.hypot(e.x-t.x,e.y-t.y);if(!(l>.5))return;let a=l<200?1:l>500?.4:-.0016668*l+1.233334,c=l<20?l/10:2;for(let u=0;u<2;u++){let m=(u===0?1:.5)*a,f=()=>on(n,c,i)*m,d=n.unit()*.4+.3,h=on(n,l/60,i)*r*a,p=e.x-t.x,x=e.y-t.y,M=-x/l,k=p/l,g=o?t:{x:t.x+f(),y:t.y+f()},A=o?e:{x:e.x+f(),y:e.y+f()},T=t.x+p*d*.66+M*h+f()*.5,S=t.y+x*d*.66+k*h+f()*.5,P=t.x+p*(.5+d*.5)+M*h+f()*.5,$=t.y+x*(.5+d*.5)+k*h+f()*.5;s.moveTo(g),s.curveTo(A,{x:T,y:S},{x:P,y:$})}}function ye(t,e,n,i){let r=new R;for(let o=0;o<t.length;o++)U(t[o],t[(o+1)%t.length],e,n,i,!1,r);return r}function sn(t,e,n){let i=new R,r=t.x+t.width/2,o=t.y+t.height/2,s=t.width/2,l=t.height/2;if(!(s>.5&&l>.5))return i;let a=Math.max(8,Math.min(24,Math.trunc((s+l)/6))),c=Math.min(2.5,(s+l)/25)*n;for(let u=0;u<2;u++){let m=e.unit()*2*Math.PI,f=2*Math.PI*(u===0?1:1.04),d=Math.trunc(a*f/(2*Math.PI))+1,h=[];for(let p=0;p<d;p++){let x=m+f*p/a,M=e.signed()*c,k=e.signed()*c;h.push({x:r+s*Math.cos(x)+M,y:o+l*Math.sin(x)+k})}oi(h,i)}return i}function oi(t,e){if(!(t.length<=2)){e.moveTo(t[0]);for(let n=0;n<t.length-1;n++){let i=t[Math.max(n-1,0)],r=t[n],o=t[n+1],s=t[Math.min(n+2,t.length-1)];e.curveTo(o,{x:r.x+(o.x-i.x)/6,y:r.y+(o.y-i.y)/6},{x:o.x-(s.x-r.x)/6,y:o.y-(s.y-r.y)/6})}}}var ge=-41*Math.PI/180;function ln(t,e,n){if(!(n>0))return[];let i=Math.cos(e),r=Math.sin(e),o=t.filter(f=>f.length>=3).map(f=>f.map(d=>({x:d.x*i+d.y*r,y:-d.x*r+d.y*i}))),s=o.flat();if(s.length===0)return[];let l=1/0,a=-1/0;for(let f of s)l=Math.min(l,f.y),a=Math.max(a,f.y);let c=(f,d)=>({x:f*i-d*r,y:f*r+d*i}),u=[],m=l+n/2;for(;m<a;){let f=[];for(let d of o)for(let h=0;h<d.length;h++){let p=d[h],x=d[(h+1)%d.length];(p.y<=m&&m<x.y||x.y<=m&&m<p.y)&&f.push(p.x+(m-p.y)/(x.y-p.y)*(x.x-p.x))}f.sort((d,h)=>d-h);for(let d=0;d+1<f.length;d+=2)f[d+1]-f[d]>.5&&u.push([c(f[d],m),c(f[d+1],m)]);m+=n}return u}var Gt=class{constructor(e=6e3){b(this,"map",new Map);b(this,"limit");this.limit=e}get(e,n){let i=this.map.get(e);if(i!==void 0)return i;let r=n();return this.map.size>=this.limit&&this.map.clear(),this.map.set(e,r),r}},un=new Gt,si=new Gt;function hn(t,e){let n=t.style;return[e,t.kind,t.x,t.y,t.width,t.height,t.seed,n.roughness,n.bowing,n.strokeWidth,n.fillStyle,n.fillStyle==="grid"?lt(t):0,t.path?t.path.join(","):t.subpaths?JSON.stringify(t.subpaths):""].join("|")}var lt=t=>Math.max(8,t.style.gridSize??50);function at(t){return t.kind==="freehand"&&t.path===null?cn(t):un.get(hn(t,0),()=>cn(t))}function dn(t){return[{x:t.x,y:t.y},{x:Y(t),y:t.y},{x:Y(t),y:z(t)},{x:t.x,y:z(t)}]}function Bt(t){return[{x:C(t),y:t.y},{x:Y(t),y:G(t)},{x:C(t),y:z(t)},{x:t.x,y:G(t)}]}function cn(t){let e=L(t),n=new et(t.seed),i=t.style.roughness;switch(t.kind){case"ellipse":return i<=0?R.ellipse(e):sn(e,n,i);case"diamond":return i<=0?new R().addLines(Bt(e)).close():ye(Bt(e),n,i,t.style.bowing);case"text":case"connector":return R.rect(e);case"freehand":return ai(t);case"path":return i<=0?de(t):li(t,n);default:return i<=0?R.rect(e):ye(dn(e),n,i,t.style.bowing)}}function li(t,e){let n=t.style.roughness,i=new R;for(let r of Ct(t)){let o=r.points;for(let s=0;s<(o.length-1)/3;s++){let l=o[3*s],a=o[3*s+1],c=o[3*s+2],u=o[3*s+3];if(a.x===l.x&&a.y===l.y&&c.x===u.x&&c.y===u.y){U(l,u,e,n,t.style.bowing,!0,i);continue}let m=Math.hypot(a.x-l.x,a.y-l.y)+Math.hypot(c.x-a.x,c.y-a.y)+Math.hypot(u.x-c.x,u.y-c.y),f=Math.min(2.5,m/30)*n;for(let d=0;d<2;d++){let h=d===0?1:.5,p=()=>e.signed()*f*h;i.moveTo(l);let x=a.x+p(),M=a.y+p(),k=c.x+p(),g=c.y+p();i.curveTo(u,{x,y:M},{x:k,y:g})}}}return i}function ai(t){let e=L(t),n=t.path;if(n&&n.length>=8&&(n.length-2)%6===0){let o=l=>({x:e.x+n[2*l]*e.width,y:e.y+n[2*l+1]*e.height}),s=new R().moveTo(o(0));for(let l=0;l<Math.floor((n.length/2-1)/3);l++)s.curveTo(o(3*l+3),o(3*l+1),o(3*l+2));return s}let i=(t.points??[]).map(o=>({x:e.x+o.x*e.width,y:e.y+o.y*e.height})),r=new R;if(i.length===0)return r;if(r.moveTo(i[0]),i.length===1)return r.lineTo(i[0]);if(i.length===2)return r.lineTo(i[1]);for(let o=1;o<i.length-1;o++)r.quadTo({x:(i[o].x+i[o+1].x)/2,y:(i[o].y+i[o+1].y)/2},i[o]);return r.lineTo(i[i.length-1])}function xe(t){return t.kind!=="path"?[ci(t)]:Ct(t).map(e=>_e(e.points,3))}function ci(t){let e=L(t);if(t.kind==="ellipse"){let n=[];for(let i=0;i<48;i++){let r=2*Math.PI*i/48;n.push({x:C(e)+e.width/2*Math.cos(r),y:G(e)+e.height/2*Math.sin(r)})}return n}return t.kind==="diamond"?Bt(e):dn(e)}function Ot(t){let e=L(t);return t.kind==="ellipse"?R.ellipse(e):t.kind==="path"?de(t):t.kind==="diamond"?new R().addLines(Bt(e)).close():R.rect(e)}function be(t){return t.style.fillStyle==="solid"||["freehand","connector","text","image"].includes(t.kind)?new R:un.get(hn(t,1),()=>ui(t))}function ui(t){let e=xe(t),n=new et((t.seed^1540483477)>>>0),i=new R;if(t.style.fillStyle==="grid"){for(let[s,l]of fn(e,lt(t)))t.style.roughness<=0?i.moveTo(s).lineTo(l):U(s,l,n,t.style.roughness*.3,.3,!1,i);return i}let r=Math.max(4,t.style.strokeWidth*4),o=[ge];t.style.fillStyle==="crossHatch"&&o.push(ge+Math.PI/2);for(let s of o)for(let[l,a]of ln(e,s,r))t.style.roughness<=0?i.moveTo(l).lineTo(a):U(l,a,n,t.style.roughness*.6,.5,!1,i);return i}function mn(t,e){switch(t){case"solid":return null;case"dashed":return[Math.max(8,e*4),Math.max(6,e*3)];case"dotted":return[.01,Math.max(5,e*2.6)]}}function ke(t,e){let n=t.connector;if(!n)return[new R,new R];let[i,r]=st(t,e),o=[t.seed,t.style.roughness,t.style.bowing,t.style.strokeWidth,n.routing,n.startHead,n.endHead,n.start.port,n.end.port,i.x,i.y,r.x,r.y].join("|");return si.get(o,()=>hi(t,e))}function hi(t,e){let n=t.connector,i=new et(t.seed),r=t.style.roughness,o=t.style.bowing,s=new R,l=new R,a=J(t,e);if(a.kind==="polyline"){let d=a.points;for(let h=0;h<Math.max(d.length-1,0);h++)r<=0?s.moveTo(d[h]).lineTo(d[h+1]):U(d[h],d[h+1],i,r,o,!0,s)}else{s.moveTo(a.a);let d=r*3,h=()=>i.signed()*d,p=a.b.x+h(),x=a.b.y+h(),M=a.c.x+h(),k=a.c.y+h();s.curveTo(a.d,{x:p,y:x},{x:M,y:k})}let c=Z(a),u=Math.max(10,t.style.strokeWidth*5),m=(d,h)=>{r<=0?s.moveTo(d).lineTo(h):U(d,h,i,r*.5,0,!0,s)},f=(d,h,p)=>{let[x,M]=Je(h,p,u);switch(d){case"arrow":m(x,h),m(M,h);break;case"triangle":m(x,h),m(M,h),m(x,M),l.moveTo(h).lineTo(x).lineTo(M).close();break;case"dot":{let k=u*.32,g=Math.atan2(h.y-p.y,h.x-p.x),A=h.x-k*Math.cos(g),T=h.y-k*Math.sin(g);l.addEllipse({x:A-k,y:T-k,width:2*k,height:2*k});break}case"bar":{let k=Math.atan2(h.y-p.y,h.x-p.x)+Math.PI/2,g=u*.5;m({x:h.x-g*Math.cos(k),y:h.y-g*Math.sin(k)},{x:h.x+g*Math.cos(k),y:h.y+g*Math.sin(k)});break}}};return c.length>1&&(f(n.endHead,c[c.length-1],c[c.length-2]),f(n.startHead,c[0],c[1])),[s,l]}var gn=2,we=.75,di=.8,pn=.6;function fn(t,e){return[...yn(t,e,!1),...yn(t,e,!0)]}function yn(t,e,n){if(!(e>0))return[];let i=c=>n?{x:c.y,y:c.x}:c,r=t.filter(c=>c.length>=3).map(c=>c.map(i)),o=r.flat().map(c=>c.y);if(o.length===0)return[];let s=Math.min(...o),l=Math.max(...o),a=[];for(let c=Math.floor(s/e)*e;c<l-.5;c+=e){if(!(c>s+.5))continue;let u=[];for(let m of r)for(let f=0;f<m.length;f++){let d=m[f],h=m[(f+1)%m.length];(d.y<=c&&c<h.y||h.y<=c&&c<d.y)&&u.push(d.x+(c-d.y)/(h.y-d.y)*(h.x-d.x))}u.sort((m,f)=>m-f);for(let m=0;m+1<u.length;m+=2)u[m+1]-u[m]>.5&&a.push([i({x:u[m],y:c}),i({x:u[m+1],y:c})])}return a}function xn(t){return .2126*t.r+.7152*t.g+.0722*t.b>.5?{r:t.r*.84,g:t.g*.84,b:t.b*.8,a:t.a}:{r:t.r+(1-t.r)*.25,g:t.g+(1-t.g)*.25,b:t.b+(1-t.b)*.25,a:t.a}}function Pe(t){return t.style.fillStyle==="grid"&&t.style.fill.a>0&&["rectangle","ellipse","diamond","path"].includes(t.kind)}function Nt(t,e,n){let i=(Math.imul(t,668265261)^Math.imul(e,374761393)^Math.imul(n,2654435761))>>>0;return i=Math.imul(i^i>>>15,2246822507)>>>0,i=Math.imul(i^i>>>13,3266489909)>>>0,i=(i^i>>>16)>>>0,i/4294967296}function bn(t,e,n){let i=n/gn;return{x:(t+.5+(Nt(t,e,1)-.5)*pn)*i,y:(e+.5+(Nt(t,e,2)-.5)*pn)*i}}function mi(t,e,n,i){let r=1/0;for(let o=-1;o<=1;o++)for(let s=-1;s<=1;s++){if(o===0&&s===0)continue;let l=bn(t+o,e+s,i);r=Math.min(r,Math.hypot(l.x-n.x,l.y-n.y))}return r*di}function fi(t,e,n){let i=Math.cos(e),r=Math.sin(e),o=n*.8,s=n*.5;return[-1,0,1].map(l=>{let a=t.x-r*s*l,c=t.y+i*s*l;return[{x:a-i*o,y:c-r*o},{x:a+i*o,y:c+r*o}]})}function pi(t,e){let n=!1;for(let i of t){if(i.length<3)continue;let r=i.length-1;for(let o=0;o<i.length;o++){let s=i[o],l=i[r];s.y>e.y!=l.y>e.y&&e.x<(l.x-s.x)*(e.y-s.y)/(l.y-s.y)+s.x&&(n=!n),r=o}}return n}function yi(t,e){let n=1/0;for(let i of e)if(!(i.length<2))for(let r=0;r<i.length;r++){let o=i[r],s=i[(r+1)%i.length],l=s.x-o.x,a=s.y-o.y,c=l*l+a*a,u=c>0?Math.max(0,Math.min(1,((t.x-o.x)*l+(t.y-o.y)*a)/c)):0;n=Math.min(n,Math.hypot(t.x-(o.x+u*l),t.y-(o.y+u*a)))}return n}var gi=(t,e)=>e.x>=t.x&&e.x<t.x+t.width&&e.y>=t.y&&e.y<t.y+t.height,Ft=new Map;function kn(t){if(t.length===0)return[];let e=t.map(r=>[r.kind,r.x,r.y,r.width,r.height,r.rotation??0,lt(r),JSON.stringify(r.subpaths??null),r.style.stroke.r,r.style.stroke.g,r.style.stroke.b,r.style.stroke.a,r.style.roughness,r.style.strokeWidth].join(",")).join(";"),n=Ft.get(e);if(n)return n;let i=xi(t);return Ft.size>=16&&Ft.clear(),Ft.set(e,i),i}function xi(t){let e=t.map(o=>{let s=xe(o).map(d=>d.map(h=>Q(o,h))),l=s.flat(),a=l.map(d=>d.x),c=l.map(d=>d.y),u=a.length?Math.min(...a):0,m=c.length?Math.min(...c):0,f={x:u,y:m,width:(a.length?Math.max(...a):0)-u,height:(c.length?Math.max(...c):0)-m};return{contours:s,box:f,cell:lt(o),element:o}}),n=e.map(()=>[]),i=new Set;for(let o of e){let s=o.cell/gn,l=gt(o.box,-o.cell,-o.cell),a=Math.floor(l.x/s),c=Math.ceil((l.x+l.width)/s),u=Math.floor(l.y/s),m=Math.ceil((l.y+l.height)/s),f=we*o.cell;for(let d=a;d<c;d++)for(let h=u;h<m;h++){let p=`${o.cell}|${d}|${h}`;if(i.has(p))continue;i.add(p);let x=bn(d,h,o.cell),M=1/0,k=-1;for(let S=0;S<e.length;S++){let P=e[S];if(!gi(gt(P.box,-f,-f),x))continue;if(pi(P.contours,x)){M=0;break}let $=yi(x,P.contours);$<M&&(M=$,k=S)}if(!(M>0&&M<=f&&k>=0))continue;let g=Nt(d,h,3)*Math.PI,A=mi(d,h,x,o.cell),T=Math.trunc(Nt(d,h,4)*4294967296)>>>0;for(let[S,P]of fi(x,g,A))n[k].push([S,P,T])}}let r=[];return e.forEach((o,s)=>{if(n[s].length===0)return;let l=o.element,a=new R;for(let[c,u,m]of n[s])l.style.roughness<=0?a.moveTo(c).lineTo(u):U(c,u,new et(m),l.style.roughness*.35,.2,!1,a);r.push({ink:{...l.style.stroke,a:l.style.stroke.a*.6},width:Math.max(.8,l.style.strokeWidth*.45),path:a})}),r}var Ht=.951995849609375,jt=.2129974365234375,qt=.0279998779296875,wn=.975006103515625,Pn=.2169952392578125,Mn=.0290069580078125,Rn=.957000732421875,An=.2129974365234375,Tn=.0279998779296875,vn=.975006103515625,En=.2169952392578125,Sn=.0290069580078125,$n=.60205078125,In=.92822265625,Ln=.23583984375,xt={bold:{k:{"\u2018\u2018":-95,"\u2019\u2019":-95,"\u2019s":-74,"\u2019t":-18,"A\u2019":-74,AT:-92,Av:-18,AV:-46,Aw:-18,AW:-24,Ay:-18,AY:-74,"F,":-157,"F.":-157,"f\u2019":18,FA:-55,ff:-30,fi:20,fl:20,"L\u2019":-74,LT:-92,LV:-92,LW:-55,Ly:-37,LY:-111,"P,":-179,"P.":-179,PA:-74,"r-":-55,"r,":-92,"r.":-92,rc:-18,rd:-18,re:-18,rg:-28,rn:18,ro:-18,rq:-18,RT:-18,RV:-18,RW:-18,RY:-37,"T-":-129,"T,":-111,"T;":-111,"T:":-111,"T.":-111,Ta:-111,TA:-92,Tc:-111,Te:-111,To:-111,Tr:-92,Ts:-111,Tu:-92,Tw:-111,Ty:-111,"V-":-55,"v,":-74,"V,":-129,"V;":-46,"V:":-46,"v.":-74,"V.":-129,Va:-55,VA:-46,Ve:-55,Vi:-18,Vo:-55,Vr:-37,Vu:-37,Vy:-18,"w,":-55,"W,":-74,"W;":-18,"W:":-18,"w.":-55,"W.":-74,Wa:-37,WA:-24,We:-57,Wo:-57,Wr:-18,Wu:-18,"Y-":-111,"y,":-74,"Y,":-139,"Y;":-92,"Y:":-92,"y.":-74,"Y.":-139,Ya:-92,YA:-74,Ye:-111,Yi:-37,Yo:-111,Yp:-74,Yq:-92,Yu:-74,Yv:-55},w:{" ":278,_:500,"-":407,"\u2013":500,"\u2014":1e3,",":278,";":278,":":278,"!":278,"?":556,".":278,"\u2026":1e3,"\xB7":278,"'":278,"\u2018":278,"\u2019":278,'"':463,"\u201C":463,"\u201D":463,"(":296,")":296,"[":333,"]":333,"{":333,"}":333,"@":800,"*":407,"/":371,"\\":371,"&":685,"#":556,"%":1e3,"\u2022":350,"`":259,"^":600,"\u2190":1e3,"\u2192":1e3,"\u2191":722.66,"\u2193":722.66,"+":600,"\xD7":600,"<":600,"=":600,">":600,"|":223,"~":600,"\u25E6":608.4,"\u2610":830.08,"\u2611":830.08,"\u2713":764.16,$:556,0:556,1:556,2:556,3:556,4:556,5:556,6:556,7:556,8:556,9:556,a:574,A:685,b:611,B:704,c:574,C:741,d:611,D:741,e:574,E:648,\u00E9:574,f:333,F:593,g:611,G:759,h:593,H:741,i:258,I:295,j:278,J:556,k:574,K:722,l:258,L:593,m:906,M:907,n:593,N:741,o:611,O:778,p:611,P:667,q:611,Q:778,r:389,R:722,s:537,S:649,t:352,T:611,u:593,U:741,v:520,V:630,w:814,W:944,x:537,X:667,y:519,Y:667,z:519,Z:648}},boldItalic:{k:{"\u2018\u2018":-95,"\u2019\u2019":-95,"\u2019s":-74,"\u2019t":-18,"A\u2019":-74,AT:-92,Av:-18,AV:-46,Aw:-18,AW:-24,Ay:-18,AY:-74,"F,":-157,"F.":-157,"f\u2019":18,FA:-55,ff:-68,"L\u2019":-74,LT:-92,LV:-92,LW:-55,Ly:-37,LY:-111,"P,":-179,"P.":-179,PA:-74,"r-":-55,"r,":-92,"r.":-92,rc:-18,rd:-18,re:-18,rg:-28,rn:18,ro:-18,rq:-18,RT:-18,RV:-18,RW:-18,RY:-37,"T-":-129,"T,":-111,"T;":-111,"T:":-111,"T.":-111,Ta:-111,TA:-92,Tc:-111,Te:-111,To:-111,Tr:-92,Ts:-111,Tu:-92,Tw:-111,Ty:-111,"V-":-55,"v,":-74,"V,":-129,"V;":-46,"V:":-46,"v.":-74,"V.":-129,Va:-55,VA:-46,Ve:-55,Vi:-18,Vo:-55,Vr:-37,Vu:-37,Vy:-18,"w,":-55,"W,":-74,"W;":-18,"W:":-18,"w.":-55,"W.":-74,Wa:-37,WA:-24,We:-57,Wo:-57,Wr:-18,Wu:-18,"Y-":-111,"y,":-74,"Y,":-139,"Y;":-92,"Y:":-92,"y.":-74,"Y.":-139,Ya:-92,YA:-74,Ye:-111,Yi:-37,Yo:-111,Yp:-74,Yq:-92,Yu:-74,Yv:-55},w:{" ":278,_:500,"-":407,"\u2013":500,"\u2014":1e3,",":278,";":278,":":278,"!":296,"?":574,".":278,"\u2026":1e3,"\xB7":278,"'":278,"\u2018":278,"\u2019":278,'"':481,"\u201C":481,"\u201D":481,"(":296,")":296,"[":333,"]":333,"{":333,"}":333,"@":800,"*":407,"/":389,"\\":389,"&":685,"#":556,"%":963,"\u2022":350,"`":259,"^":600,"\u2190":1e3,"\u2192":1e3,"\u2191":722.66,"\u2193":722.66,"+":600,"\xD7":600,"<":600,"=":600,">":600,"|":222,"~":600,"\u25E6":608.4,"\u2610":830.08,"\u2611":830.08,"\u2713":764.16,$:556,0:556,1:556,2:556,3:556,4:556,5:556,6:556,7:556,8:556,9:556,a:574,A:685,b:611,B:722,c:556,C:741,d:611,D:741,e:574,E:667,\u00E9:574,f:352,F:593,g:611,G:759,h:611,H:741,i:259,I:296,j:259,J:556,k:556,K:722,l:259,L:574,m:907,M:907,n:611,N:741,o:593,O:778,p:611,P:667,q:611,Q:778,r:389,R:722,s:519,S:648,t:370,T:611,u:611,U:741,v:519,V:630,w:815,W:944,x:519,X:667,y:519,Y:648,z:500,Z:648}},italic:{k:{"\u2018\u2018":-95,"\u2019\u2019":-95,"\u2019s":-74,"\u2019t":-18,"A\u2019":-74,AT:-92,Av:-18,AV:-46,Aw:-18,AW:-24,Ay:-18,AY:-74,"F,":-157,"F.":-157,"f\u2019":18,FA:-55,ff:-15,fi:1,fl:1,"L\u2019":-74,LT:-92,LV:-92,LW:-55,Ly:-37,LY:-111,"P,":-179,"P.":-179,PA:-74,"r-":-55,"r,":-92,"r.":-92,rc:-18,rd:-18,re:-18,rg:-28,rn:18,ro:-18,rq:-18,RT:-18,RV:-18,RW:-18,RY:-37,"T-":-129,"T,":-111,"T;":-111,"T:":-111,"T.":-111,Ta:-111,TA:-92,Tc:-111,Te:-111,To:-111,Tr:-92,Ts:-111,Tu:-92,Tw:-111,Ty:-111,"V-":-55,"v,":-74,"V,":-129,"V;":-46,"V:":-46,"v.":-74,"V.":-129,Va:-55,VA:-46,Ve:-55,Vi:-18,Vo:-55,Vr:-37,Vu:-37,Vy:-18,"w,":-55,"W,":-74,"W;":-18,"W:":-18,"w.":-55,"W.":-74,Wa:-37,WA:-24,We:-57,Wo:-57,Wr:-18,Wu:-18,"Y-":-111,"y,":-74,"Y,":-139,"Y;":-92,"Y:":-92,"y.":-74,"Y.":-139,Ya:-92,YA:-74,Ye:-111,Yi:-37,Yo:-111,Yp:-74,Yq:-92,Yu:-74,Yv:-55},w:{" ":278,_:500,"-":389,"\u2013":500,"\u2014":1e3,",":278,";":278,":":278,"!":259,"?":556,".":278,"\u2026":1e3,"\xB7":278,"'":278,"\u2018":278,"\u2019":278,'"':426,"\u201C":426,"\u201D":426,"(":259,")":259,"[":259,"]":259,"{":333,"}":333,"@":800,"*":352,"/":333,"\\":333,"&":630,"#":556,"%":926,"\u2022":500,"`":222,"^":600,"\u2190":1e3,"\u2192":1e3,"\u2191":645.02,"\u2193":645.02,"+":600,"\xD7":600,"<":600,"=":600,">":600,"|":222,"~":600,"\u25E6":578.13,"\u2610":830.08,"\u2611":830.08,"\u2713":764.16,$:556,0:556,1:556,2:556,3:556,4:556,5:556,6:556,7:556,8:556,9:556,a:519,A:667,b:593,B:685,c:537,C:722,d:593,D:704,e:537,E:611,\u00E9:537,f:296,F:574,g:574,G:759,h:556,H:722,i:222,I:259,j:222,J:519,k:481,K:667,l:222,L:556,m:852,M:870,n:556,N:722,o:574,O:759,p:593,P:648,q:593,Q:759,r:333,R:685,s:481,S:648,t:315,T:574,u:556,U:722,v:481,V:611,w:759,W:926,x:481,X:611,y:481,Y:611,z:444,Z:611}},regular:{k:{"\u2018\u2018":-95,"\u2019\u2019":-95,"\u2019s":-74,"\u2019t":-18,"A\u2019":-74,AT:-92,Av:-18,AV:-46,Aw:-18,AW:-24,Ay:-18,AY:-74,"F,":-157,"F.":-157,"f\u2019":18,FA:-55,ff:-15,"L\u2019":-74,LT:-92,LV:-92,LW:-55,Ly:-37,LY:-111,"P,":-179,"P.":-179,PA:-74,"r-":-55,"r,":-92,"r.":-92,rc:-18,rd:-18,re:-18,rg:-28,rn:18,ro:-18,rq:-18,RT:-18,RV:-18,RW:-18,RY:-37,"T-":-129,"T,":-111,"T;":-111,"T:":-111,"T.":-111,Ta:-111,TA:-92,Tc:-111,Te:-111,To:-111,Tr:-92,Ts:-111,Tu:-92,Tw:-111,Ty:-111,"V-":-55,"v,":-74,"V,":-129,"V;":-46,"V:":-46,"v.":-74,"V.":-129,Va:-55,VA:-46,Ve:-55,Vi:-18,Vo:-55,Vr:-37,Vu:-37,Vy:-18,"w,":-55,"W,":-74,"W;":-18,"W:":-18,"w.":-55,"W.":-74,Wa:-37,WA:-24,We:-57,Wo:-57,Wr:-18,Wu:-18,"Y-":-111,"y,":-74,"Y,":-139,"Y;":-92,"Y:":-92,"y.":-74,"Y.":-139,Ya:-92,YA:-74,Ye:-111,Yi:-37,Yo:-111,Yp:-74,Yq:-92,Yu:-74,Yv:-55},w:{" ":278,_:500,"-":389,"\u2013":500,"\u2014":1e3,",":278,";":278,":":278,"!":259,"?":556,".":278,"\u2026":1e3,"\xB7":278,"'":278,"\u2018":278,"\u2019":278,'"':426,"\u201C":426,"\u201D":426,"(":259,")":259,"[":259,"]":259,"{":333,"}":333,"@":800,"*":352,"/":333,"\\":333,"&":630,"#":556,"%":1e3,"\u2022":350,"`":222,"^":600,"\u2190":1e3,"\u2192":1e3,"\u2191":645.02,"\u2193":645.02,"+":600,"\xD7":600,"<":600,"=":600,">":600,"|":222,"~":600,"\u25E6":578.13,"\u2610":830.08,"\u2611":830.08,"\u2713":764.16,$:556,0:556,1:556,2:556,3:556,4:556,5:556,6:556,7:556,8:556,9:556,a:537,A:648,b:593,B:685,c:537,C:722,d:593,D:704,e:537,E:611,\u00E9:537,f:296,F:574,g:574,G:759,h:556,H:722,i:222,I:259,j:222,J:519,k:519,K:667,l:222,L:556,m:853,M:871,n:556,N:722,o:574,O:760,p:593,P:648,q:593,Q:760,r:333,R:685,s:500,S:648,t:315,T:574,u:556,U:722,v:500,V:611,w:758,W:926,x:518,X:611,y:500,Y:648,z:480,Z:611}}},_t={"\u2190":[.966796875,.2109375,0],"\u2192":[.966796875,.2109375,0],"\u2191":[.966796875,.2109375,0],"\u2193":[.966796875,.2109375,0],"\u25E6":[.966796875,.2109375,0],"\u2610":[.66650390625,.25,.08349609375],"\u2611":[.66650390625,.25,.08349609375],"\u2713":[.966796875,.2109375,0]};function V(t){let e=n=>Math.round(Math.min(Math.max(n,0),1)*255).toString(16).padStart(2,"0");return`#${e(t.r)}${e(t.g)}${e(t.b)}`}function Ut(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Kt(t,e,n="regular"){let i=xt[n],r=i.w.n,o=0,s="";for(let l of t)o+=i.w[l]??r,s&&(o+=i.k[s+l]??0),s=l;return o*e/1e3}function bi(t){let e=Math.round(Ht*t),n=Math.round(jt*t),i=Math.round(qt*t);return[e+n,e+n+i]}function Me(t,e){return t===" "?!0:(t==="/"||t==="-")&&e!==""&&!/[\d\s/-]/.test(e)}function ki(t){let e=[...t],n=[],i="";return e.forEach((r,o)=>{i+=r;let s=e[o+1]??"";Me(r,s)&&s!==" "&&(n.push(i),i="")}),(i||n.length===0)&&n.push(i),n}function wi(t,e,n,i="regular"){let r=[];for(let o of t.split(/\r\n|\n|\r|\u2028|\u2029/)){let s="";for(let l of ki(o)){let a=s+l;for(s===""||Kt(a.trimEnd(),e,i)<=n?s=a:(r.push(s),s=l);Kt(s.trimEnd(),e,i)>n&&[...s.trimEnd()].length>1;){let c=[...s],u=c.length-1;for(;u>1&&Kt(c.slice(0,u).join(""),e,i)>n;)u--;r.push(c.slice(0,u).join("")),s=c.slice(u).join("")}}r.push(s)}return r}function Vn(t,e,n){if(t==="")return{width:0,height:0};let i=wi(t,e,n),r=Math.max(...i.map(l=>Kt(l.trimEnd(),e))),[o,s]=bi(e);return{width:Math.ceil(r-1e-9),height:Math.ceil(o+s*(i.length-1)-1e-9)}}var Dn=12,ct="\u2028";function Pi(t){let e=Math.min(Dn,t.width/2),n=Math.min(Dn,t.height/2);return{x:t.x+e,y:t.y+n,width:t.width-2*e,height:t.height-2*n}}var Ae=/^ {0,3}(#{1,6})(?:[ \t]+(.*?))?(?:[ \t]+#+)?[ \t]*$/,Cn=/^ {0,3}([-*_])(?:[ \t]*\1){2,}[ \t]*$/,Te=/^ {0,3}(`{3,}|~{3,})/,vt=/^( *)([-*+]|\d{1,9}[.)])([ \t]+|$)(.*)$/,Xt=/^ {0,3}> ?(.*)$/,Mi=/^ *\|? *:?-+:? *(\| *:?-+:? *)*\|? *$/;function Re(t){let e=t.trim();return e.startsWith("|")&&(e=e.slice(1)),e.endsWith("|")&&!e.endsWith("\\|")&&(e=e.slice(0,-1)),e.split(/(?<!\\)\|/).map(n=>n.trim())}function Ri(t){return $e(t.replace(/\r\n?/g,`
3
3
  `).split(`
4
4
  `))}function $e(t){let e=[],n=0,i=null,r=()=>{i&&(e.push({kind:"paragraph",inline:F(i.join(`
@@ -60,4 +60,4 @@ output { font-variant-numeric: tabular-nums; min-width: 4.5em; text-align: right
60
60
  <button class="next" aria-label="Next scene" hidden>\u23ED</button>
61
61
  <input class="scrub" type="range" min="0" max="0" value="0" aria-label="Frame">
62
62
  <output class="time">0.0s</output>
63
- </div>`;let n=i=>this.root.querySelector(i);this.stage=n(".stage"),this.bar=n(".bar"),this.playButton=n(".play"),this.scrub=n(".scrub"),this.time=n(".time"),this.sceneLabel=n(".scene"),this.prev=n(".prev"),this.next=n(".next"),this.playButton.onclick=()=>{let i=this.playback;i&&!this.playing&&i.scenes.length&&i.frame>=i.frameCount-1&&i.show(0),this.setPlaying(!this.playing)},this.scrub.oninput=()=>{this.setPlaying(!1),this.playback?.show(Number(this.scrub.value))},this.next.onclick=()=>this.nextScene(),this.prev.onclick=()=>this.prevScene(),this.addEventListener("keydown",i=>this.key(i))}connectedCallback(){this.hasAttribute("tabindex")||(this.tabIndex=0),"IntersectionObserver"in window&&(this.observer=new IntersectionObserver(n=>{this.visible=n.some(i=>i.isIntersecting),this.schedule()}),this.observer.observe(this)),this.load()}disconnectedCallback(){cancelAnimationFrame(this.raf),this.raf=0,this.observer?.disconnect()}attributeChangedCallback(n,i,r){i===r||!this.isConnected||(n==="src"?this.load():n==="label"?this.stage.setAttribute("aria-label",r??""):this.file&&this.mount())}async load(){let n=++this.loadToken;try{let i,r=this.getAttribute("src");if(r){let o=await fetch(r);if(!o.ok)throw new Error(`${r}: ${o.status} ${o.statusText}`);i=await o.text()}else{let o=this.querySelector('script[type="application/json"], script[type="application/vnd.dottie+json"]');if(!o)return;i=o.textContent??""}if(n!==this.loadToken)return;this.setFile(Rt(i))}catch(i){if(n!==this.loadToken)return;this.stage.innerHTML="";let r=document.createElement("div");r.className="error",r.textContent=`Couldn't show this drawing: ${i.message}`,this.stage.append(r),this.dispatchEvent(new CustomEvent("error",{detail:i}))}}setFile(n){this.file=n;for(let i of n.warnings)console.warn(`dottie-player: ${i}`);this.mount(),this.dispatchEvent(new CustomEvent("load",{detail:{file:n}}))}mount(){let n=this.file,i=n.pages[Lt(n,this.getAttribute("page"))],r=new It(i,this.stage,{clean:this.hasAttribute("clean")});this.playback=r,this.style.setProperty("--dottie-page",V(r.background)),this.style.setProperty("--dottie-ink",V(r.ink)),this.style.colorScheme=pt(r.background)<.5?"dark":"light",this.stage.setAttribute("aria-label",this.getAttribute("label")??i.name);let o=this.getAttribute("at"),s=i.timeline!==null&&r.frameCount>1&&o===null;this.bar.hidden=!s||this.hasAttribute("no-controls");let l=r.scenes.length>0;if(this.prev.hidden=this.next.hidden=this.sceneLabel.hidden=!l,this.scrub.max=String(r.frameCount-1),r.onFrame=c=>{if(this.scrub.value=String(c),this.time.textContent=`${r.plan.times[c].toFixed(1)}s`,l){let u=r.sceneAt(c);this.sceneLabel.textContent=u<0?"":r.sceneNames[u]}this.dispatchEvent(new CustomEvent("frame",{detail:{frame:c,time:r.plan.times[c]}}))},!s){r.drawAt(o!==null?Number(o)||0:it(i),o!==null?qn(r,Number(o)||0):r.posterFrame),this.setPlaying(!1);return}if(r.show(0),typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches){r.show(r.posterFrame),this.setPlaying(!1);return}this.setPlaying(!this.hasAttribute("no-autoplay")&&!r.scenes.includes(0))}get paused(){return!this.playing}get currentTime(){let n=this.playback;return n&&n.frame>=0?n.plan.times[n.frame]:0}set currentTime(n){let i=this.playback;i&&(this.setPlaying(!1),i.show(qn(i,n)))}get duration(){return this.playback?.page.timeline?.duration??0}play(){this.setPlaying(!0)}pause(){this.setPlaying(!1)}nextScene(){let n=this.playback;n&&(n.frame>=n.frameCount-1&&n.show(0),this.setPlaying(!0))}prevScene(){let n=this.playback;if(!n)return;let i=n.sceneAt(n.frame);i>=0&&n.scenes[i]===n.frame&&(i-=1),this.setPlaying(!1),n.show(i>=0?n.scenes[i]:0)}setPlaying(n){let i=this.playback;this.playing=n&&!!i&&i.frameCount>1&&i.page.timeline!==null,this.playButton.textContent=this.playing?"\u275A\u275A":"\u25B6",this.playButton.setAttribute("aria-label",this.playing?"Pause":"Play"),this.last=0,this.acc=0,this.schedule()}schedule(){this.playing&&this.visible&&!this.raf&&this.isConnected&&(this.raf=requestAnimationFrame(n=>this.tick(n)))}tick(n){this.raf=0;let i=this.playback;if(!this.playing||!i)return;let r=1e3/(i.page.timeline?.fps??12);this.last&&(this.acc+=Math.min(n-this.last,1e3)),this.last=n;let o=!1;for(;this.playing&&this.acc>=r;){this.acc-=r;let s=i.frame+1;if(s>=i.frameCount&&(i.scenes.length||this.hasAttribute("no-loop"))){this.setPlaying(!1);break}if(i.frame=s%i.frameCount,o=!0,i.scenes.includes(i.frame)){this.setPlaying(!1);break}}o&&i.show(i.frame),this.playing&&!this.visible&&(this.last=0),this.schedule()}key(n){let i=this.playback;if(!i||i.page.timeline===null||i.frameCount<=1||this.hasAttribute("at")||n.target===this.scrub&&n.key!==" ")return;let r=i.scenes.length>0,o=!0;n.key===" "?r?this.playing?this.setPlaying(!1):this.nextScene():this.setPlaying(!this.playing):n.key==="ArrowRight"?r?this.nextScene():(this.setPlaying(!1),i.show(i.frame+1)):n.key==="ArrowLeft"?r?this.prevScene():(this.setPlaying(!1),i.show(i.frame-1)):n.key==="."?(this.setPlaying(!1),i.show(i.frame+1)):n.key===","?(this.setPlaying(!1),i.show(i.frame-1)):o=!1,o&&n.preventDefault()}};b(wt,"observedAttributes",["src","page","at","clean","no-controls","label"]);function qn(t,e){let n=0;return t.plan.times.forEach((i,r)=>{Math.abs(i-e)<Math.abs(t.plan.times[n]-e)&&(n=r)}),n}function ne(t="dottie-player"){typeof customElements<"u"&&!customElements.get(t)&&customElements.define(t,class extends wt{})}var v=t=>{let e=ce(t*1e4)/1e4;return e===0?0:e},We=t=>[v(t.x),v(t.y)],Dt=t=>[v(t.x),v(t.y),v(t.width),v(t.height)],_n=t=>[v(t.r),v(t.g),v(t.b),v(t.a)];function Ye(t){let e=t.timeline;if(!e)return[0];let n=e.duration,i=[0,n];for(let r=1;r<16;r++)i.push(n*(r+.37)/16);for(let r of e.markers??[])i.push(r.t);return e.poster!==null&&i.push(e.poster),[...new Set(i.map(v))].filter(r=>r>=0&&r<=n).sort((r,o)=>r-o)}function ze(t,e){let n=W(t,e),i=tt(n),r=new Set(n.layers.filter(l=>l.visible).map(l=>l.id)),o={t:v(e)},s=t.timeline;if(s){let l=bt(s,e);o.view=l?Dt(l):null;let a=-1;(s.markers??[]).forEach((c,u)=>{c.t<=e&&(a=u)}),o.scene=a}return s?.camera?.length||(o.bounds=Dt(rt(n))),o.elements=At(n).map((l,a)=>{let c={id:l.id,kind:l.kind,z:a,seed:l.seed,frame:Dt({x:l.x,y:l.y,width:l.width,height:l.height})};if(r.has(l.layerID)||(c.hiddenLayer=!0),l.rotation!==null&&(c.rotation=v(l.rotation)),l.style.opacity!==null&&(c.opacity=v(l.style.opacity)),l.reveal!==null&&(c.reveal=v(l.reveal)),c.strokeWidth=v(l.style.strokeWidth),c.stroke=_n(l.style.stroke),c.fill=_n(l.style.fill),l.kind==="connector"){let[u,m]=st(l,i);c.start=We(u),c.end=We(m),c.route=Z(J(l,i)).map(We)}return c}),o}function Ce(t,e,n){let i={page:e+1,name:t.name,id:t.id},r=t.timeline;if(r){let o=kt(t);i.duration=v(r.duration),i.fps=v(r.fps),i.frameCount=o.times.length,i.poster=v(r.poster??r.duration),i.size=[v(o.size.width),v(o.size.height)],r.camera?.length||(i.bounds=Dt(o.rects[0])),i.markers=(r.markers??[]).map(s=>({t:v(s.t),name:s.name}))}else i.bounds=Dt(rt(t));return i.frames=n.map(o=>ze(t,o)),i}function Un(t,e){return{version:t.version,pages:t.pages.map((n,i)=>Ce(n,i,e??Ye(n)))}}function Ui(t,e={}){let n=Ki(t)?t:Rt(t),i=n.pages[Lt(n,e.page===void 0?null:String(e.page))];return i=W(i,e.at??it(i)),e.clean&&(i=Vt(i,0)),ee(i,e.background===void 0?{r:1,g:1,b:1,a:1}:e.background)}function Ki(t){return typeof t=="object"&&t!==null&&Array.isArray(t.pages)&&Array.isArray(t.warnings)}var Xi="0.1.0";ne();globalThis.Dottie??(globalThis.Dottie=Ge);})();
63
+ </div>`;let n=i=>this.root.querySelector(i);this.stage=n(".stage"),this.bar=n(".bar"),this.playButton=n(".play"),this.scrub=n(".scrub"),this.time=n(".time"),this.sceneLabel=n(".scene"),this.prev=n(".prev"),this.next=n(".next"),this.playButton.onclick=()=>{let i=this.playback;i&&!this.playing&&i.scenes.length&&i.frame>=i.frameCount-1&&i.show(0),this.setPlaying(!this.playing)},this.scrub.oninput=()=>{this.setPlaying(!1),this.playback?.show(Number(this.scrub.value))},this.next.onclick=()=>this.nextScene(),this.prev.onclick=()=>this.prevScene(),this.addEventListener("keydown",i=>this.key(i))}connectedCallback(){this.hasAttribute("tabindex")||(this.tabIndex=0),"IntersectionObserver"in window&&(this.observer=new IntersectionObserver(n=>{this.visible=n.some(i=>i.isIntersecting),this.schedule()}),this.observer.observe(this)),this.load()}disconnectedCallback(){cancelAnimationFrame(this.raf),this.raf=0,this.observer?.disconnect()}attributeChangedCallback(n,i,r){i===r||!this.isConnected||(n==="src"?this.load():n==="label"?this.stage.setAttribute("aria-label",r??""):this.file&&this.mount())}async load(){let n=++this.loadToken;try{let i,r=this.getAttribute("src");if(r){let o=await fetch(r);if(!o.ok)throw new Error(`${r}: ${o.status} ${o.statusText}`);i=await o.text()}else{let o=this.querySelector('script[type="application/json"], script[type="application/vnd.dottie+json"]');if(!o)return;i=o.textContent??""}if(n!==this.loadToken)return;this.setFile(Rt(i))}catch(i){if(n!==this.loadToken)return;this.stage.innerHTML="";let r=document.createElement("div");r.className="error",r.textContent=`Couldn't show this drawing: ${i.message}`,this.stage.append(r),this.dispatchEvent(new CustomEvent("error",{detail:i}))}}setFile(n){this.file=n;for(let i of n.warnings)console.warn(`dottie-player: ${i}`);this.mount(),this.dispatchEvent(new CustomEvent("load",{detail:{file:n}}))}mount(){let n=this.file,i=n.pages[Lt(n,this.getAttribute("page"))],r=new It(i,this.stage,{clean:this.hasAttribute("clean")});this.playback=r,this.style.setProperty("--dottie-page",V(r.background)),this.style.setProperty("--dottie-ink",V(r.ink)),this.style.colorScheme=pt(r.background)<.5?"dark":"light",this.stage.setAttribute("aria-label",this.getAttribute("label")??i.name);let o=this.getAttribute("at"),s=i.timeline!==null&&r.frameCount>1&&o===null;this.bar.hidden=!s||this.hasAttribute("no-controls");let l=r.scenes.length>0;if(this.prev.hidden=this.next.hidden=this.sceneLabel.hidden=!l,this.scrub.max=String(r.frameCount-1),r.onFrame=c=>{if(this.scrub.value=String(c),this.time.textContent=`${r.plan.times[c].toFixed(1)}s`,l){let u=r.sceneAt(c);this.sceneLabel.textContent=u<0?"":r.sceneNames[u]}this.dispatchEvent(new CustomEvent("frame",{detail:{frame:c,time:r.plan.times[c]}}))},!s){r.drawAt(o!==null?Number(o)||0:it(i),o!==null?qn(r,Number(o)||0):r.posterFrame),this.setPlaying(!1);return}if(r.show(0),typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches){r.show(r.posterFrame),this.setPlaying(!1);return}this.setPlaying(!this.hasAttribute("no-autoplay")&&!r.scenes.includes(0))}get paused(){return!this.playing}get currentTime(){let n=this.playback;return n&&n.frame>=0?n.plan.times[n.frame]:0}set currentTime(n){let i=this.playback;i&&(this.setPlaying(!1),i.show(qn(i,n)))}get duration(){return this.playback?.page.timeline?.duration??0}play(){this.setPlaying(!0)}pause(){this.setPlaying(!1)}nextScene(){let n=this.playback;n&&(n.frame>=n.frameCount-1&&n.show(0),this.setPlaying(!0))}prevScene(){let n=this.playback;if(!n)return;let i=n.sceneAt(n.frame);i>=0&&n.scenes[i]===n.frame&&(i-=1),this.setPlaying(!1),n.show(i>=0?n.scenes[i]:0)}setPlaying(n){let i=this.playback;this.playing=n&&!!i&&i.frameCount>1&&i.page.timeline!==null,this.playButton.textContent=this.playing?"\u275A\u275A":"\u25B6",this.playButton.setAttribute("aria-label",this.playing?"Pause":"Play"),this.last=0,this.acc=0,this.schedule()}schedule(){this.playing&&this.visible&&!this.raf&&this.isConnected&&(this.raf=requestAnimationFrame(n=>this.tick(n)))}tick(n){this.raf=0;let i=this.playback;if(!this.playing||!i)return;let r=1e3/(i.page.timeline?.fps??12);this.last&&(this.acc+=Math.min(n-this.last,1e3)),this.last=n;let o=!1;for(;this.playing&&this.acc>=r;){this.acc-=r;let s=i.frame+1;if(s>=i.frameCount&&(i.scenes.length||this.hasAttribute("no-loop"))){this.setPlaying(!1);break}if(i.frame=s%i.frameCount,o=!0,i.scenes.includes(i.frame)){this.setPlaying(!1);break}}o&&i.show(i.frame),this.playing&&!this.visible&&(this.last=0),this.schedule()}key(n){let i=this.playback;if(!i||i.page.timeline===null||i.frameCount<=1||this.hasAttribute("at")||n.target===this.scrub&&n.key!==" ")return;let r=i.scenes.length>0,o=!0;n.key===" "?r?this.playing?this.setPlaying(!1):this.nextScene():this.setPlaying(!this.playing):n.key==="ArrowRight"?r?this.nextScene():(this.setPlaying(!1),i.show(i.frame+1)):n.key==="ArrowLeft"?r?this.prevScene():(this.setPlaying(!1),i.show(i.frame-1)):n.key==="."?(this.setPlaying(!1),i.show(i.frame+1)):n.key===","?(this.setPlaying(!1),i.show(i.frame-1)):o=!1,o&&n.preventDefault()}};b(wt,"observedAttributes",["src","page","at","clean","no-controls","label"]);function qn(t,e){let n=0;return t.plan.times.forEach((i,r)=>{Math.abs(i-e)<Math.abs(t.plan.times[n]-e)&&(n=r)}),n}function ne(t="dottie-player"){typeof customElements<"u"&&!customElements.get(t)&&customElements.define(t,class extends wt{})}var v=t=>{let e=ce(t*1e4)/1e4;return e===0?0:e},We=t=>[v(t.x),v(t.y)],Dt=t=>[v(t.x),v(t.y),v(t.width),v(t.height)],_n=t=>[v(t.r),v(t.g),v(t.b),v(t.a)];function Ye(t){let e=t.timeline;if(!e)return[0];let n=e.duration,i=[0,n];for(let r=1;r<16;r++)i.push(n*(r+.37)/16);for(let r of e.markers??[])i.push(r.t);return e.poster!==null&&i.push(e.poster),[...new Set(i.map(v))].filter(r=>r>=0&&r<=n).sort((r,o)=>r-o)}function ze(t,e){let n=W(t,e),i=tt(n),r=new Set(n.layers.filter(l=>l.visible).map(l=>l.id)),o={t:v(e)},s=t.timeline;if(s){let l=bt(s,e);o.view=l?Dt(l):null;let a=-1;(s.markers??[]).forEach((c,u)=>{c.t<=e&&(a=u)}),o.scene=a}return s?.camera?.length||(o.bounds=Dt(rt(n))),o.elements=At(n).map((l,a)=>{let c={id:l.id,kind:l.kind,z:a,seed:l.seed,frame:Dt({x:l.x,y:l.y,width:l.width,height:l.height})};if(r.has(l.layerID)||(c.hiddenLayer=!0),l.rotation!==null&&(c.rotation=v(l.rotation)),l.style.opacity!==null&&(c.opacity=v(l.style.opacity)),l.reveal!==null&&(c.reveal=v(l.reveal)),c.strokeWidth=v(l.style.strokeWidth),c.stroke=_n(l.style.stroke),c.fill=_n(l.style.fill),l.kind==="connector"){let[u,m]=st(l,i);c.start=We(u),c.end=We(m),c.route=Z(J(l,i)).map(We)}return c}),o}function Ce(t,e,n){let i={page:e+1,name:t.name,id:t.id},r=t.timeline;if(r){let o=kt(t);i.duration=v(r.duration),i.fps=v(r.fps),i.frameCount=o.times.length,i.poster=v(r.poster??r.duration),i.size=[v(o.size.width),v(o.size.height)],r.camera?.length||(i.bounds=Dt(o.rects[0])),i.markers=(r.markers??[]).map(s=>({t:v(s.t),name:s.name}))}else i.bounds=Dt(rt(t));return i.frames=n.map(o=>ze(t,o)),i}function Un(t,e){return{version:t.version,pages:t.pages.map((n,i)=>Ce(n,i,e??Ye(n)))}}function Ui(t,e={}){let n=Ki(t)?t:Rt(t),i=n.pages[Lt(n,e.page===void 0?null:String(e.page))];return i=W(i,e.at??it(i)),e.clean&&(i=Vt(i,0)),ee(i,e.background===void 0?{r:1,g:1,b:1,a:1}:e.background)}function Ki(t){return typeof t=="object"&&t!==null&&Array.isArray(t.pages)&&Array.isArray(t.warnings)}var Xi="0.2.0";ne();globalThis.Dottie??(globalThis.Dottie=Ge);})();
package/dist/dottie.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! dottie.js 0.1.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
1
+ /*! dottie.js 0.2.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);
@@ -2969,7 +2969,7 @@ function render(source, options = {}) {
2969
2969
  function isFile(v) {
2970
2970
  return typeof v === "object" && v !== null && Array.isArray(v.pages) && Array.isArray(v.warnings);
2971
2971
  }
2972
- var VERSION = "0.1.0";
2972
+ var VERSION = "0.2.0";
2973
2973
  export {
2974
2974
  BOIL_DRAWINGS,
2975
2975
  DottieFormatError,
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ /** What follows the runtime; DOTTIE_DATA and DOTTIE_OPTIONS are replaced with JSON. Plain ES5, unminified. */
2
+ export declare const LOADER = ";(function () {\n var data = DOTTIE_DATA, options = DOTTIE_OPTIONS;\n var script = document.currentScript;\n function attr(el, name, value) { el.setAttribute(name.replace(/[A-Z]/g, function (c) { return \"-\" + c.toLowerCase(); }), value); }\n function mount() {\n var player = document.createElement(\"dottie-player\"), k;\n for (k in options) attr(player, k, options[k]);\n if (script) for (k in script.dataset) if (k !== \"target\") attr(player, k, script.dataset[k]);\n var target = script && script.dataset.target ? document.querySelector(script.dataset.target) : null;\n if (target) target.appendChild(player);\n else if (script && script.parentNode && script.parentNode !== document.head) script.parentNode.insertBefore(player, script);\n else document.body.appendChild(player);\n player.setFile(window.Dottie.parseDottie(data));\n }\n if (document.readyState === \"loading\" && (!script || script.parentNode === document.head || script.dataset.target)) {\n document.addEventListener(\"DOMContentLoaded\", mount);\n } else {\n mount();\n }\n})();\n";
3
+ export interface CompileOptions {
4
+ /** Page name or 1-based number; only that page goes in. The first page by default. */
5
+ page?: string | number;
6
+ /** Show this moment (seconds) as a still. */
7
+ at?: number;
8
+ clean?: boolean;
9
+ noControls?: boolean;
10
+ noAutoplay?: boolean;
11
+ noLoop?: boolean;
12
+ label?: string;
13
+ }
14
+ /** JSON safe inside a script: it can't close the tag or start a comment. */
15
+ export declare function scriptJSON(value: unknown): string;
16
+ /** The player's attributes for `options`, as both compilers write them. */
17
+ export declare function playerAttributes(options: CompileOptions): Record<string, string>;
18
+ /** The compiled script for `source` (a .dottie file's text or parsed JSON). `runtime` is dottie-player.min.js. */
19
+ export declare function compile(source: string | unknown, runtime: string, options?: CompileOptions): string;
@@ -18,4 +18,4 @@ export interface RenderOptions {
18
18
  /** One page of a drawing as an SVG document, as Dottie's own SVG export draws it. */
19
19
  export declare function render(source: string | DottieFile | unknown, options?: RenderOptions): string;
20
20
  /** The library's version. */
21
- export declare const VERSION = "0.1.0";
21
+ export declare const VERSION = "0.2.0";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@phin-tech/dottie",
3
- "version": "0.1.0",
4
- "description": "Render and play Dottie (.dottie) drawings and animations in the browser",
3
+ "version": "0.2.0",
4
+ "description": "Render, play and compile Dottie (.dottie) drawings and animations in the browser",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -21,6 +21,10 @@
21
21
  "./player": {
22
22
  "types": "./dist/types/player-entry.d.ts",
23
23
  "default": "./dist/dottie-player.js"
24
+ },
25
+ "./compile": {
26
+ "types": "./dist/types/compile.d.ts",
27
+ "default": "./dist/compile.js"
24
28
  }
25
29
  },
26
30
  "files": [
@@ -64,5 +68,8 @@
64
68
  ],
65
69
  "engines": {
66
70
  "node": ">=18"
71
+ },
72
+ "bin": {
73
+ "dottie-compile": "dist/dottie-compile.mjs"
67
74
  }
68
75
  }