-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathoptions.ts
More file actions
107 lines (97 loc) · 4 KB
/
Copy pathoptions.ts
File metadata and controls
107 lines (97 loc) · 4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/**
* Turn a `<posecode-player>`'s attributes into a validated options object.
*
* Pure and DOM-free so it is unit-testable in node: the element hands us a
* plain record of its attribute values. Boolean attributes follow a friendly
* variant of HTML semantics: present means true, but an explicit `"false"`
* (or `"0"` / `"no"`) turns them off, which reads better in hand-written embeds.
*/
export interface PlayerOptions {
/** Start playing as soon as the movement loads. */
autoplay: boolean;
/** Loop the timeline. */
loop: boolean;
/** Show the playback control bar. */
controls: boolean;
/** Slowly orbit the camera when idle. */
autoRotate: boolean;
/** Playback speed multiplier (0.1–4). */
speed: number;
/**
* Realistic skinned figure pinned to one GLB URL, from an explicit
* `character="<url>"` attribute. `""` when the attribute is absent (rig
* picks the character from `characterUrls` instead) or the character is
* disabled. Load failures fall back to the procedural figure, so an offline
* page degrades instead of blanking.
*/
characterUrl: string;
/** True when `character="off"` (or another falsey word) explicitly disables any skinned character. */
characterDisabled: boolean;
/**
* Rig name (the loaded document's `rig` directive) → character GLB URL,
* applied when `characterUrl` is unset and the character isn't disabled.
* Defaults to the hosted characters for every built-in rig name.
*/
characterUrls: Record<string, string>;
}
/** The character the hosted playground uses, served from the same origin. */
export const DEFAULT_CHARACTER_URL = "https://posecode.org/models/xbot.glb";
/** Hosted character per built-in rig name, keyed by posecode-parser's RigName. */
export const DEFAULT_CHARACTER_URLS: Record<string, string> = {
humanoid: DEFAULT_CHARACTER_URL,
avatar1: "https://posecode.org/models/avatar1.glb",
avatar2: "https://posecode.org/models/avatar2.glb",
avatar3: "https://posecode.org/models/avatar3.glb",
};
export const DEFAULT_OPTIONS: PlayerOptions = {
autoplay: true,
loop: true,
controls: true,
autoRotate: true,
speed: 1,
characterUrl: "",
characterDisabled: false,
characterUrls: DEFAULT_CHARACTER_URLS,
};
const SPEED_MIN = 0.1;
const SPEED_MAX = 4;
/** Attribute values as read from the element (null = attribute absent). */
export interface RawAttributes {
autoplay?: string | null;
loop?: string | null;
controls?: string | null;
autorotate?: string | null;
speed?: string | null;
character?: string | null;
}
const FALSEY = new Set(["false", "0", "no", "off"]);
/** Present attribute is true unless its value is an explicit falsey word. */
function boolAttr(value: string | null | undefined, fallback: boolean): boolean {
if (value === undefined || value === null) return fallback;
return !FALSEY.has(value.trim().toLowerCase());
}
function clamp(n: number, lo: number, hi: number): number {
return Math.min(hi, Math.max(lo, n));
}
export function parseOptions(attrs: RawAttributes): PlayerOptions {
const speedRaw = attrs.speed != null ? Number(attrs.speed) : NaN;
// `character` accepts a GLB URL (pinned regardless of the document's rig),
// a falsey word to disable any skinned character, or absent to let each
// loaded document's `rig` directive pick from characterUrls.
const characterRaw = attrs.character?.trim();
const characterDisabled =
characterRaw !== undefined && characterRaw !== null && FALSEY.has(characterRaw.toLowerCase());
const characterUrl = characterRaw && !characterDisabled ? characterRaw : "";
return {
autoplay: boolAttr(attrs.autoplay, DEFAULT_OPTIONS.autoplay),
loop: boolAttr(attrs.loop, DEFAULT_OPTIONS.loop),
controls: boolAttr(attrs.controls, DEFAULT_OPTIONS.controls),
autoRotate: boolAttr(attrs.autorotate, DEFAULT_OPTIONS.autoRotate),
speed: Number.isFinite(speedRaw)
? clamp(speedRaw, SPEED_MIN, SPEED_MAX)
: DEFAULT_OPTIONS.speed,
characterUrl,
characterDisabled,
characterUrls: DEFAULT_OPTIONS.characterUrls,
};
}