etro 0.7.0 → 0.8.2

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.
Files changed (158) hide show
  1. package/.github/workflows/nodejs.yml +4 -2
  2. package/.github/workflows/shipjs-trigger.yml +29 -0
  3. package/CHANGELOG.md +73 -12
  4. package/CODE_OF_CONDUCT.md +5 -5
  5. package/CONTRIBUTING.md +31 -77
  6. package/README.md +81 -26
  7. package/dist/effect/base.d.ts +51 -0
  8. package/dist/effect/brightness.d.ts +16 -0
  9. package/dist/effect/channels.d.ts +23 -0
  10. package/dist/effect/chroma-key.d.ts +23 -0
  11. package/dist/effect/contrast.d.ts +15 -0
  12. package/dist/effect/elliptical-mask.d.ts +31 -0
  13. package/dist/effect/gaussian-blur.d.ts +60 -0
  14. package/dist/effect/grayscale.d.ts +7 -0
  15. package/dist/effect/index.d.ts +15 -0
  16. package/dist/effect/pixelate.d.ts +18 -0
  17. package/dist/effect/shader.d.ts +99 -0
  18. package/dist/effect/stack.d.ts +23 -0
  19. package/dist/effect/transform.d.ts +73 -0
  20. package/dist/etro-cjs.js +9287 -3331
  21. package/dist/etro-iife.js +9229 -3273
  22. package/dist/etro.d.ts +7 -0
  23. package/dist/event.d.ts +35 -0
  24. package/dist/index.d.ts +6 -0
  25. package/dist/layer/audio-source.d.ts +24 -0
  26. package/dist/layer/audio.d.ts +14 -0
  27. package/dist/layer/base.d.ts +82 -0
  28. package/dist/layer/image.d.ts +6 -0
  29. package/dist/layer/index.d.ts +11 -0
  30. package/dist/layer/text.d.ts +60 -0
  31. package/dist/layer/video.d.ts +11 -0
  32. package/dist/layer/visual-source.d.ts +32 -0
  33. package/dist/layer/visual.d.ts +58 -0
  34. package/dist/movie.d.ts +192 -0
  35. package/dist/object.d.ts +12 -0
  36. package/dist/util.d.ts +125 -0
  37. package/eslint.conf.js +2 -9
  38. package/eslint.example-conf.js +9 -0
  39. package/eslint.test-conf.js +1 -0
  40. package/eslint.typescript-conf.js +5 -0
  41. package/examples/application/readme-screenshot.html +16 -17
  42. package/examples/application/video-player.html +10 -11
  43. package/examples/application/webcam.html +6 -6
  44. package/examples/introduction/audio.html +30 -18
  45. package/examples/introduction/effects.html +37 -14
  46. package/examples/introduction/export.html +32 -25
  47. package/examples/introduction/functions.html +6 -4
  48. package/examples/introduction/hello-world1.html +9 -5
  49. package/examples/introduction/hello-world2.html +5 -5
  50. package/examples/introduction/keyframes.html +35 -23
  51. package/examples/introduction/media.html +26 -18
  52. package/examples/introduction/text.html +9 -5
  53. package/karma.conf.js +5 -3
  54. package/package.json +36 -14
  55. package/rollup.config.js +15 -4
  56. package/scripts/gen-effect-samples.html +26 -25
  57. package/scripts/save-effect-samples.js +14 -15
  58. package/ship.config.js +80 -0
  59. package/src/effect/base.ts +115 -0
  60. package/src/effect/brightness.ts +43 -0
  61. package/src/effect/channels.ts +50 -0
  62. package/src/effect/chroma-key.ts +82 -0
  63. package/src/effect/contrast.ts +42 -0
  64. package/src/effect/elliptical-mask.ts +75 -0
  65. package/src/effect/gaussian-blur.ts +232 -0
  66. package/src/effect/grayscale.ts +34 -0
  67. package/src/effect/index.ts +22 -0
  68. package/src/effect/pixelate.ts +58 -0
  69. package/src/effect/shader.ts +557 -0
  70. package/src/effect/stack.ts +78 -0
  71. package/src/effect/transform.ts +193 -0
  72. package/src/etro.ts +26 -0
  73. package/src/event.ts +112 -0
  74. package/src/index.ts +8 -0
  75. package/src/layer/audio-source.ts +219 -0
  76. package/src/layer/audio.ts +34 -0
  77. package/src/layer/base.ts +175 -0
  78. package/src/layer/image.ts +8 -0
  79. package/src/layer/index.ts +13 -0
  80. package/src/layer/text.ts +138 -0
  81. package/src/layer/video.ts +15 -0
  82. package/src/layer/visual-source.ts +150 -0
  83. package/src/layer/visual.ts +197 -0
  84. package/src/movie.ts +707 -0
  85. package/src/object.ts +14 -0
  86. package/src/util.ts +466 -0
  87. package/tsconfig.json +8 -0
  88. package/docs/effect.js.html +0 -1215
  89. package/docs/event.js.html +0 -145
  90. package/docs/index.html +0 -81
  91. package/docs/index.js.html +0 -92
  92. package/docs/layer.js.html +0 -888
  93. package/docs/module-effect-GaussianBlurComponent.html +0 -345
  94. package/docs/module-effect.Brightness.html +0 -339
  95. package/docs/module-effect.Channels.html +0 -319
  96. package/docs/module-effect.ChromaKey.html +0 -611
  97. package/docs/module-effect.Contrast.html +0 -339
  98. package/docs/module-effect.EllipticalMask.html +0 -200
  99. package/docs/module-effect.GaussianBlur.html +0 -202
  100. package/docs/module-effect.GaussianBlurHorizontal.html +0 -242
  101. package/docs/module-effect.GaussianBlurVertical.html +0 -242
  102. package/docs/module-effect.Pixelate.html +0 -330
  103. package/docs/module-effect.Shader.html +0 -1227
  104. package/docs/module-effect.Stack.html +0 -406
  105. package/docs/module-effect.Transform.Matrix.html +0 -193
  106. package/docs/module-effect.Transform.html +0 -1174
  107. package/docs/module-effect.html +0 -148
  108. package/docs/module-event.html +0 -473
  109. package/docs/module-index.html +0 -186
  110. package/docs/module-layer-Media.html +0 -1116
  111. package/docs/module-layer-MediaMixin.html +0 -164
  112. package/docs/module-layer.Audio.html +0 -1188
  113. package/docs/module-layer.Base.html +0 -629
  114. package/docs/module-layer.Image.html +0 -1421
  115. package/docs/module-layer.Text.html +0 -1731
  116. package/docs/module-layer.Video.html +0 -1938
  117. package/docs/module-layer.Visual.html +0 -1698
  118. package/docs/module-layer.html +0 -137
  119. package/docs/module-movie.html +0 -3118
  120. package/docs/module-util.Color.html +0 -702
  121. package/docs/module-util.Font.html +0 -395
  122. package/docs/module-util.html +0 -845
  123. package/docs/movie.js.html +0 -689
  124. package/docs/scripts/collapse.js +0 -20
  125. package/docs/scripts/linenumber.js +0 -25
  126. package/docs/scripts/nav.js +0 -12
  127. package/docs/scripts/polyfill.js +0 -4
  128. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -202
  129. package/docs/scripts/prettify/lang-css.js +0 -2
  130. package/docs/scripts/prettify/prettify.js +0 -28
  131. package/docs/scripts/search.js +0 -83
  132. package/docs/styles/jsdoc.css +0 -671
  133. package/docs/styles/prettify.css +0 -79
  134. package/docs/util.js.html +0 -503
  135. package/screenshots/2019-08-17_0.png +0 -0
  136. package/spec/assets/effect/gaussian-blur-horizontal.png +0 -0
  137. package/spec/assets/effect/gaussian-blur-vertical.png +0 -0
  138. package/spec/assets/effect/original.png +0 -0
  139. package/spec/assets/effect/pixelate.png +0 -0
  140. package/spec/assets/effect/transform/multiply.png +0 -0
  141. package/spec/assets/effect/transform/rotate.png +0 -0
  142. package/spec/assets/effect/transform/scale-fraction.png +0 -0
  143. package/spec/assets/effect/transform/scale.png +0 -0
  144. package/spec/assets/effect/transform/translate-fraction.png +0 -0
  145. package/spec/assets/effect/transform/translate.png +0 -0
  146. package/spec/assets/layer/audio.wav +0 -0
  147. package/spec/assets/layer/image.jpg +0 -0
  148. package/spec/effect.spec.js +0 -352
  149. package/spec/event.spec.js +0 -25
  150. package/spec/layer.spec.js +0 -150
  151. package/spec/movie.spec.js +0 -162
  152. package/spec/util.spec.js +0 -285
  153. package/src/effect.js +0 -1268
  154. package/src/event.js +0 -78
  155. package/src/index.js +0 -23
  156. package/src/layer.js +0 -897
  157. package/src/movie.js +0 -637
  158. package/src/util.js +0 -505
package/src/object.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { Movie } from './movie'
2
+
3
+ /** A movie, layer or effect */
4
+ export default interface EtroObject {
5
+ /** Used in etro internals */
6
+ type: string
7
+ /** Which properties to not watch for changes, for `Movie#autoRefresh` */
8
+ publicExcludes: string[]
9
+ /** Map of property name to function to run on result of `val` */
10
+ propertyFilters: Record<string, <T>(value: T) => T>
11
+ movie: Movie
12
+
13
+ getDefaultOptions(): object // eslint-disable-line @typescript-eslint/ban-types
14
+ }
package/src/util.ts ADDED
@@ -0,0 +1,466 @@
1
+ /**
2
+ * @module util
3
+ */
4
+
5
+ import EtroObject from './object'
6
+ import { publish } from './event'
7
+ import { Movie } from './movie'
8
+
9
+ /**
10
+ * Gets the first matching property descriptor in the prototype chain, or
11
+ * undefined.
12
+ * @param obj
13
+ * @param name
14
+ */
15
+ function getPropertyDescriptor (obj: unknown, name: string | number | symbol): PropertyDescriptor {
16
+ do {
17
+ const propDesc = Object.getOwnPropertyDescriptor(obj, name)
18
+ if (propDesc)
19
+ return propDesc
20
+
21
+ obj = Object.getPrototypeOf(obj)
22
+ } while (obj)
23
+ return undefined
24
+ }
25
+
26
+ /**
27
+ * Merges `options` with `defaultOptions`, and then copies the properties with
28
+ * the keys in `defaultOptions` from the merged object to `destObj`.
29
+ *
30
+ * @return
31
+ */
32
+ // TODO: Make methods like getDefaultOptions private
33
+ export function applyOptions (options: object, destObj: EtroObject): void { // eslint-disable-line @typescript-eslint/ban-types
34
+ const defaultOptions = destObj.getDefaultOptions()
35
+
36
+ // Validate; make sure `keys` doesn't have any extraneous items
37
+ for (const option in options)
38
+ // eslint-disable-next-line no-prototype-builtins
39
+ if (!defaultOptions.hasOwnProperty(option))
40
+ throw new Error("Invalid option: '" + option + "'")
41
+
42
+ // Merge options and defaultOptions
43
+ options = { ...defaultOptions, ...options }
44
+
45
+ // Copy options
46
+ for (const option in options) {
47
+ const propDesc = getPropertyDescriptor(destObj, option)
48
+ // Update the property as long as the property has not been set (unless if it has a setter)
49
+ if (!propDesc || propDesc.set)
50
+ destObj[option] = options[option]
51
+ }
52
+ }
53
+
54
+ // This must be cleared at the start of each frame
55
+ const valCache = new WeakMap()
56
+ function cacheValue (element: EtroObject, path: string, value: unknown) {
57
+ // Initiate movie cache
58
+ if (!valCache.has(element.movie))
59
+ valCache.set(element.movie, new WeakMap())
60
+
61
+ const movieCache = valCache.get(element.movie)
62
+
63
+ // Iniitate element cache
64
+ if (!movieCache.has(element))
65
+ movieCache.set(element, {})
66
+
67
+ const elementCache = movieCache.get(element)
68
+
69
+ // Cache the value
70
+ elementCache[path] = value
71
+ return value
72
+ }
73
+ function hasCachedValue (element, path) {
74
+ return valCache.has(element.movie) &&
75
+ valCache.get(element.movie).has(element) &&
76
+ path in valCache.get(element.movie).get(element)
77
+ }
78
+ function getCachedValue (element, path) {
79
+ return valCache.get(element.movie).get(element)[path]
80
+ }
81
+ export function clearCachedValues (movie: Movie): void {
82
+ valCache.delete(movie)
83
+ }
84
+
85
+ /**
86
+ * A keyframe set.
87
+ *
88
+ * Usage:
89
+ * ```js
90
+ new etro.KeyFrame([time1, value1, interpolation1], [time2, value2])`
91
+ * ```
92
+ * TypeScript users need to specify the type of the value as a type parameter.
93
+ */
94
+ export class KeyFrame<T> {
95
+ value: unknown[][]
96
+ /** Keys to interpolate, or all keys if undefined */
97
+ interpolationKeys: string[]
98
+
99
+ constructor (...value: T[][]) {
100
+ this.value = value
101
+ this.interpolationKeys = []
102
+ }
103
+
104
+ withKeys (keys: string[]): KeyFrame<T> {
105
+ this.interpolationKeys = keys
106
+ return this
107
+ }
108
+
109
+ evaluate (time: number): T {
110
+ if (this.value.length === 0)
111
+ throw new Error('Empty keyframe')
112
+
113
+ if (time === undefined)
114
+ throw new Error('|time| is undefined or null')
115
+
116
+ const firstTime: number = this.value[0][0] as number
117
+ if (time < firstTime)
118
+ throw new Error('No keyframe point before |time|')
119
+
120
+ // I think reduce are slow to do per-frame (or more)?
121
+ for (let i = 0; i < this.value.length; i++) {
122
+ const startTime = this.value[i][0] as number
123
+ const startValue = this.value[i][1] as T
124
+ type interpolateType = <U = number | object>(startValue: U, endValue: U, percentProgress: number, interpolationKeys: string[]) => U // eslint-disable-line @typescript-eslint/ban-types
125
+ const interpolate = this.value[i].length === 3 ? this.value[i][2] as interpolateType : linearInterp
126
+ if (i + 1 < this.value.length) {
127
+ const endTime = this.value[i + 1][0] as number
128
+ const endValue = this.value[i + 1][1] as T
129
+ if (startTime <= time && time < endTime)
130
+ // No need for endValue if it is flat interpolation
131
+ // TODO: support custom interpolation for 'other' types?
132
+ if (!(typeof startValue === 'number' || typeof endValue === 'object')) {
133
+ return startValue
134
+ } else if (typeof startValue !== typeof endValue) {
135
+ throw new Error('Type mismatch in keyframe values')
136
+ } else {
137
+ // Interpolate
138
+ const percentProgress = (time - startTime) / (endTime - startTime)
139
+ return interpolate(
140
+ startValue as unknown as (number | object), // eslint-disable-line @typescript-eslint/ban-types
141
+ endValue as unknown as (number | object), // eslint-disable-line @typescript-eslint/ban-types
142
+ percentProgress, this.interpolationKeys
143
+ ) as unknown as T
144
+ }
145
+ } else {
146
+ // Repeat last value forever
147
+ return startValue
148
+ }
149
+ }
150
+ }
151
+ }
152
+
153
+ /** A dynamic property. Supports simple values, keyframes and functions */
154
+ export type Dynamic<T> = T | KeyFrame<T> | ((element: EtroObject, time: number) => T)
155
+
156
+ /**
157
+ * Computes a property.
158
+ *
159
+ * @param element - the etro object to which the property belongs to
160
+ * @param path - the dot-separated path to a property on `element`
161
+ * @param time - time to calculate keyframes for, if necessary
162
+ *
163
+ * Note that only values used in keyframes that are numbers or objects
164
+ * (including arrays) are interpolated. All other values are taken sequentially
165
+ * with no interpolation. JavaScript will convert parsed colors, if created
166
+ * correctly, to their string representations when assigned to a
167
+ * CanvasRenderingContext2D property.
168
+ */
169
+ // TODO: Is this function efficient?
170
+ // TODO: Update doc @params to allow for keyframes
171
+ export function val (element: EtroObject, path: string, time: number): any { // eslint-disable-line @typescript-eslint/no-explicit-any
172
+ if (hasCachedValue(element, path))
173
+ return getCachedValue(element, path)
174
+
175
+ // Get property of element at path
176
+ const pathParts = path.split('.')
177
+ let property = element[pathParts.shift()]
178
+ while (pathParts.length > 0)
179
+ property = property[pathParts.shift()]
180
+
181
+ // Property filter function
182
+ const process = element.propertyFilters[path]
183
+
184
+ let value
185
+ if (property instanceof KeyFrame)
186
+ value = property.evaluate(time)
187
+ else if (typeof property === 'function')
188
+ value = property(element, time) // TODO? add more args
189
+ else
190
+ // Simple value
191
+ value = property
192
+
193
+ return cacheValue(element, path, process ? process.call(element, value) : value)
194
+ }
195
+
196
+ /* export function floorInterp(x1, x2, t, objectKeys) {
197
+ // https://stackoverflow.com/a/25835337/3783155 (TODO: preserve getters/setters, etc?)
198
+ return !objectKeys ? x1 : objectKeys.reduce((a, x) => {
199
+ if (x1.hasOwnProperty(x)) a[x] = o[x]; // ignore x2
200
+ return a;
201
+ }, Object.create(Object.getPrototypeOf(x1)));
202
+ } */
203
+
204
+ export function linearInterp (x1: number | object, x2: number | object, t: number, objectKeys?: string[]): number | object { // eslint-disable-line @typescript-eslint/ban-types
205
+ if (typeof x1 !== typeof x2)
206
+ throw new Error('Type mismatch')
207
+
208
+ if (typeof x1 !== 'number' && typeof x1 !== 'object')
209
+ // Flat interpolation (floor)
210
+ return x1
211
+
212
+ if (typeof x1 === 'object') { // to work with objects (including arrays)
213
+ // TODO: make this code DRY
214
+ if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2))
215
+ throw new Error('Prototype mismatch')
216
+
217
+ // Preserve prototype of objects
218
+ const int = Object.create(Object.getPrototypeOf(x1))
219
+ // Take the intersection of properties
220
+ const keys = Object.keys(x1) || objectKeys // TODO: reverse operands
221
+ for (let i = 0; i < keys.length; i++) {
222
+ const key = keys[i]
223
+ // eslint-disable-next-line no-prototype-builtins
224
+ if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key))
225
+ continue
226
+
227
+ int[key] = linearInterp(x1[key], x2[key], t)
228
+ }
229
+ return int
230
+ }
231
+ return (1 - t) * x1 + t * (x2 as number)
232
+ }
233
+
234
+ export function cosineInterp (x1: number | object, x2: number | object, t: number, objectKeys?: string[]): number | object { // eslint-disable-line @typescript-eslint/ban-types
235
+ if (typeof x1 !== typeof x2)
236
+ throw new Error('Type mismatch')
237
+
238
+ if (typeof x1 !== 'number' && typeof x1 !== 'object')
239
+ // Flat interpolation (floor)
240
+ return x1
241
+
242
+ if (typeof x1 === 'object' && typeof x2 === 'object') { // to work with objects (including arrays)
243
+ if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2))
244
+ throw new Error('Prototype mismatch')
245
+
246
+ // Preserve prototype of objects
247
+ const int = Object.create(Object.getPrototypeOf(x1))
248
+ // Take the intersection of properties
249
+ const keys = Object.keys(x1) || objectKeys
250
+ for (let i = 0; i < keys.length; i++) {
251
+ const key = keys[i]
252
+ // eslint-disable-next-line no-prototype-builtins
253
+ if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key))
254
+ continue
255
+
256
+ int[key] = cosineInterp(x1[key], x2[key], t)
257
+ }
258
+ return int
259
+ }
260
+ const cos = Math.cos(Math.PI / 2 * t)
261
+ return cos * (x1 as number) + (1 - cos) * (x2 as number)
262
+ }
263
+
264
+ /**
265
+ * An RGBA color, for proper interpolation and shader effects
266
+ */
267
+ export class Color {
268
+ r: number
269
+ g: number
270
+ b: number
271
+ a: number
272
+
273
+ /**
274
+ * @param r
275
+ * @param g
276
+ * @param b
277
+ * @param a
278
+ */
279
+ constructor (r: number, g: number, b: number, a = 1.0) {
280
+ this.r = r
281
+ this.g = g
282
+ this.b = b
283
+ this.a = a
284
+ }
285
+
286
+ /**
287
+ * Converts to a CSS color
288
+ */
289
+ toString (): string {
290
+ return `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`
291
+ }
292
+ }
293
+
294
+ const parseColorCanvas = document.createElement('canvas')
295
+ parseColorCanvas.width = parseColorCanvas.height = 1
296
+ const parseColorCtx = parseColorCanvas.getContext('2d')
297
+ /**
298
+ * Converts a CSS color string to a {@link Color} object representation.
299
+ * @param str
300
+ * @return the parsed color
301
+ */
302
+ export function parseColor (str: string): Color {
303
+ // TODO - find a better way to deal with the fact that invalid values of "col"
304
+ // are ignored.
305
+ parseColorCtx.clearRect(0, 0, 1, 1)
306
+ parseColorCtx.fillStyle = str
307
+ parseColorCtx.fillRect(0, 0, 1, 1)
308
+ const data = parseColorCtx.getImageData(0, 0, 1, 1).data
309
+ return new Color(data[0], data[1], data[2], data[3] / 255)
310
+ }
311
+
312
+ /**
313
+ * A font, for proper interpolation
314
+ */
315
+ export class Font {
316
+ size: number
317
+ sizeUnit: string
318
+ family: string
319
+ style: string
320
+ variant: string
321
+ weight: string
322
+ stretch: string
323
+ lineHeight: string
324
+
325
+ /**
326
+ * @param size
327
+ * @param family
328
+ * @param sizeUnit
329
+ */
330
+ constructor (size: number, sizeUnit: string, family: string, style = 'normal', variant = 'normal',
331
+ weight = 'normal', stretch = 'normal', lineHeight = 'normal') {
332
+ this.size = size
333
+ this.sizeUnit = sizeUnit
334
+ this.family = family
335
+ this.style = style
336
+ this.variant = variant
337
+ this.weight = weight
338
+ this.stretch = stretch
339
+ this.lineHeight = lineHeight
340
+ }
341
+
342
+ /**
343
+ * Converts to CSS font syntax
344
+ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/font
345
+ */
346
+ toString (): string {
347
+ let s = ''
348
+ if (this.style !== 'normal') s += this.style + ' '
349
+ if (this.variant !== 'normal') s += this.variant + ' '
350
+ if (this.weight !== 'normal') s += this.weight + ' '
351
+ if (this.stretch !== 'normal') s += this.stretch + ' '
352
+ s += `${this.size}${this.sizeUnit} `
353
+ if (this.lineHeight !== 'normal') s += this.lineHeight + ' '
354
+ s += this.family
355
+
356
+ return s
357
+ }
358
+ }
359
+
360
+ const parseFontEl = document.createElement('div')
361
+ /**
362
+ * Converts a CSS font string to a {@link Font} object
363
+ * representation.
364
+ * @param str
365
+ * @return the parsed font
366
+ */
367
+ export function parseFont (str: string): Font {
368
+ // Assign css string to html element
369
+ parseFontEl.setAttribute('style', `font: ${str}`)
370
+ const {
371
+ fontSize, fontFamily, fontStyle, fontVariant, fontWeight, lineHeight
372
+ } = parseFontEl.style
373
+ parseFontEl.removeAttribute('style')
374
+
375
+ const size = parseFloat(fontSize)
376
+ const sizeUnit = fontSize.substring(size.toString().length)
377
+ return new Font(size, sizeUnit, fontFamily, fontStyle, fontVariant, fontWeight, lineHeight)
378
+ }
379
+
380
+ /**
381
+ * @param mapper
382
+ * @param canvas
383
+ * @param ctx
384
+ * @param x
385
+ * @param y
386
+ * @param width
387
+ * @param height
388
+ * @param flush
389
+ * @deprecated Use {@link effect.Shader} instead
390
+ */
391
+ export function mapPixels (
392
+ mapper: (pixels: Uint8ClampedArray, i: number) => void,
393
+ canvas: HTMLCanvasElement,
394
+ ctx: CanvasRenderingContext2D,
395
+ x: number,
396
+ y: number,
397
+ width: number,
398
+ height: number,
399
+ flush = true
400
+ ): void {
401
+ x = x || 0
402
+ y = y || 0
403
+ width = width || canvas.width
404
+ height = height || canvas.height
405
+ const frame = ctx.getImageData(x, y, width, height)
406
+ for (let i = 0, l = frame.data.length; i < l; i += 4)
407
+ mapper(frame.data, i)
408
+
409
+ if (flush)
410
+ ctx.putImageData(frame, x, y)
411
+ }
412
+
413
+ /**
414
+ * <p>Emits "change" event when public properties updated, recursively.
415
+ * <p>Must be called before any watchable properties are set, and only once in
416
+ * the prototype chain.
417
+ *
418
+ * @param target - object to watch
419
+ */
420
+ export function watchPublic (target: EtroObject): EtroObject {
421
+ const getPath = (receiver, prop) =>
422
+ (receiver === proxy ? '' : (paths.get(receiver) + '.')) + prop
423
+ const callback = function (prop, val, receiver) {
424
+ // Public API property updated, emit 'modify' event.
425
+ publish(proxy, `${target.type}.change.modify`, { property: getPath(receiver, prop), newValue: val })
426
+ }
427
+ const canWatch = (receiver, prop) => !prop.startsWith('_') &&
428
+ (receiver.publicExcludes === undefined || !receiver.publicExcludes.includes(prop))
429
+
430
+ // The path to each child property (each is a unique proxy)
431
+ const paths = new WeakMap()
432
+
433
+ const handler = {
434
+ set (obj, prop, val, receiver) {
435
+ // Recurse
436
+ if (typeof val === 'object' && val !== null && !paths.has(val) && canWatch(receiver, prop)) {
437
+ val = new Proxy(val, handler)
438
+ paths.set(val, getPath(receiver, prop))
439
+ }
440
+
441
+ // Set property or attribute
442
+ // Search prototype chain for the closest setter
443
+ let objProto = obj
444
+ while ((objProto = Object.getPrototypeOf(objProto))) {
445
+ const propDesc = Object.getOwnPropertyDescriptor(objProto, prop)
446
+ if (propDesc && propDesc.set) {
447
+ // Call setter, supplying proxy as this (fixes event bugs)
448
+ propDesc.set.call(receiver, val)
449
+ break
450
+ }
451
+ }
452
+ if (!objProto)
453
+ // Couldn't find setter; set value on instance
454
+ obj[prop] = val
455
+
456
+ // Check if the property isn't blacklisted in publicExcludes.
457
+ if (canWatch(receiver, prop))
458
+ callback(prop, val, receiver)
459
+
460
+ return true
461
+ }
462
+ }
463
+
464
+ const proxy = new Proxy(target, handler)
465
+ return proxy
466
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["es6", "DOM"],
4
+ "target": "es5",
5
+ "declaration": true
6
+ },
7
+ "include": ["./src/**/*"]
8
+ }