@px-petals/roku.motion-web 1.0.0 → 1.1.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/bands/bands.js +3 -1
- package/bundle/bundle.js +56 -0
- package/list/list.js +12 -1
- package/lottie/lottie.layers.js +64 -11
- package/lottie/lottie.paint.js +30 -0
- package/lottie/lottie.paths.js +19 -4
- package/lottie/lottie.props.js +18 -8
- package/lottie/lottie.shapes.js +2 -1
- package/mixer/mixer.js +31 -8
- package/mixer/mixer.mix.js +17 -0
- package/motion/motion.convert.js +32 -1
- package/motion/motion.flow.js +20 -2
- package/motion/motion.machine.js +28 -13
- package/motion/motion.values.js +13 -9
- package/motion.js +196 -20
- package/package.json +10 -16
- package/player/player.js +25 -5
- package/readme.md +78 -3
- package/script/luau/lib/base/base.js +202 -0
- package/script/luau/lib/base/base.table.js +189 -0
- package/script/luau/lib/lib.args.js +152 -0
- package/script/luau/lib/lib.js +23 -0
- package/script/luau/lib/math/math.bit32.js +144 -0
- package/script/luau/lib/math/math.js +288 -0
- package/script/luau/lib/math/math.vector.js +125 -0
- package/script/luau/lib/string/string.format.js +143 -0
- package/script/luau/lib/string/string.js +160 -0
- package/script/luau/lib/string/string.pattern.js +505 -0
- package/script/luau/load/load.js +362 -0
- package/script/luau/load/load.ops.js +79 -0
- package/script/luau/luau.convert.js +259 -0
- package/script/luau/luau.js +132 -0
- package/script/luau/luau.md +111 -0
- package/script/luau/vm/vm.js +167 -0
- package/script/luau/vm/vm.meta.js +401 -0
- package/script/luau/vm/vm.num.js +236 -0
- package/script/luau/vm/vm.run.js +842 -0
- package/script/luau/vm/vm.value.js +406 -0
- package/script/play/play.js +199 -0
- package/script/play/play.objects.js +295 -0
- package/script/play/play.scope.js +91 -0
- package/script/rive/api/api.statics.js +31 -0
- package/script/rive/api/api.type.js +188 -0
- package/script/rive/api/artboard/artboard.js +89 -0
- package/script/rive/api/assets/assets.js +72 -0
- package/script/rive/api/data/data.value.js +99 -0
- package/script/rive/api/draw/draw.contours.js +163 -0
- package/script/rive/api/draw/draw.measure.js +196 -0
- package/script/rive/api/draw/draw.paint.js +235 -0
- package/script/rive/api/draw/draw.path.js +153 -0
- package/script/rive/api/draw/draw.renderer.js +117 -0
- package/script/rive/api/math/math.color.js +96 -0
- package/script/rive/api/math/math.mat2d.js +180 -0
- package/script/rive/api/math/math.vector.js +121 -0
- package/script/rive/api/media/media.audio.js +113 -0
- package/script/rive/api/media/media.draw.js +73 -0
- package/script/rive/api/media/media.image.js +61 -0
- package/script/rive/api/media/media.mesh.js +50 -0
- package/script/rive/api/pointer/pointer.event.js +64 -0
- package/script/rive/api/pointer/pointer.route.js +69 -0
- package/script/rive/api/text/text.font.js +129 -0
- package/script/rive/api/text/text.js +278 -0
- package/script/rive/api/text/text.layout.js +259 -0
- package/script/rive/context/context.js +76 -0
- package/script/rive/context/context.memory.js +196 -0
- package/script/rive/context/context.viewmodel.js +302 -0
- package/script/rive/context/lists/lists.js +160 -0
- package/script/rive/context/lists/lists.values.js +281 -0
- package/script/rive/host/host.globals.js +44 -0
- package/script/rive/host/host.js +533 -0
- package/script/rive/host/host.node.js +45 -0
- package/script/rive/rive.js +23 -0
- package/script/rive/rive.md +180 -0
- package/script/script.js +11 -0
- package/skin/skin.deform.js +514 -0
- package/skin/skin.ik.js +260 -0
- package/skin/skin.js +297 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lua values. They are plain JS values wherever one fits:
|
|
3
|
+
*
|
|
4
|
+
* | Lua | JS |
|
|
5
|
+
* | -------- | -------------------------------------------- |
|
|
6
|
+
* | nil | `undefined` |
|
|
7
|
+
* | boolean | `true`, `false` |
|
|
8
|
+
* | number | a double |
|
|
9
|
+
* | string | a byte string: one char per byte, 0 to 255 |
|
|
10
|
+
* | vector | `Vector` (immutable, three components) |
|
|
11
|
+
* | table | `LuaTable` |
|
|
12
|
+
* | function | `LuaClosure` (bytecode) or `NativeFunction` |
|
|
13
|
+
*
|
|
14
|
+
* A JS array is never a Lua value, so a native function can return one to
|
|
15
|
+
* mean several results.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Values
|
|
19
|
+
// ■■■■■■
|
|
20
|
+
|
|
21
|
+
/** Where the next object id comes from; ids key tables by identity and name them in `tostring` */
|
|
22
|
+
let lastId = 0
|
|
23
|
+
|
|
24
|
+
/** @returns {number} */
|
|
25
|
+
export function newId() {
|
|
26
|
+
return ++lastId
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const f32 = new Float32Array(1)
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A number rounded to single precision, which is what a vector component holds.
|
|
33
|
+
* (`Math.fround` is newer than the oldest browsers this runs in.)
|
|
34
|
+
*
|
|
35
|
+
* @param {number} x
|
|
36
|
+
* @returns {number}
|
|
37
|
+
*/
|
|
38
|
+
export function fround(x) {
|
|
39
|
+
f32[0] = x
|
|
40
|
+
return f32[0]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** A three-component vector value, single precision per component; it compares and keys by components */
|
|
44
|
+
export class Vector {
|
|
45
|
+
/**
|
|
46
|
+
* @param {number} x
|
|
47
|
+
* @param {number} y
|
|
48
|
+
* @param {number} z
|
|
49
|
+
*/
|
|
50
|
+
constructor(x, y, z) {
|
|
51
|
+
this.x = fround(x)
|
|
52
|
+
this.y = fround(y)
|
|
53
|
+
this.z = fround(z)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The results of a function that returns nothing. Never mutate it. */
|
|
58
|
+
export const EMPTY = /** @type {any[]} */ ([])
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* A host function. It gets the VM and its arguments as an array and returns
|
|
62
|
+
* an array of results, or any other value for exactly one result.
|
|
63
|
+
*
|
|
64
|
+
* @typedef {(vm: any, args: any[]) => any} NativeCall
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
export class NativeFunction {
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} name
|
|
70
|
+
* @param {NativeCall} fn
|
|
71
|
+
*/
|
|
72
|
+
constructor(name, fn) {
|
|
73
|
+
this.name = name
|
|
74
|
+
this.fn = fn
|
|
75
|
+
this.id = 0
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** A variable a closure captured: on the VM's stack while its function runs, in `v` once that returns */
|
|
80
|
+
export class Upvalue {
|
|
81
|
+
/**
|
|
82
|
+
* @param {any[]} stack
|
|
83
|
+
* @param {number} index
|
|
84
|
+
*/
|
|
85
|
+
constructor(stack, index) {
|
|
86
|
+
/** @type {any[] | null} */
|
|
87
|
+
this.s = stack
|
|
88
|
+
this.i = index
|
|
89
|
+
this.v = undefined
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** A function made from a proto and the upvalues it captured */
|
|
94
|
+
export class LuaClosure {
|
|
95
|
+
/**
|
|
96
|
+
* @param {import('../load/load.js').Proto} proto
|
|
97
|
+
* @param {Upvalue[]} upvals
|
|
98
|
+
* @param {LuaTable} env the globals
|
|
99
|
+
*/
|
|
100
|
+
constructor(proto, upvals, env) {
|
|
101
|
+
this.proto = proto
|
|
102
|
+
this.upvals = upvals
|
|
103
|
+
this.env = env
|
|
104
|
+
this.id = 0
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** A thrown Lua error: the error value, and how many Lua frames up its position is taken from (0: none) */
|
|
109
|
+
export class LuaThrow {
|
|
110
|
+
/**
|
|
111
|
+
* @param {any} value
|
|
112
|
+
* @param {number} [level]
|
|
113
|
+
*/
|
|
114
|
+
constructor(value, level) {
|
|
115
|
+
this.value = value
|
|
116
|
+
this.level = level === undefined ? 0 : level
|
|
117
|
+
/** The source line the error was raised at, 0 when unknown */
|
|
118
|
+
this.line = 0
|
|
119
|
+
/** The chunk that line is in */
|
|
120
|
+
this.chunk = ''
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* An error to throw from a native function or the VM: a string error gets the
|
|
126
|
+
* position of the Lua code that was running, `level` frames up.
|
|
127
|
+
*
|
|
128
|
+
* @param {any} value
|
|
129
|
+
* @param {number} [level]
|
|
130
|
+
* @returns {LuaThrow}
|
|
131
|
+
*/
|
|
132
|
+
export function fail(value, level) {
|
|
133
|
+
return new LuaThrow(value, level === undefined ? 1 : level)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @param {any} v
|
|
138
|
+
* @returns {string} Lua's `type` of v
|
|
139
|
+
*/
|
|
140
|
+
export function typeName(v) {
|
|
141
|
+
switch (typeof v) {
|
|
142
|
+
case 'undefined': return 'nil'
|
|
143
|
+
case 'boolean': return 'boolean'
|
|
144
|
+
case 'number': return 'number'
|
|
145
|
+
case 'string': return 'string'
|
|
146
|
+
default: break
|
|
147
|
+
}
|
|
148
|
+
if (v instanceof LuaTable) return 'table'
|
|
149
|
+
if (v instanceof Vector) return 'vector'
|
|
150
|
+
return 'function'
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Tables
|
|
154
|
+
// ■■■■■■
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* A Lua table. Keys 1..n are the `arr` array (kept free of trailing nils, so
|
|
158
|
+
* `arr.length` is a border); every other key is in one of three dictionaries
|
|
159
|
+
* (strings, numbers, everything else) that map a key to a slot in the `hk`
|
|
160
|
+
* and `hv` arrays. Slots keep their insertion order, which is the traversal
|
|
161
|
+
* order, and a removed key keeps its slot with a nil value until the next
|
|
162
|
+
* rebuild, so assigning nil during a traversal is safe.
|
|
163
|
+
*/
|
|
164
|
+
export class LuaTable {
|
|
165
|
+
constructor() {
|
|
166
|
+
/** @type {any[]} */
|
|
167
|
+
this.arr = []
|
|
168
|
+
/** @type {Record<string, number> | null} */
|
|
169
|
+
this.hs = null
|
|
170
|
+
/** @type {Record<string, number> | null} */
|
|
171
|
+
this.hn = null
|
|
172
|
+
/** @type {Record<string, number> | null} */
|
|
173
|
+
this.ho = null
|
|
174
|
+
/** @type {any[]} */
|
|
175
|
+
this.hk = []
|
|
176
|
+
/** @type {any[]} */
|
|
177
|
+
this.hv = []
|
|
178
|
+
this.dead = 0
|
|
179
|
+
/** the key and value `advance` found */
|
|
180
|
+
this.ck = undefined
|
|
181
|
+
this.cv = undefined
|
|
182
|
+
/** @type {LuaTable | undefined} */
|
|
183
|
+
this.meta = undefined
|
|
184
|
+
this.frozen = false
|
|
185
|
+
this.id = 0
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @param {string} key
|
|
190
|
+
* @returns {any}
|
|
191
|
+
*/
|
|
192
|
+
getStr(key) {
|
|
193
|
+
const hs = this.hs
|
|
194
|
+
if (hs === null) return undefined
|
|
195
|
+
const slot = hs[key]
|
|
196
|
+
return slot === undefined ? undefined : this.hv[slot]
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @param {any} key
|
|
201
|
+
* @returns {any}
|
|
202
|
+
*/
|
|
203
|
+
get(key) {
|
|
204
|
+
if (typeof key === 'string') return this.getStr(key)
|
|
205
|
+
if (typeof key === 'number') {
|
|
206
|
+
const arr = this.arr
|
|
207
|
+
if (key >= 1 && key <= arr.length && Math.floor(key) === key) return arr[key - 1]
|
|
208
|
+
const hn = this.hn
|
|
209
|
+
if (hn === null) return undefined
|
|
210
|
+
const slot = hn[key]
|
|
211
|
+
return slot === undefined ? undefined : this.hv[slot]
|
|
212
|
+
}
|
|
213
|
+
const slot = this.slotOf(key)
|
|
214
|
+
return slot === undefined ? undefined : this.hv[slot]
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* The slot a key holds in the hash part, or undefined
|
|
219
|
+
*
|
|
220
|
+
* @param {any} key
|
|
221
|
+
* @returns {number | undefined}
|
|
222
|
+
*/
|
|
223
|
+
slotOf(key) {
|
|
224
|
+
if (typeof key === 'string') return this.hs === null ? undefined : this.hs[key]
|
|
225
|
+
if (typeof key === 'number') return this.hn === null ? undefined : this.hn[key]
|
|
226
|
+
return this.ho === null ? undefined : this.ho[objectKey(key)]
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Sets a key without looking at the metatable. The key must not be nil or NaN.
|
|
231
|
+
*
|
|
232
|
+
* @param {any} key
|
|
233
|
+
* @param {any} value
|
|
234
|
+
*/
|
|
235
|
+
set(key, value) {
|
|
236
|
+
if (typeof key === 'number') {
|
|
237
|
+
const arr = this.arr
|
|
238
|
+
const n = arr.length
|
|
239
|
+
if (key >= 1 && key <= n + 1 && Math.floor(key) === key) {
|
|
240
|
+
if (key <= n) {
|
|
241
|
+
arr[key - 1] = value
|
|
242
|
+
if (value === undefined && key === n) {
|
|
243
|
+
let m = n - 1
|
|
244
|
+
while (m > 0 && arr[m - 1] === undefined) m--
|
|
245
|
+
arr.length = m
|
|
246
|
+
}
|
|
247
|
+
} else if (value !== undefined) {
|
|
248
|
+
arr.push(value)
|
|
249
|
+
if (this.hn !== null) this.migrate()
|
|
250
|
+
}
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
let slot = this.slotOf(key)
|
|
255
|
+
if (slot !== undefined) {
|
|
256
|
+
const old = this.hv[slot]
|
|
257
|
+
this.hv[slot] = value
|
|
258
|
+
if (value === undefined && old !== undefined) this.dead++
|
|
259
|
+
else if (old === undefined && value !== undefined) this.dead--
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
if (value === undefined) return
|
|
263
|
+
if (this.dead > 16 && this.dead * 2 > this.hk.length) this.rebuild()
|
|
264
|
+
slot = this.hk.length
|
|
265
|
+
this.hk.push(key)
|
|
266
|
+
this.hv.push(value)
|
|
267
|
+
if (typeof key === 'string') {
|
|
268
|
+
const hs = this.hs === null ? (this.hs = Object.create(null)) : this.hs
|
|
269
|
+
hs[key] = slot
|
|
270
|
+
} else if (typeof key === 'number') {
|
|
271
|
+
const hn = this.hn === null ? (this.hn = Object.create(null)) : this.hn
|
|
272
|
+
hn[key] = slot
|
|
273
|
+
} else {
|
|
274
|
+
const ho = this.ho === null ? (this.ho = Object.create(null)) : this.ho
|
|
275
|
+
ho[objectKey(key)] = slot
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** After the array part grew, moves the keys that now continue it out of the hash part */
|
|
280
|
+
migrate() {
|
|
281
|
+
const hn = /** @type {Record<string, number>} */ (this.hn)
|
|
282
|
+
for (;;) {
|
|
283
|
+
const slot = hn[this.arr.length + 1]
|
|
284
|
+
if (slot === undefined || this.hv[slot] === undefined) return
|
|
285
|
+
this.arr.push(this.hv[slot])
|
|
286
|
+
this.hv[slot] = undefined
|
|
287
|
+
this.dead++
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Drops the slots whose values are nil */
|
|
292
|
+
rebuild() {
|
|
293
|
+
const keys = this.hk
|
|
294
|
+
const values = this.hv
|
|
295
|
+
this.hk = []
|
|
296
|
+
this.hv = []
|
|
297
|
+
this.hs = null
|
|
298
|
+
this.hn = null
|
|
299
|
+
this.ho = null
|
|
300
|
+
this.dead = 0
|
|
301
|
+
for (let i = 0; i < keys.length; i++) if (values[i] !== undefined) this.set(keys[i], values[i])
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** @returns {number} a border: the length `#` gives */
|
|
305
|
+
length() {
|
|
306
|
+
return this.arr.length
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* One step of a traversal. A position is an array index (0 and up), or -1
|
|
311
|
+
* and down for hash slot 0 and up; it survives assigning nil to entries.
|
|
312
|
+
* Finds the entry at or after `pos`, leaves its key and value in `ck` and
|
|
313
|
+
* `cv`, and returns the position after it; null when there are no more.
|
|
314
|
+
*
|
|
315
|
+
* @param {number} pos where to start: 0 for the beginning
|
|
316
|
+
* @returns {number | null}
|
|
317
|
+
*/
|
|
318
|
+
advance(pos) {
|
|
319
|
+
const arr = this.arr
|
|
320
|
+
let i = pos
|
|
321
|
+
if (i >= 0) {
|
|
322
|
+
for (; i < arr.length; i++) {
|
|
323
|
+
if (arr[i] !== undefined) {
|
|
324
|
+
this.ck = i + 1
|
|
325
|
+
this.cv = arr[i]
|
|
326
|
+
return i + 1
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
i = -1
|
|
330
|
+
}
|
|
331
|
+
const hv = this.hv
|
|
332
|
+
for (let s = -i - 1; s < hv.length; s++) {
|
|
333
|
+
if (hv[s] !== undefined) {
|
|
334
|
+
this.ck = this.hk[s]
|
|
335
|
+
this.cv = hv[s]
|
|
336
|
+
return -s - 2
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return null
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Stores values at keys `start`, `start + 1`, ... in the array part, nils
|
|
344
|
+
* included, as a table constructor does (so `{ nil, nil, 3 }` has length 3).
|
|
345
|
+
*
|
|
346
|
+
* @param {number} start the first key, 1 or more
|
|
347
|
+
* @param {any[]} source
|
|
348
|
+
* @param {number} from the first index in source
|
|
349
|
+
* @param {number} count
|
|
350
|
+
*/
|
|
351
|
+
setRange(start, source, from, count) {
|
|
352
|
+
const arr = this.arr
|
|
353
|
+
while (arr.length < start - 1) arr.push(undefined)
|
|
354
|
+
for (let i = 0; i < count; i++) {
|
|
355
|
+
const v = source[from + i]
|
|
356
|
+
if (start - 1 + i < arr.length) arr[start - 1 + i] = v
|
|
357
|
+
else arr.push(v)
|
|
358
|
+
if (this.hn !== null) {
|
|
359
|
+
const slot = this.hn[start + i]
|
|
360
|
+
if (slot !== undefined && this.hv[slot] !== undefined) {
|
|
361
|
+
this.hv[slot] = undefined
|
|
362
|
+
this.dead++
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
let m = arr.length
|
|
367
|
+
while (m > 0 && arr[m - 1] === undefined) m--
|
|
368
|
+
arr.length = m
|
|
369
|
+
if (this.hn !== null) this.migrate()
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* The position after a key, for `next`; undefined when the key is not in the table.
|
|
374
|
+
*
|
|
375
|
+
* @param {any} key
|
|
376
|
+
* @returns {number | undefined}
|
|
377
|
+
*/
|
|
378
|
+
after(key) {
|
|
379
|
+
if (key === undefined) return 0
|
|
380
|
+
if (typeof key === 'number' && key >= 1 && key <= this.arr.length && Math.floor(key) === key) return key
|
|
381
|
+
const slot = this.slotOf(key)
|
|
382
|
+
return slot === undefined ? undefined : -slot - 2
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/** @returns {LuaTable} a copy without the metatable's identity: same entries, same metatable */
|
|
386
|
+
clone() {
|
|
387
|
+
const t = new LuaTable()
|
|
388
|
+
t.arr = this.arr.slice()
|
|
389
|
+
for (let s = 0; s < this.hk.length; s++) if (this.hv[s] !== undefined) t.set(this.hk[s], this.hv[s])
|
|
390
|
+
t.meta = this.meta
|
|
391
|
+
return t
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* The dictionary key for a key that is neither a string nor a number.
|
|
397
|
+
*
|
|
398
|
+
* @param {any} key
|
|
399
|
+
* @returns {string}
|
|
400
|
+
*/
|
|
401
|
+
function objectKey(key) {
|
|
402
|
+
if (typeof key === 'boolean') return key ? 'b1' : 'b0'
|
|
403
|
+
if (key instanceof Vector) return 'v' + key.x + ',' + key.y + ',' + key.z
|
|
404
|
+
if (key.id === 0) key.id = newId()
|
|
405
|
+
return 'o' + key.id
|
|
406
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scripts a bundle ships, run while it plays: scripted converters on a
|
|
3
|
+
* slot's source, transition conditions on a machine's guards, and listener
|
|
4
|
+
* actions on its interactions.
|
|
5
|
+
*
|
|
6
|
+
* This file imports the Luau VM and Rive host, so the player's main bundle
|
|
7
|
+
* does not: the page gives `script/script.js` (which exports `createScripts`)
|
|
8
|
+
* to `loadScripts` (bundle/bundle.js), and only for a bundle that lists scripts
|
|
9
|
+
* (`manifest.scripts`). A bundle without them downloads and parses none of it,
|
|
10
|
+
* and the legacy build's main file holds none of it either (the host is a file
|
|
11
|
+
* of its own there).
|
|
12
|
+
*
|
|
13
|
+
* Scripted nodes, drawables and path effects (`objects`) run every frame beside
|
|
14
|
+
* these, from `play.objects.js`.
|
|
15
|
+
*
|
|
16
|
+
* The scripts see the player's live values as their view model: a property is
|
|
17
|
+
* a key of the values by its dotted path, and a write changes the value (a
|
|
18
|
+
* trigger sets the event). A converter script that reads none of it is run
|
|
19
|
+
* once per distinct input; one that reads it is run on each use.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { createScriptHost } from '../rive/rive.js'
|
|
23
|
+
import { createObjects } from './play.objects.js'
|
|
24
|
+
|
|
25
|
+
/** Tells the runtimes of several Motions apart, which may share a manifest's objects */
|
|
26
|
+
let nextRuntime = 1
|
|
27
|
+
|
|
28
|
+
/** The interactions a pointer event is made of, by the name a script sees */
|
|
29
|
+
const POINTER_TYPES = /** @type {Record<string, string>} */ ({
|
|
30
|
+
Click: 'click',
|
|
31
|
+
PointerDown: 'down',
|
|
32
|
+
PointerUp: 'up',
|
|
33
|
+
PointerEnter: 'enter',
|
|
34
|
+
PointerExit: 'exit',
|
|
35
|
+
PointerMove: 'move',
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @typedef {object} Scripts
|
|
40
|
+
* @property {(descriptor: any, value: any, isReverse?: boolean, kind?: string, values?: Record<string, any>) => any} convert a converter script's result for a value; the value itself when the script gives none
|
|
41
|
+
* @property {(guard: any, values: Record<string, any>) => boolean} evaluate whether a transition condition script lets its transition pass
|
|
42
|
+
* @property {(action: any, type: string, values: Record<string, any>, events: Record<string, boolean>) => boolean} perform runs a listener action script; whether it changed a value or fired a trigger
|
|
43
|
+
* @property {(values: Record<string, any>) => void} changed the values changed: scripts listening to a property hear of it
|
|
44
|
+
* @property {(entries: import('./play.objects.js').ObjectEntry[], env: import('./play.objects.js').ObjectsEnv) => import('./play.objects.js').Objects} objects starts the bundle's scripted nodes, drawables and path effects, which run each frame (`play.objects.js`)
|
|
45
|
+
* @property {number} count the scripts started so far
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {any} manifest
|
|
50
|
+
* @param {Record<string, Uint8Array>} bytes each script's bytecode, by the entry a descriptor names it by
|
|
51
|
+
* @param {(message: string) => void} report
|
|
52
|
+
* @param {Function} [make] makes a script host; default the Rive host (a test passes one that counts)
|
|
53
|
+
* @returns {Scripts}
|
|
54
|
+
*/
|
|
55
|
+
export function createScripts(manifest, bytes, report, make = createScriptHost) {
|
|
56
|
+
const runtime = nextRuntime++
|
|
57
|
+
/** @type {Array<{ host: any, pure: boolean, hasIn: boolean, lastIn: any, lastOut: any }>} */
|
|
58
|
+
const entries = []
|
|
59
|
+
/** @type {Array<{ path: string, fn: (value: any) => void, last: any }>} */
|
|
60
|
+
const subscriptions = []
|
|
61
|
+
/** @type {Record<string, any>} */
|
|
62
|
+
let values = {}
|
|
63
|
+
/** @type {Record<string, boolean>} */
|
|
64
|
+
let events = {}
|
|
65
|
+
let dirty = false
|
|
66
|
+
/** values a script read, to tell whether a result may be reused */
|
|
67
|
+
let reads = 0
|
|
68
|
+
/** @type {Record<string, string>} */
|
|
69
|
+
const kinds = {}
|
|
70
|
+
const properties = (manifest.data && manifest.data.properties) || []
|
|
71
|
+
for (let n = 0; n < properties.length; n++) kinds[properties[n].name] = properties[n].type
|
|
72
|
+
/** the pointer event of the latest listener action, reused */
|
|
73
|
+
const pointer = { type: 'click' }
|
|
74
|
+
|
|
75
|
+
/** The player's values, as the view model a script reaches */
|
|
76
|
+
const adapter = {
|
|
77
|
+
get: (/** @type {string} */ path) => {
|
|
78
|
+
reads++
|
|
79
|
+
return values[path]
|
|
80
|
+
},
|
|
81
|
+
kind: (/** @type {string} */ path) => {
|
|
82
|
+
const kind = kinds[path]
|
|
83
|
+
if (kind) return kind
|
|
84
|
+
const value = values[path]
|
|
85
|
+
return typeof value === 'number' ? 'number' : typeof value === 'string' ? 'string' : typeof value === 'boolean' ? 'boolean' : undefined
|
|
86
|
+
},
|
|
87
|
+
set: (/** @type {string} */ path, /** @type {any} */ value) => {
|
|
88
|
+
if (values[path] === value) return
|
|
89
|
+
values[path] = value
|
|
90
|
+
dirty = true
|
|
91
|
+
},
|
|
92
|
+
fire: (/** @type {string} */ path) => {
|
|
93
|
+
events[path] = true
|
|
94
|
+
dirty = true
|
|
95
|
+
},
|
|
96
|
+
subscribe: (/** @type {string} */ path, /** @type {(value: any) => void} */ fn) => {
|
|
97
|
+
const subscription = { path, fn, last: values[path] }
|
|
98
|
+
subscriptions.push(subscription)
|
|
99
|
+
return () => {
|
|
100
|
+
const at = subscriptions.indexOf(subscription)
|
|
101
|
+
if (at >= 0) subscriptions.splice(at, 1)
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The host a descriptor, guard or action runs, made when it is first used.
|
|
108
|
+
*
|
|
109
|
+
* @param {any} ref carries `script`, and `inputs` when the object sets any
|
|
110
|
+
* @returns {typeof entries[number] | null}
|
|
111
|
+
*/
|
|
112
|
+
function entryOf(ref) {
|
|
113
|
+
if (ref._runtime === runtime) return ref._host < 0 ? null : entries[ref._host]
|
|
114
|
+
ref._runtime = runtime
|
|
115
|
+
const data = bytes[ref.script]
|
|
116
|
+
if (!data) {
|
|
117
|
+
report("script '" + ref.script + "' is not in the bundle")
|
|
118
|
+
ref._host = -1
|
|
119
|
+
return null
|
|
120
|
+
}
|
|
121
|
+
const host = make({
|
|
122
|
+
bytes: data,
|
|
123
|
+
inputs: ref.inputs,
|
|
124
|
+
viewModel: adapter,
|
|
125
|
+
log: () => {},
|
|
126
|
+
onError: (/** @type {Error} */ error) => report("script '" + ref.script + "': " + error.message),
|
|
127
|
+
})
|
|
128
|
+
host.init()
|
|
129
|
+
entries.push({ host, pure: true, hasIn: false, lastIn: undefined, lastOut: undefined })
|
|
130
|
+
ref._host = entries.length - 1
|
|
131
|
+
return entries[ref._host]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @param {Record<string, any>} v
|
|
136
|
+
* @param {Record<string, boolean>} [e]
|
|
137
|
+
*/
|
|
138
|
+
function bind(v, e) {
|
|
139
|
+
values = v
|
|
140
|
+
if (e) events = e
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
convert(descriptor, value, isReverse, kind, current) {
|
|
145
|
+
if (current) bind(current)
|
|
146
|
+
const entry = entryOf(descriptor)
|
|
147
|
+
if (!entry) return value
|
|
148
|
+
// a script that read nothing gives the same result for the same input
|
|
149
|
+
if (!isReverse && entry.pure && entry.hasIn && entry.lastIn === value) return entry.lastOut
|
|
150
|
+
|
|
151
|
+
const before = reads
|
|
152
|
+
const out = isReverse ? entry.host.reverseConvert(value, kind) : entry.host.convert(value, kind)
|
|
153
|
+
if (reads !== before) entry.pure = false
|
|
154
|
+
if (!out) return value
|
|
155
|
+
if (!isReverse) {
|
|
156
|
+
entry.hasIn = true
|
|
157
|
+
entry.lastIn = value
|
|
158
|
+
entry.lastOut = out.value
|
|
159
|
+
}
|
|
160
|
+
return out.value
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
evaluate(guard, current) {
|
|
164
|
+
bind(current)
|
|
165
|
+
const entry = entryOf(guard)
|
|
166
|
+
return !!entry && entry.host.evaluate()
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
perform(action, type, current, pending) {
|
|
170
|
+
bind(current, pending)
|
|
171
|
+
const entry = entryOf(action)
|
|
172
|
+
if (!entry) return false
|
|
173
|
+
dirty = false
|
|
174
|
+
pointer.type = POINTER_TYPES[type] || 'click'
|
|
175
|
+
entry.host.perform(pointer)
|
|
176
|
+
return dirty
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
changed(current) {
|
|
180
|
+
if (!subscriptions.length) return
|
|
181
|
+
bind(current)
|
|
182
|
+
const heard = subscriptions.slice()
|
|
183
|
+
for (let n = 0; n < heard.length; n++) {
|
|
184
|
+
const value = values[heard[n].path]
|
|
185
|
+
if (value === heard[n].last) continue
|
|
186
|
+
heard[n].last = value
|
|
187
|
+
heard[n].fn(value)
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
objects(list, env) {
|
|
192
|
+
return createObjects(list, bytes, env, report, make)
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
get count() {
|
|
196
|
+
return entries.length
|
|
197
|
+
},
|
|
198
|
+
}
|
|
199
|
+
}
|