etro 0.8.0 → 0.8.3

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 (97) hide show
  1. package/.github/workflows/nodejs.yml +3 -1
  2. package/.github/workflows/shipjs-trigger.yml +29 -0
  3. package/CHANGELOG.md +36 -13
  4. package/CODE_OF_CONDUCT.md +5 -5
  5. package/CONTRIBUTING.md +22 -72
  6. package/README.md +2 -2
  7. package/dist/effect/base.d.ts +14 -1
  8. package/dist/etro-cjs.js +189 -230
  9. package/dist/etro-iife.js +189 -230
  10. package/dist/layer/base.d.ts +13 -0
  11. package/eslint.conf.js +2 -1
  12. package/eslint.test-conf.js +1 -0
  13. package/examples/application/readme-screenshot.html +4 -8
  14. package/examples/application/video-player.html +3 -4
  15. package/examples/introduction/effects.html +23 -4
  16. package/karma.conf.js +4 -2
  17. package/package.json +8 -4
  18. package/scripts/gen-effect-samples.html +0 -3
  19. package/ship.config.js +80 -0
  20. package/src/effect/base.ts +29 -10
  21. package/src/effect/gaussian-blur.ts +10 -10
  22. package/src/effect/pixelate.ts +1 -2
  23. package/src/effect/shader.ts +18 -22
  24. package/src/effect/stack.ts +8 -4
  25. package/src/effect/transform.ts +13 -14
  26. package/src/event.ts +8 -14
  27. package/src/layer/audio-source.ts +16 -14
  28. package/src/layer/audio.ts +1 -2
  29. package/src/layer/base.ts +26 -7
  30. package/src/layer/visual.ts +11 -6
  31. package/src/movie.ts +70 -64
  32. package/src/util.ts +50 -57
  33. package/docs/effect.js.html +0 -1215
  34. package/docs/event.js.html +0 -145
  35. package/docs/index.html +0 -81
  36. package/docs/index.js.html +0 -92
  37. package/docs/layer.js.html +0 -888
  38. package/docs/module-effect-GaussianBlurComponent.html +0 -345
  39. package/docs/module-effect.Brightness.html +0 -339
  40. package/docs/module-effect.Channels.html +0 -319
  41. package/docs/module-effect.ChromaKey.html +0 -611
  42. package/docs/module-effect.Contrast.html +0 -339
  43. package/docs/module-effect.EllipticalMask.html +0 -200
  44. package/docs/module-effect.GaussianBlur.html +0 -202
  45. package/docs/module-effect.GaussianBlurHorizontal.html +0 -242
  46. package/docs/module-effect.GaussianBlurVertical.html +0 -242
  47. package/docs/module-effect.Pixelate.html +0 -330
  48. package/docs/module-effect.Shader.html +0 -1227
  49. package/docs/module-effect.Stack.html +0 -406
  50. package/docs/module-effect.Transform.Matrix.html +0 -193
  51. package/docs/module-effect.Transform.html +0 -1174
  52. package/docs/module-effect.html +0 -148
  53. package/docs/module-event.html +0 -473
  54. package/docs/module-index.html +0 -186
  55. package/docs/module-layer-Media.html +0 -1116
  56. package/docs/module-layer-MediaMixin.html +0 -164
  57. package/docs/module-layer.Audio.html +0 -1188
  58. package/docs/module-layer.Base.html +0 -629
  59. package/docs/module-layer.Image.html +0 -1421
  60. package/docs/module-layer.Text.html +0 -1731
  61. package/docs/module-layer.Video.html +0 -1938
  62. package/docs/module-layer.Visual.html +0 -1698
  63. package/docs/module-layer.html +0 -137
  64. package/docs/module-movie.html +0 -3118
  65. package/docs/module-util.Color.html +0 -702
  66. package/docs/module-util.Font.html +0 -395
  67. package/docs/module-util.html +0 -845
  68. package/docs/movie.js.html +0 -689
  69. package/docs/scripts/collapse.js +0 -20
  70. package/docs/scripts/linenumber.js +0 -25
  71. package/docs/scripts/nav.js +0 -12
  72. package/docs/scripts/polyfill.js +0 -4
  73. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -202
  74. package/docs/scripts/prettify/lang-css.js +0 -2
  75. package/docs/scripts/prettify/prettify.js +0 -28
  76. package/docs/scripts/search.js +0 -83
  77. package/docs/styles/jsdoc.css +0 -671
  78. package/docs/styles/prettify.css +0 -79
  79. package/docs/util.js.html +0 -503
  80. package/spec/assets/effect/gaussian-blur-horizontal.png +0 -0
  81. package/spec/assets/effect/gaussian-blur-vertical.png +0 -0
  82. package/spec/assets/effect/grayscale.png +0 -0
  83. package/spec/assets/effect/original.png +0 -0
  84. package/spec/assets/effect/pixelate.png +0 -0
  85. package/spec/assets/effect/transform/multiply.png +0 -0
  86. package/spec/assets/effect/transform/rotate.png +0 -0
  87. package/spec/assets/effect/transform/scale-fraction.png +0 -0
  88. package/spec/assets/effect/transform/scale.png +0 -0
  89. package/spec/assets/effect/transform/translate-fraction.png +0 -0
  90. package/spec/assets/effect/transform/translate.png +0 -0
  91. package/spec/assets/layer/audio.wav +0 -0
  92. package/spec/assets/layer/image.jpg +0 -0
  93. package/spec/effect.spec.js +0 -421
  94. package/spec/event.spec.js +0 -39
  95. package/spec/layer.spec.js +0 -307
  96. package/spec/movie.spec.js +0 -346
  97. package/spec/util.spec.js +0 -294
@@ -36,7 +36,20 @@ declare class Base implements EtroObject {
36
36
  * movie's timeline
37
37
  */
38
38
  constructor(options: BaseOptions);
39
+ /**
40
+ * Attaches this layer to `movie` if not already attached.
41
+ * @ignore
42
+ */
43
+ tryAttach(movie: Movie): void;
39
44
  attach(movie: Movie): void;
45
+ /**
46
+ * Dettaches this layer from its movie if the number of times `tryDetach` has
47
+ * been called (including this call) equals the number of times `tryAttach`
48
+ * has been called.
49
+ *
50
+ * @ignore
51
+ */
52
+ tryDetach(): void;
40
53
  detach(): void;
41
54
  /**
42
55
  * Called when the layer is activated
package/eslint.conf.js CHANGED
@@ -15,6 +15,7 @@ module.exports = {
15
15
  sourceType: 'module'
16
16
  },
17
17
  rules: {
18
- 'brace-style': ['error', '1tbs', { allowSingleLine: false }]
18
+ 'brace-style': ['error', '1tbs', { allowSingleLine: false }],
19
+ curly: ['error', 'multi', 'consistent']
19
20
  },
20
21
  }
@@ -1,4 +1,5 @@
1
1
  const conf = require('./eslint.conf.js')
2
2
  conf.env.jasmine = true
3
+ conf.globals.define = 'readonly'
3
4
  conf.globals.etro = 'readonly'
4
5
  module.exports = conf
@@ -15,9 +15,8 @@
15
15
  document.body.appendChild(canvas)
16
16
  movie = new etro.Movie({ canvas })
17
17
  etro.event.subscribe(movie, 'movie.timeupdate', () => {
18
- if (movie.currentTime >= 11) {
18
+ if (movie.currentTime >= 11)
19
19
  movie.pause()
20
- }
21
20
  })
22
21
 
23
22
  sample = document.createElement('video')
@@ -32,15 +31,14 @@
32
31
  const width = movie.width / cols; const height = movie.height / rows
33
32
  let numbLoaded = 0
34
33
 
35
- for (let y = 0; y < movie.height; y += height) {
34
+ for (let y = 0; y < movie.height; y += height)
36
35
  for (let x = 0; x < movie.width; x += width) {
37
36
  const video = document.createElement('video')
38
37
  video.src = '../assets/desert.mp4'
39
- if (x * y) {
38
+ if (x * y)
40
39
  video.onplay = event => {
41
40
  console.log(event)
42
41
  }
43
- }
44
42
 
45
43
  video.onloadedmetadata = () => {
46
44
  const layer = new etro.layer.Video({
@@ -54,12 +52,10 @@
54
52
  movie.addLayer(layer)
55
53
  numbLoaded++
56
54
 
57
- if (numbLoaded === rows * cols) {
55
+ if (numbLoaded === rows * cols)
58
56
  end()
59
- }
60
57
  }
61
58
  }
62
- }
63
59
  }
64
60
 
65
61
  const end = () => {
@@ -83,14 +83,13 @@
83
83
  canvas.addEventListener('click', controls.playPause.click) // FIXME: isn"t firing
84
84
  document.body.addEventListener('keyup', event => {
85
85
  const key = event.which || event.keyCode || 0
86
- if (key === 32) {
86
+ if (key === 32)
87
87
  controls.playPause.click()
88
- } // spacebar
88
+ // spacebar
89
89
  })
90
90
  etro.event.subscribe(movie, 'movie.ended', event => {
91
- if (!event.repeat) {
91
+ if (!event.repeat)
92
92
  controls.playPause.className = 'play'
93
- }
94
93
  })
95
94
 
96
95
  controls.progressBar.min = 0
@@ -26,13 +26,18 @@
26
26
  // create a transparent blue 500x200 at (50, 20) that starts at time 1 and lasts 2 seconds
27
27
  .addLayer(
28
28
  new etro.layer.Image({ startTime: 2, duration: 2, source: image }).addEffect(
29
- new etro.effect.GaussianBlur(3)
29
+ new etro.effect.GaussianBlur({ radius: 3 })
30
30
  )
31
31
  )
32
32
  .addLayer(
33
33
  new etro.layer.Image({ startTime: 4, duration: 2, source: image }).addEffect(
34
34
  // you can also use keyframes for almost any property in Etro
35
- new etro.effect.Channels({ 0: { r: 2, g: 0.5 }, 2: { r: 0.5, g: 2 } })
35
+ new etro.effect.Channels({
36
+ factors: new etro.KeyFrame(
37
+ [0, { r: 2, g: 0.5 }],
38
+ [2, { r: 0.5, g: 2 }]
39
+ )
40
+ })
36
41
  )
37
42
  )
38
43
  .addLayer(
@@ -44,14 +49,28 @@
44
49
  width: movie.width,
45
50
  height: movie.height
46
51
  }).addEffect(
47
- new etro.effect.Transform(new etro.effect.Transform.Matrix().rotate(Math.PI / 6)) // 30d
52
+ new etro.effect.Transform({
53
+ matrix: new etro.effect.Transform.Matrix().rotate(Math.PI / 6) // 30d
54
+ })
48
55
  )
49
56
  )
50
57
  .addLayer(
51
58
  new etro.layer.Image({ startTime: 8, duration: 2, source: image }).addEffect(
52
- new etro.effect.EllipticalMask(image.width / 2, image.height / 2, image.width / 2, image.height / 2)
59
+ new etro.effect.EllipticalMask({
60
+ x: image.width / 2,
61
+ y: image.height / 2,
62
+ radiusX: image.width / 2,
63
+ radiusY: image.height / 2
64
+ })
53
65
  )
54
66
  )
67
+ // .addEffect(new etro.effect.GaussianBlur({ radius: 5 }))
68
+
69
+ setTimeout(() => {
70
+ // console.log(movie.layers[1].effects[0].effects[1].shape)
71
+ }, 3100)
72
+
73
+ movie
55
74
  .play()
56
75
  }
57
76
  </script>
package/karma.conf.js CHANGED
@@ -9,12 +9,14 @@ module.exports = function (config) {
9
9
 
10
10
  // frameworks to use
11
11
  // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
12
- frameworks: ['jasmine'],
12
+ frameworks: ['jasmine', 'requirejs', 'es6-shim'],
13
13
 
14
14
  // list of files / patterns to load in the browser
15
15
  files: [
16
16
  'dist/etro-iife.js',
17
- 'spec/*.spec.js',
17
+ { pattern: 'spec/*.spec.js', included: false },
18
+ { pattern: 'node_modules/resemblejs/*.js', included: false },
19
+ 'spec/main.js',
18
20
  { pattern: 'spec/assets/**/*', included: false }
19
21
  ],
20
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "etro",
3
- "version": "0.8.0",
3
+ "version": "0.8.3",
4
4
  "description": "An extendable video-editing framework for the browser and Node",
5
5
  "browser": "dist/etro-cjs.js",
6
6
  "types": "dist/index.d.ts",
@@ -28,19 +28,22 @@
28
28
  "ev": "0.0.7",
29
29
  "http-server": "^0.12.3",
30
30
  "jasmine": "^3.4.0",
31
- "jsdoc": "^3.6.3",
32
- "jsdoc-export-default-interop": "^0.3.1",
33
31
  "karma": "^6.1.1",
34
32
  "karma-chrome-launcher": "^3.1.0",
33
+ "karma-es6-shim": "^1.0.0",
35
34
  "karma-jasmine": "^2.0.1",
35
+ "karma-requirejs": "^1.1.0",
36
36
  "karma-super-dots-reporter": "^0.2.0",
37
+ "keep-a-changelog": "^0.10.4",
37
38
  "puppeteer": "^2.0.0",
39
+ "resemblejs": "^3.2.5",
38
40
  "rollup": "^1.19.4",
39
41
  "rollup-plugin-cleaner": "^1.0.0",
40
42
  "rollup-plugin-eslint": "^7.0.0",
41
43
  "rollup-plugin-node-resolve": "^5.2.0",
42
44
  "rollup-plugin-typescript2": "^0.29.0",
43
45
  "rollup-plugin-uglify-es": "^0.0.1",
46
+ "shipjs": "0.23.3",
44
47
  "typedoc": "^0.20.34",
45
48
  "typescript": "^4.1.3"
46
49
  },
@@ -54,7 +57,8 @@
54
57
  "lint:test": "eslint -c eslint.test-conf.js spec",
55
58
  "lint:examples": "eslint -c eslint.example-conf.js --ext .html examples",
56
59
  "start": "http-server",
57
- "test": "karma start"
60
+ "test": "karma start",
61
+ "release": "shipjs prepare"
58
62
  },
59
63
  "repository": {
60
64
  "type": "git",
@@ -74,9 +74,6 @@
74
74
  'transform/translate.png': new etro.effect.Transform({
75
75
  matrix: new etro.effect.Transform.Matrix().translate(-3, 5)
76
76
  }),
77
- 'transform/translate-fraction.png': new etro.effect.Transform({
78
- matrix: new etro.effect.Transform.Matrix().translate(0.5, 0.5)
79
- }),
80
77
  'transform/scale.png': new etro.effect.Transform({
81
78
  matrix: new etro.effect.Transform.Matrix().scale(2, 2)
82
79
  }),
package/ship.config.js ADDED
@@ -0,0 +1,80 @@
1
+ const { parser } = require('keep-a-changelog')
2
+ const fs = require('fs')
3
+ const semver = require('semver')
4
+
5
+ module.exports = {
6
+ updateChangelog: false,
7
+ formatCommitMessage: ({ version }) => `Release v${version}`,
8
+ formatPullRequestTitle: ({ version }) => `Release v${version}`,
9
+ getNextVersion: ({ currentVersion, dir }) => {
10
+ const changelog = new Changelog(`${dir}/CHANGELOG.md`)
11
+ return changelog.nextVersion(currentVersion)
12
+ },
13
+ versionUpdated: async ({ version, _releaseType, dir, _exec }) => {
14
+ const parsedVersion = semver.parse(version)
15
+ if (parsedVersion.prerelease.length)
16
+ return
17
+
18
+ // Release 'Unreleased' section in changelog
19
+ const changelogFile = `${dir}/CHANGELOG.md`
20
+ const oldChangelog = fs.readFileSync(changelogFile, 'utf8')
21
+ const parsed = parser(oldChangelog)
22
+ const release = parsed.findRelease() // get 'Unreleased' section
23
+ release.setVersion(version) // release
24
+ release.setDate(new Date()) // today
25
+ const newChangelog = parsed.toString()
26
+ fs.writeFileSync(changelogFile, newChangelog, 'utf8')
27
+ }
28
+ }
29
+
30
+ class Changelog {
31
+ constructor (path) {
32
+ const data = fs.readFileSync(path, 'utf8')
33
+ const lines = data.split(/\r?\n/)
34
+ const headings = []
35
+ let unreleased = false
36
+
37
+ this.releaseTag = 'latest'
38
+ lines.every((line) => {
39
+ if (line.startsWith('## [Unreleased]')) {
40
+ unreleased = true
41
+ const tagMatch = line.match(/## \[Unreleased\]\[(.*)\]/)
42
+ if (tagMatch)
43
+ this.releaseTag = tagMatch[1].trim()
44
+ } else if (line.startsWith('## ')) {
45
+ return false
46
+ }
47
+
48
+ if (unreleased)
49
+ if (line.startsWith('### ')) {
50
+ headings.push(line.match(/### (.*)/)[1].trim())
51
+ }
52
+
53
+ return true
54
+ })
55
+
56
+ if (headings.includes('Changed'))
57
+ this.releaseType = 'major'
58
+ else if (headings.includes('Added'))
59
+ this.releaseType = 'minor'
60
+ else
61
+ this.releaseType = 'patch'
62
+ }
63
+
64
+ nextVersion (version) {
65
+ const parsedVersion = semver.parse(version)
66
+
67
+ if (this.releaseTag !== 'latest')
68
+ if (parsedVersion.prerelease.length) {
69
+ parsedVersion.inc('prerelease', this.releaseTag)
70
+ } else {
71
+ parsedVersion.inc(this.releaseType)
72
+ parsedVersion.prerelease = [this.releaseTag, 0]
73
+ parsedVersion.format()
74
+ }
75
+ else
76
+ parsedVersion.inc(this.releaseType)
77
+
78
+ return parsedVersion.version
79
+ }
80
+ }
@@ -31,9 +31,9 @@ export class Base implements BaseObject {
31
31
 
32
32
  // Propogate up to target
33
33
  subscribe(newThis, 'effect.change.modify', event => {
34
- if (!newThis._target) {
34
+ if (!newThis._target)
35
35
  return
36
- }
36
+
37
37
  const type = `${newThis._target.type}.change.effect.modify`
38
38
  publish(newThis._target, type, { ...event, target: newThis._target, source: newThis, type })
39
39
  })
@@ -41,22 +41,41 @@ export class Base implements BaseObject {
41
41
  return newThis
42
42
  }
43
43
 
44
- attach (target: Movie | Visual): void {
44
+ /**
45
+ * Attaches this effect to `target` if not already attached.
46
+ * @ignore
47
+ */
48
+ tryAttach (target: Movie | Visual): void {
49
+ if (this._occurrenceCount === 0)
50
+ this.attach(target)
51
+
45
52
  this._occurrenceCount++
46
- this._target = target
47
53
  }
48
54
 
49
- detach (): void {
50
- if (this._target === null) {
55
+ attach (movie: Movie | Visual): void {
56
+ this._target = movie
57
+ }
58
+
59
+ /**
60
+ * Dettaches this effect from its target if the number of times `tryDetach`
61
+ * has been called (including this call) equals the number of times
62
+ * `tryAttach` has been called.
63
+ *
64
+ * @ignore
65
+ */
66
+ tryDetach (): void {
67
+ if (this._target === null)
51
68
  throw new Error('No movie to detach from')
52
- }
53
69
 
54
70
  this._occurrenceCount--
55
71
  // If this effect occurs in another place in the containing array, do not
56
72
  // unset _target. (For calling `unshift` on the `layers` proxy)
57
- if (this._occurrenceCount === 0) {
58
- this._target = null
59
- }
73
+ if (this._occurrenceCount === 0)
74
+ this.detach()
75
+ }
76
+
77
+ detach (): void {
78
+ this._target = null
60
79
  }
61
80
 
62
81
  // subclasses must implement apply
@@ -63,12 +63,12 @@ class GaussianBlurComponent extends Shader {
63
63
 
64
64
  apply (target: Movie | Visual, reltime: number): void {
65
65
  const radiusVal = val(this, 'radius', reltime)
66
- if (radiusVal !== this._radiusCache) {
66
+ if (radiusVal !== this._radiusCache)
67
67
  // Regenerate gaussian distribution canvas.
68
68
  this.shape = GaussianBlurComponent._render1DKernel(
69
69
  GaussianBlurComponent._gen1DKernel(radiusVal)
70
70
  )
71
- }
71
+
72
72
  this._radiusCache = radiusVal
73
73
 
74
74
  super.apply(target, reltime)
@@ -106,28 +106,28 @@ class GaussianBlurComponent extends Shader {
106
106
  const pascal = GaussianBlurComponent._genPascalRow(2 * radius + 1)
107
107
  // don't use `reduce` and `map` (overhead?)
108
108
  let sum = 0
109
- for (let i = 0; i < pascal.length; i++) {
109
+ for (let i = 0; i < pascal.length; i++)
110
110
  sum += pascal[i]
111
- }
112
- for (let i = 0; i < pascal.length; i++) {
111
+
112
+ for (let i = 0; i < pascal.length; i++)
113
113
  pascal[i] /= sum
114
- }
114
+
115
115
  return pascal
116
116
  }
117
117
 
118
118
  private static _genPascalRow (index: number): number[] {
119
- if (index < 0) {
119
+ if (index < 0)
120
120
  throw new Error(`Invalid index ${index}`)
121
- }
121
+
122
122
  let currRow = [1]
123
123
  for (let i = 1; i < index; i++) {
124
124
  const nextRow = []
125
125
  nextRow.length = currRow.length + 1
126
126
  // edges are always 1's
127
127
  nextRow[0] = nextRow[nextRow.length - 1] = 1
128
- for (let j = 1; j < nextRow.length - 1; j++) {
128
+ for (let j = 1; j < nextRow.length - 1; j++)
129
129
  nextRow[j] = currRow[j - 1] + currRow[j]
130
- }
130
+
131
131
  currRow = nextRow
132
132
  }
133
133
  return currRow
@@ -50,9 +50,8 @@ export class Pixelate extends Shader {
50
50
 
51
51
  apply (target: Movie | Visual, reltime: number): void {
52
52
  const ps = val(this, 'pixelSize', reltime)
53
- if (ps % 1 !== 0 || ps < 0) {
53
+ if (ps % 1 !== 0 || ps < 0)
54
54
  throw new Error('Pixel size must be a nonnegative integer')
55
- }
56
55
 
57
56
  super.apply(target, reltime)
58
57
  }
@@ -117,9 +117,9 @@ export class Shader extends Base {
117
117
  private _initGl () {
118
118
  this._canvas = document.createElement('canvas')
119
119
  const gl = this._canvas.getContext('webgl')
120
- if (gl === null) {
120
+ if (gl === null)
121
121
  throw new Error('Unable to initialize WebGL. Your browser or machine may not support it.')
122
- }
122
+
123
123
  this._gl = gl
124
124
  return gl
125
125
  }
@@ -127,9 +127,9 @@ export class Shader extends Base {
127
127
  private _initTextures (userUniforms, userTextures, sourceTextureOptions) {
128
128
  const gl = this._gl
129
129
  const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)
130
- if (userTextures.length > maxTextures) {
130
+ if (userTextures.length > maxTextures)
131
131
  console.warn('Too many textures!')
132
- }
132
+
133
133
  this._userTextures = {}
134
134
  for (const name in userTextures) {
135
135
  const userOptions: TextureOptions = userTextures[name]
@@ -143,9 +143,9 @@ export class Shader extends Base {
143
143
  * textures, without having to define multiple properties in the effect
144
144
  * object.
145
145
  */
146
- if (userUniforms[name]) {
146
+ if (userUniforms[name])
147
147
  throw new Error(`Texture - uniform naming conflict: ${name}!`)
148
- }
148
+
149
149
  // Add this as a "user uniform".
150
150
  userUniforms[name] = '1i' // texture pointer
151
151
  }
@@ -309,14 +309,12 @@ export class Shader extends Base {
309
309
 
310
310
  // Tell the shader we bound the texture to texture unit 0.
311
311
  // All base (Shader class) uniforms are optional.
312
- if (this._uniformLocations.source) {
312
+ if (this._uniformLocations.source)
313
313
  gl.uniform1i(this._uniformLocations.source, 0)
314
- }
315
314
 
316
315
  // All base (Shader class) uniforms are optional.
317
- if (this._uniformLocations.size) {
316
+ if (this._uniformLocations.size)
318
317
  gl.uniform2iv(this._uniformLocations.size, [target.canvas.width, target.canvas.height])
319
- }
320
318
 
321
319
  for (const unprefixed in this._userUniforms) {
322
320
  const options = this._userUniforms[unprefixed] as UniformOptions
@@ -371,43 +369,41 @@ export class Shader extends Base {
371
369
  let i = 0
372
370
  for (const name in this._userTextures) {
373
371
  const testValue = val(this, name, reltime)
374
- if (value === testValue) {
372
+ if (value === testValue)
375
373
  value = Shader.INTERNAL_TEXTURE_UNITS + i // after the internal texture units
376
- }
374
+
377
375
  i++
378
376
  }
379
377
  }
380
378
 
381
379
  if (outputType === '3fv') {
382
380
  // allow 4-component vectors; TODO: why?
383
- if (Array.isArray(value) && (value.length === 3 || value.length === 4)) {
381
+ if (Array.isArray(value) && (value.length === 3 || value.length === 4))
384
382
  return value
385
- }
383
+
386
384
  // kind of loose so this can be changed if needed
387
- if (typeof value === 'object') {
385
+ if (typeof value === 'object')
388
386
  return [
389
387
  value.r !== undefined ? value.r : def,
390
388
  value.g !== undefined ? value.g : def,
391
389
  value.b !== undefined ? value.b : def
392
390
  ]
393
- }
394
391
 
395
392
  throw new Error(`Invalid type: ${outputType} or value: ${value}`)
396
393
  }
397
394
 
398
395
  if (outputType === '4fv') {
399
- if (Array.isArray(value) && value.length === 4) {
396
+ if (Array.isArray(value) && value.length === 4)
400
397
  return value
401
- }
398
+
402
399
  // kind of loose so this can be changed if needed
403
- if (typeof value === 'object') {
400
+ if (typeof value === 'object')
404
401
  return [
405
402
  value.r !== undefined ? value.r : def,
406
403
  value.g !== undefined ? value.g : def,
407
404
  value.b !== undefined ? value.b : def,
408
405
  value.a !== undefined ? value.a : def
409
406
  ]
410
- }
411
407
 
412
408
  throw new Error(`Invalid type: ${outputType} or value: ${value}`)
413
409
  }
@@ -513,9 +509,9 @@ export class Shader extends Base {
513
509
  } else {
514
510
  // No, it's not a power of 2. Turn off mips and set
515
511
  // wrapping to clamp to edge
516
- if (wrapS !== gl.CLAMP_TO_EDGE || wrapT !== gl.CLAMP_TO_EDGE) {
512
+ if (wrapS !== gl.CLAMP_TO_EDGE || wrapT !== gl.CLAMP_TO_EDGE)
517
513
  console.warn('Wrap mode is not CLAMP_TO_EDGE for a non-power-of-two texture. Defaulting to CLAMP_TO_EDGE')
518
- }
514
+
519
515
  gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
520
516
  gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
521
517
  }
@@ -19,6 +19,7 @@ export class Stack extends Base {
19
19
  super()
20
20
 
21
21
  this._effectsBack = []
22
+ // TODO: Throw 'change' events in handlers
22
23
  this.effects = new Proxy(this._effectsBack, {
23
24
  deleteProperty: function (target: Base[], property: number | string): boolean {
24
25
  const value = target[property]
@@ -29,9 +30,9 @@ export class Stack extends Base {
29
30
  set: function (target: Base[], property: number | string, value: Base): boolean {
30
31
  // TODO: make sure type check works
31
32
  if (!isNaN(Number(property))) { // if property is a number (index)
32
- if (target[property]) {
33
+ if (target[property])
33
34
  target[property].detach() // Detach old effect from movie
34
- }
35
+
35
36
  value.attach(this._target) // Attach effect to movie
36
37
  }
37
38
  target[property] = value
@@ -39,11 +40,13 @@ export class Stack extends Base {
39
40
  }
40
41
  })
41
42
  options.effects.forEach(effect => this.effects.push(effect))
43
+
44
+ // TODO: Propogate 'change' events from children up
42
45
  }
43
46
 
44
47
  attach (movie: Movie): void {
45
48
  super.attach(movie)
46
- this.effects.forEach(effect => {
49
+ this.effects.filter(effect => !!effect).forEach(effect => {
47
50
  effect.detach()
48
51
  effect.attach(movie)
49
52
  })
@@ -51,7 +54,7 @@ export class Stack extends Base {
51
54
 
52
55
  detach (): void {
53
56
  super.detach()
54
- this.effects.forEach(effect => {
57
+ this.effects.filter(effect => !!effect).forEach(effect => {
55
58
  effect.detach()
56
59
  })
57
60
  }
@@ -59,6 +62,7 @@ export class Stack extends Base {
59
62
  apply (target: Movie | Visual, reltime: number): void {
60
63
  for (let i = 0; i < this.effects.length; i++) {
61
64
  const effect = this.effects[i]
65
+ if (!effect) continue
62
66
  effect.apply(target, reltime)
63
67
  }
64
68
  }
@@ -36,12 +36,12 @@ class Transform extends Base {
36
36
  }
37
37
 
38
38
  apply (target: Movie | Visual, reltime: number): void {
39
- if (target.canvas.width !== this._tmpCanvas.width) {
39
+ if (target.canvas.width !== this._tmpCanvas.width)
40
40
  this._tmpCanvas.width = target.canvas.width
41
- }
42
- if (target.canvas.height !== this._tmpCanvas.height) {
41
+
42
+ if (target.canvas.height !== this._tmpCanvas.height)
43
43
  this._tmpCanvas.height = target.canvas.height
44
- }
44
+
45
45
  // Use data, since that's the underlying storage
46
46
  this._tmpMatrix.data = val(this, 'matrix.data', reltime)
47
47
 
@@ -81,9 +81,8 @@ namespace Transform { // eslint-disable-line @typescript-eslint/no-namespace
81
81
  }
82
82
 
83
83
  identity (): Matrix {
84
- for (let i = 0; i < this.data.length; i++) {
84
+ for (let i = 0; i < this.data.length; i++)
85
85
  this.data[i] = Matrix.IDENTITY.data[i]
86
- }
87
86
 
88
87
  return this
89
88
  }
@@ -94,9 +93,9 @@ namespace Transform { // eslint-disable-line @typescript-eslint/no-namespace
94
93
  * @param [val]
95
94
  */
96
95
  cell (x: number, y: number, val?: number): number {
97
- if (val !== undefined) {
96
+ if (val !== undefined)
98
97
  this.data[3 * y + x] = val
99
- }
98
+
100
99
  return this.data[3 * y + x]
101
100
  }
102
101
 
@@ -131,19 +130,19 @@ namespace Transform { // eslint-disable-line @typescript-eslint/no-namespace
131
130
  */
132
131
  multiply (other: Matrix): Matrix {
133
132
  // copy to temporary matrix to avoid modifying `this` while reading from it
134
- for (let x = 0; x < 3; x++) {
133
+ for (let x = 0; x < 3; x++)
135
134
  for (let y = 0; y < 3; y++) {
136
135
  let sum = 0
137
- for (let i = 0; i < 3; i++) {
136
+ for (let i = 0; i < 3; i++)
138
137
  sum += this.cell(x, i) * other.cell(i, y)
139
- }
138
+
140
139
  Matrix._TMP_MATRIX.cell(x, y, sum)
141
140
  }
142
- }
141
+
143
142
  // copy data from TMP_MATRIX to this
144
- for (let i = 0; i < Matrix._TMP_MATRIX.data.length; i++) {
143
+ for (let i = 0; i < Matrix._TMP_MATRIX.data.length; i++)
145
144
  this.data[i] = Matrix._TMP_MATRIX.data[i]
146
- }
145
+
147
146
  return this
148
147
  }
149
148