etro 0.8.0 → 0.8.1

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 (95) hide show
  1. package/.github/workflows/nodejs.yml +3 -1
  2. package/CHANGELOG.md +9 -1
  3. package/CODE_OF_CONDUCT.md +5 -5
  4. package/CONTRIBUTING.md +22 -72
  5. package/README.md +1 -1
  6. package/dist/effect/base.d.ts +14 -1
  7. package/dist/etro-cjs.js +156 -213
  8. package/dist/etro-iife.js +156 -213
  9. package/dist/layer/base.d.ts +13 -0
  10. package/eslint.conf.js +2 -1
  11. package/eslint.test-conf.js +1 -0
  12. package/examples/application/readme-screenshot.html +4 -8
  13. package/examples/application/video-player.html +3 -4
  14. package/examples/introduction/effects.html +23 -4
  15. package/karma.conf.js +4 -2
  16. package/package.json +4 -1
  17. package/scripts/gen-effect-samples.html +0 -3
  18. package/src/effect/base.ts +29 -10
  19. package/src/effect/gaussian-blur.ts +10 -10
  20. package/src/effect/pixelate.ts +1 -2
  21. package/src/effect/shader.ts +18 -22
  22. package/src/effect/stack.ts +5 -2
  23. package/src/effect/transform.ts +13 -14
  24. package/src/event.ts +8 -14
  25. package/src/layer/audio-source.ts +16 -14
  26. package/src/layer/audio.ts +1 -2
  27. package/src/layer/base.ts +26 -7
  28. package/src/layer/visual.ts +5 -6
  29. package/src/movie.ts +41 -49
  30. package/src/util.ts +50 -57
  31. package/docs/effect.js.html +0 -1215
  32. package/docs/event.js.html +0 -145
  33. package/docs/index.html +0 -81
  34. package/docs/index.js.html +0 -92
  35. package/docs/layer.js.html +0 -888
  36. package/docs/module-effect-GaussianBlurComponent.html +0 -345
  37. package/docs/module-effect.Brightness.html +0 -339
  38. package/docs/module-effect.Channels.html +0 -319
  39. package/docs/module-effect.ChromaKey.html +0 -611
  40. package/docs/module-effect.Contrast.html +0 -339
  41. package/docs/module-effect.EllipticalMask.html +0 -200
  42. package/docs/module-effect.GaussianBlur.html +0 -202
  43. package/docs/module-effect.GaussianBlurHorizontal.html +0 -242
  44. package/docs/module-effect.GaussianBlurVertical.html +0 -242
  45. package/docs/module-effect.Pixelate.html +0 -330
  46. package/docs/module-effect.Shader.html +0 -1227
  47. package/docs/module-effect.Stack.html +0 -406
  48. package/docs/module-effect.Transform.Matrix.html +0 -193
  49. package/docs/module-effect.Transform.html +0 -1174
  50. package/docs/module-effect.html +0 -148
  51. package/docs/module-event.html +0 -473
  52. package/docs/module-index.html +0 -186
  53. package/docs/module-layer-Media.html +0 -1116
  54. package/docs/module-layer-MediaMixin.html +0 -164
  55. package/docs/module-layer.Audio.html +0 -1188
  56. package/docs/module-layer.Base.html +0 -629
  57. package/docs/module-layer.Image.html +0 -1421
  58. package/docs/module-layer.Text.html +0 -1731
  59. package/docs/module-layer.Video.html +0 -1938
  60. package/docs/module-layer.Visual.html +0 -1698
  61. package/docs/module-layer.html +0 -137
  62. package/docs/module-movie.html +0 -3118
  63. package/docs/module-util.Color.html +0 -702
  64. package/docs/module-util.Font.html +0 -395
  65. package/docs/module-util.html +0 -845
  66. package/docs/movie.js.html +0 -689
  67. package/docs/scripts/collapse.js +0 -20
  68. package/docs/scripts/linenumber.js +0 -25
  69. package/docs/scripts/nav.js +0 -12
  70. package/docs/scripts/polyfill.js +0 -4
  71. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -202
  72. package/docs/scripts/prettify/lang-css.js +0 -2
  73. package/docs/scripts/prettify/prettify.js +0 -28
  74. package/docs/scripts/search.js +0 -83
  75. package/docs/styles/jsdoc.css +0 -671
  76. package/docs/styles/prettify.css +0 -79
  77. package/docs/util.js.html +0 -503
  78. package/spec/assets/effect/gaussian-blur-horizontal.png +0 -0
  79. package/spec/assets/effect/gaussian-blur-vertical.png +0 -0
  80. package/spec/assets/effect/grayscale.png +0 -0
  81. package/spec/assets/effect/original.png +0 -0
  82. package/spec/assets/effect/pixelate.png +0 -0
  83. package/spec/assets/effect/transform/multiply.png +0 -0
  84. package/spec/assets/effect/transform/rotate.png +0 -0
  85. package/spec/assets/effect/transform/scale-fraction.png +0 -0
  86. package/spec/assets/effect/transform/scale.png +0 -0
  87. package/spec/assets/effect/transform/translate-fraction.png +0 -0
  88. package/spec/assets/effect/transform/translate.png +0 -0
  89. package/spec/assets/layer/audio.wav +0 -0
  90. package/spec/assets/layer/image.jpg +0 -0
  91. package/spec/effect.spec.js +0 -421
  92. package/spec/event.spec.js +0 -39
  93. package/spec/layer.spec.js +0 -307
  94. package/spec/movie.spec.js +0 -346
  95. package/spec/util.spec.js +0 -294
package/dist/etro-iife.js CHANGED
@@ -53,14 +53,11 @@ var etro = (function () {
53
53
  this._parts = id.split('.');
54
54
  }
55
55
  TypeId.prototype.contains = function (other) {
56
- if (other._parts.length > this._parts.length) {
56
+ if (other._parts.length > this._parts.length)
57
57
  return false;
58
- }
59
- for (var i = 0; i < other._parts.length; i++) {
60
- if (other._parts[i] !== this._parts[i]) {
58
+ for (var i = 0; i < other._parts.length; i++)
59
+ if (other._parts[i] !== this._parts[i])
61
60
  return false;
62
- }
63
- }
64
61
  return true;
65
62
  };
66
63
  TypeId.prototype.toString = function () {
@@ -77,9 +74,8 @@ var etro = (function () {
77
74
  * @param listener
78
75
  */
79
76
  function subscribe(target, type, listener) {
80
- if (!listeners.has(target)) {
77
+ if (!listeners.has(target))
81
78
  listeners.set(target, []);
82
- }
83
79
  listeners.get(target).push({ type: new TypeId(type), listener: listener });
84
80
  }
85
81
  /**
@@ -93,9 +89,8 @@ var etro = (function () {
93
89
  function unsubscribe(target, listener) {
94
90
  // Make sure `listener` has been added with `subscribe`.
95
91
  if (!listeners.has(target) ||
96
- !listeners.get(target).map(function (pair) { return pair.listener; }).includes(listener)) {
92
+ !listeners.get(target).map(function (pair) { return pair.listener; }).includes(listener))
97
93
  throw new Error('No matching event listener to remove');
98
- }
99
94
  var removed = listeners.get(target)
100
95
  .filter(function (pair) { return pair.listener !== listener; });
101
96
  listeners.set(target, removed);
@@ -112,17 +107,15 @@ var etro = (function () {
112
107
  event.target = target; // could be a proxy
113
108
  event.type = type;
114
109
  var t = new TypeId(type);
115
- if (!listeners.has(target)) {
110
+ if (!listeners.has(target))
116
111
  // No event fired
117
112
  return null;
118
- }
119
113
  // Call event listeners for this event.
120
114
  var listenersForType = [];
121
115
  for (var i = 0; i < listeners.get(target).length; i++) {
122
116
  var item = listeners.get(target)[i];
123
- if (t.contains(item.type)) {
117
+ if (t.contains(item.type))
124
118
  listenersForType.push(item.listener);
125
- }
126
119
  }
127
120
  for (var i = 0; i < listenersForType.length; i++) {
128
121
  var listener = listenersForType[i];
@@ -150,9 +143,8 @@ var etro = (function () {
150
143
  function getPropertyDescriptor(obj, name) {
151
144
  do {
152
145
  var propDesc = Object.getOwnPropertyDescriptor(obj, name);
153
- if (propDesc) {
146
+ if (propDesc)
154
147
  return propDesc;
155
- }
156
148
  obj = Object.getPrototypeOf(obj);
157
149
  } while (obj);
158
150
  return undefined;
@@ -167,35 +159,30 @@ var etro = (function () {
167
159
  function applyOptions(options, destObj) {
168
160
  var defaultOptions = destObj.getDefaultOptions();
169
161
  // Validate; make sure `keys` doesn't have any extraneous items
170
- for (var option in options) {
162
+ for (var option in options)
171
163
  // eslint-disable-next-line no-prototype-builtins
172
- if (!defaultOptions.hasOwnProperty(option)) {
164
+ if (!defaultOptions.hasOwnProperty(option))
173
165
  throw new Error("Invalid option: '" + option + "'");
174
- }
175
- }
176
166
  // Merge options and defaultOptions
177
167
  options = __assign(__assign({}, defaultOptions), options);
178
168
  // Copy options
179
169
  for (var option in options) {
180
170
  var propDesc = getPropertyDescriptor(destObj, option);
181
171
  // Update the property as long as the property has not been set (unless if it has a setter)
182
- if (!propDesc || propDesc.set) {
172
+ if (!propDesc || propDesc.set)
183
173
  destObj[option] = options[option];
184
- }
185
174
  }
186
175
  }
187
176
  // This must be cleared at the start of each frame
188
177
  var valCache = new WeakMap();
189
178
  function cacheValue(element, path, value) {
190
179
  // Initiate movie cache
191
- if (!valCache.has(element.movie)) {
180
+ if (!valCache.has(element.movie))
192
181
  valCache.set(element.movie, new WeakMap());
193
- }
194
182
  var movieCache = valCache.get(element.movie);
195
183
  // Iniitate element cache
196
- if (!movieCache.has(element)) {
184
+ if (!movieCache.has(element))
197
185
  movieCache.set(element, {});
198
- }
199
186
  var elementCache = movieCache.get(element);
200
187
  // Cache the value
201
188
  elementCache[path] = value;
@@ -235,16 +222,13 @@ var etro = (function () {
235
222
  return this;
236
223
  };
237
224
  KeyFrame.prototype.evaluate = function (time) {
238
- if (this.value.length === 0) {
225
+ if (this.value.length === 0)
239
226
  throw new Error('Empty keyframe');
240
- }
241
- if (time === undefined) {
227
+ if (time === undefined)
242
228
  throw new Error('|time| is undefined or null');
243
- }
244
229
  var firstTime = this.value[0][0];
245
- if (time < firstTime) {
230
+ if (time < firstTime)
246
231
  throw new Error('No keyframe point before |time|');
247
- }
248
232
  // I think reduce are slow to do per-frame (or more)?
249
233
  for (var i = 0; i < this.value.length; i++) {
250
234
  var startTime = this.value[i][0];
@@ -253,7 +237,7 @@ var etro = (function () {
253
237
  if (i + 1 < this.value.length) {
254
238
  var endTime = this.value[i + 1][0];
255
239
  var endValue = this.value[i + 1][1];
256
- if (startTime <= time && time < endTime) {
240
+ if (startTime <= time && time < endTime)
257
241
  // No need for endValue if it is flat interpolation
258
242
  // TODO: support custom interpolation for 'other' types?
259
243
  if (!(typeof startValue === 'number' || typeof endValue === 'object')) {
@@ -269,7 +253,6 @@ var etro = (function () {
269
253
  endValue, // eslint-disable-line @typescript-eslint/ban-types
270
254
  percentProgress, this.interpolationKeys);
271
255
  }
272
- }
273
256
  }
274
257
  else {
275
258
  // Repeat last value forever
@@ -295,28 +278,23 @@ var etro = (function () {
295
278
  // TODO: Is this function efficient?
296
279
  // TODO: Update doc @params to allow for keyframes
297
280
  function val(element, path, time) {
298
- if (hasCachedValue(element, path)) {
281
+ if (hasCachedValue(element, path))
299
282
  return getCachedValue(element, path);
300
- }
301
283
  // Get property of element at path
302
284
  var pathParts = path.split('.');
303
285
  var property = element[pathParts.shift()];
304
- while (pathParts.length > 0) {
286
+ while (pathParts.length > 0)
305
287
  property = property[pathParts.shift()];
306
- }
307
288
  // Property filter function
308
289
  var process = element.propertyFilters[path];
309
290
  var value;
310
- if (property instanceof KeyFrame) {
291
+ if (property instanceof KeyFrame)
311
292
  value = property.evaluate(time);
312
- }
313
- else if (typeof property === 'function') {
293
+ else if (typeof property === 'function')
314
294
  value = property(element, time); // TODO? add more args
315
- }
316
- else {
295
+ else
317
296
  // Simple value
318
297
  value = property;
319
- }
320
298
  return cacheValue(element, path, process ? process.call(element, value) : value);
321
299
  }
322
300
  /* export function floorInterp(x1, x2, t, objectKeys) {
@@ -327,18 +305,15 @@ var etro = (function () {
327
305
  }, Object.create(Object.getPrototypeOf(x1)));
328
306
  } */
329
307
  function linearInterp(x1, x2, t, objectKeys) {
330
- if (typeof x1 !== typeof x2) {
308
+ if (typeof x1 !== typeof x2)
331
309
  throw new Error('Type mismatch');
332
- }
333
- if (typeof x1 !== 'number' && typeof x1 !== 'object') {
310
+ if (typeof x1 !== 'number' && typeof x1 !== 'object')
334
311
  // Flat interpolation (floor)
335
312
  return x1;
336
- }
337
313
  if (typeof x1 === 'object') { // to work with objects (including arrays)
338
314
  // TODO: make this code DRY
339
- if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2)) {
315
+ if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2))
340
316
  throw new Error('Prototype mismatch');
341
- }
342
317
  // Preserve prototype of objects
343
318
  var int = Object.create(Object.getPrototypeOf(x1));
344
319
  // Take the intersection of properties
@@ -346,9 +321,8 @@ var etro = (function () {
346
321
  for (var i = 0; i < keys.length; i++) {
347
322
  var key = keys[i];
348
323
  // eslint-disable-next-line no-prototype-builtins
349
- if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key)) {
324
+ if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key))
350
325
  continue;
351
- }
352
326
  int[key] = linearInterp(x1[key], x2[key], t);
353
327
  }
354
328
  return int;
@@ -356,17 +330,14 @@ var etro = (function () {
356
330
  return (1 - t) * x1 + t * x2;
357
331
  }
358
332
  function cosineInterp(x1, x2, t, objectKeys) {
359
- if (typeof x1 !== typeof x2) {
333
+ if (typeof x1 !== typeof x2)
360
334
  throw new Error('Type mismatch');
361
- }
362
- if (typeof x1 !== 'number' && typeof x1 !== 'object') {
335
+ if (typeof x1 !== 'number' && typeof x1 !== 'object')
363
336
  // Flat interpolation (floor)
364
337
  return x1;
365
- }
366
338
  if (typeof x1 === 'object' && typeof x2 === 'object') { // to work with objects (including arrays)
367
- if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2)) {
339
+ if (Object.getPrototypeOf(x1) !== Object.getPrototypeOf(x2))
368
340
  throw new Error('Prototype mismatch');
369
- }
370
341
  // Preserve prototype of objects
371
342
  var int = Object.create(Object.getPrototypeOf(x1));
372
343
  // Take the intersection of properties
@@ -374,9 +345,8 @@ var etro = (function () {
374
345
  for (var i = 0; i < keys.length; i++) {
375
346
  var key = keys[i];
376
347
  // eslint-disable-next-line no-prototype-builtins
377
- if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key)) {
348
+ if (!x1.hasOwnProperty(key) || !x2.hasOwnProperty(key))
378
349
  continue;
379
- }
380
350
  int[key] = cosineInterp(x1[key], x2[key], t);
381
351
  }
382
352
  return int;
@@ -506,12 +476,10 @@ var etro = (function () {
506
476
  width = width || canvas.width;
507
477
  height = height || canvas.height;
508
478
  var frame = ctx.getImageData(x, y, width, height);
509
- for (var i = 0, l = frame.data.length; i < l; i += 4) {
479
+ for (var i = 0, l = frame.data.length; i < l; i += 4)
510
480
  mapper(frame.data, i);
511
- }
512
- if (flush) {
481
+ if (flush)
513
482
  ctx.putImageData(frame, x, y);
514
- }
515
483
  }
516
484
  /**
517
485
  * <p>Emits "change" event when public properties updated, recursively.
@@ -528,17 +496,17 @@ var etro = (function () {
528
496
  // Public API property updated, emit 'modify' event.
529
497
  publish(proxy, target.type + ".change.modify", { property: getPath(receiver, prop), newValue: val });
530
498
  };
531
- var check = function (prop) { return !(prop.startsWith('_') || target.publicExcludes.includes(prop)); };
499
+ var canWatch = function (receiver, prop) { return !prop.startsWith('_') &&
500
+ (target.publicExcludes === undefined || !target.publicExcludes.includes(prop)); };
532
501
  // The path to each child property (each is a unique proxy)
533
502
  var paths = new WeakMap();
534
503
  var handler = {
535
504
  set: function (obj, prop, val, receiver) {
536
505
  // Recurse
537
- if (typeof val === 'object' && val !== null && !paths.has(val) && check(prop)) {
506
+ if (typeof val === 'object' && val !== null && !paths.has(val) && canWatch(receiver, prop)) {
538
507
  val = new Proxy(val, handler);
539
508
  paths.set(val, getPath(receiver, prop));
540
509
  }
541
- var was = prop in obj;
542
510
  // Set property or attribute
543
511
  // Search prototype chain for the closest setter
544
512
  var objProto = obj;
@@ -550,15 +518,12 @@ var etro = (function () {
550
518
  break;
551
519
  }
552
520
  }
553
- if (!objProto) {
521
+ if (!objProto)
554
522
  // Couldn't find setter; set value on instance
555
523
  obj[prop] = val;
556
- }
557
- // Check if it already existed and if it's a valid property to watch, if
558
- // on root object.
559
- if (obj !== target || (was && check(prop))) {
524
+ // Check if the property isn't blacklisted in publicExcludes.
525
+ if (canWatch(receiver, prop))
560
526
  callback(prop, val, receiver);
561
- }
562
527
  return true;
563
528
  }
564
529
  };
@@ -598,23 +563,20 @@ var etro = (function () {
598
563
  applyOptions(options, _this);
599
564
  var load = function () {
600
565
  // TODO: && ?
601
- if ((options.duration || (_this.source.duration - _this.sourceStartTime)) < 0) {
566
+ if ((options.duration || (_this.source.duration - _this.sourceStartTime)) < 0)
602
567
  throw new Error('Invalid options.duration or options.sourceStartTime');
603
- }
604
568
  _this._unstretchedDuration = options.duration || (_this.source.duration - _this.sourceStartTime);
605
569
  _this.duration = _this._unstretchedDuration / (_this.playbackRate);
606
570
  // onload will use `this`, and can't bind itself because it's before
607
571
  // super()
608
572
  onload && onload.bind(_this)(_this.source, options);
609
573
  };
610
- if (_this.source.readyState >= 2) {
574
+ if (_this.source.readyState >= 2)
611
575
  // this frame's data is available now
612
576
  load();
613
- }
614
- else {
577
+ else
615
578
  // when this frame's data is available
616
579
  _this.source.addEventListener('loadedmetadata', load);
617
- }
618
580
  _this.source.addEventListener('durationchange', function () {
619
581
  _this.duration = options.duration || (_this.source.duration - _this.sourceStartTime);
620
582
  });
@@ -624,11 +586,9 @@ var etro = (function () {
624
586
  var _this = this;
625
587
  _super.prototype.attach.call(this, movie);
626
588
  subscribe(movie, 'movie.seek', function () {
627
- var time = movie.currentTime;
628
- if (time < _this.startTime || time >= _this.startTime + _this.duration) {
589
+ if (_this.currentTime < 0 || _this.currentTime >= _this.duration)
629
590
  return;
630
- }
631
- _this.source.currentTime = time - _this.startTime;
591
+ _this.source.currentTime = _this.currentTime + _this.sourceStartTime;
632
592
  });
633
593
  // TODO: on unattach?
634
594
  subscribe(movie, 'movie.audiodestinationupdate', function (event) {
@@ -640,7 +600,7 @@ var etro = (function () {
640
600
  }
641
601
  });
642
602
  // connect to audiocontext
643
- this._audioNode = movie.actx.createMediaElementSource(this.source);
603
+ this._audioNode = this.audioNode || movie.actx.createMediaElementSource(this.source);
644
604
  // Spy on connect and disconnect to remember if it connected to
645
605
  // actx.destination (for Movie#record).
646
606
  var oldConnect = this._audioNode.connect.bind(this.audioNode);
@@ -651,14 +611,16 @@ var etro = (function () {
651
611
  var oldDisconnect = this._audioNode.disconnect.bind(this.audioNode);
652
612
  this._audioNode.disconnect = function (destination, output, input) {
653
613
  if (_this._connectedToDestination &&
654
- destination === movie.actx.destination) {
614
+ destination === movie.actx.destination)
655
615
  _this._connectedToDestination = false;
656
- }
657
616
  return oldDisconnect(destination, output, input);
658
617
  };
659
618
  // Connect to actx.destination by default (can be rewired by user)
660
619
  this.audioNode.connect(movie.actx.destination);
661
620
  };
621
+ MixedAudioSource.prototype.detach = function () {
622
+ this.audioNode.disconnect(this.movie.actx.destination);
623
+ };
662
624
  MixedAudioSource.prototype.start = function () {
663
625
  this.source.currentTime = this.currentTime + this.sourceStartTime;
664
626
  this.source.play();
@@ -690,9 +652,8 @@ var etro = (function () {
690
652
  },
691
653
  set: function (value) {
692
654
  this._playbackRate = value;
693
- if (this._unstretchedDuration !== undefined) {
655
+ if (this._unstretchedDuration !== undefined)
694
656
  this.duration = this._unstretchedDuration / value;
695
- }
696
657
  },
697
658
  enumerable: false,
698
659
  configurable: true
@@ -772,20 +733,36 @@ var etro = (function () {
772
733
  });
773
734
  return newThis;
774
735
  }
775
- Base.prototype.attach = function (movie) {
736
+ /**
737
+ * Attaches this layer to `movie` if not already attached.
738
+ * @ignore
739
+ */
740
+ Base.prototype.tryAttach = function (movie) {
741
+ if (this._occurrenceCount === 0)
742
+ this.attach(movie);
776
743
  this._occurrenceCount++;
744
+ };
745
+ Base.prototype.attach = function (movie) {
777
746
  this._movie = movie;
778
747
  };
779
- Base.prototype.detach = function () {
780
- if (this.movie === null) {
748
+ /**
749
+ * Dettaches this layer from its movie if the number of times `tryDetach` has
750
+ * been called (including this call) equals the number of times `tryAttach`
751
+ * has been called.
752
+ *
753
+ * @ignore
754
+ */
755
+ Base.prototype.tryDetach = function () {
756
+ if (this.movie === null)
781
757
  throw new Error('No movie to detach from');
782
- }
783
758
  this._occurrenceCount--;
784
759
  // If this layer occurs in another place in a `layers` array, do not unset
785
760
  // _movie. (For calling `unshift` on the `layers` proxy)
786
- if (this._occurrenceCount === 0) {
787
- this._movie = null;
788
- }
761
+ if (this._occurrenceCount === 0)
762
+ this.detach();
763
+ };
764
+ Base.prototype.detach = function () {
765
+ this._movie = null;
789
766
  };
790
767
  /**
791
768
  * Called when the layer is activated
@@ -874,9 +851,8 @@ var etro = (function () {
874
851
  */
875
852
  function Audio(options) {
876
853
  var _this = _super.call(this, options) || this;
877
- if (_this.duration === undefined) {
854
+ if (_this.duration === undefined)
878
855
  _this.duration = (_this).source.duration - _this.sourceStartTime;
879
- }
880
856
  return _this;
881
857
  }
882
858
  Audio.prototype.getDefaultOptions = function () {
@@ -914,9 +890,8 @@ var etro = (function () {
914
890
  set: function (target, property, value) {
915
891
  if (!isNaN(Number(property))) {
916
892
  // The property is a number (index)
917
- if (target[property]) {
893
+ if (target[property])
918
894
  target[property].detach();
919
- }
920
895
  value.attach(_this);
921
896
  }
922
897
  target[property] = value;
@@ -959,18 +934,16 @@ var etro = (function () {
959
934
  Visual.prototype.endRender = function () {
960
935
  var w = val(this, 'width', this.currentTime) || val(this.movie, 'width', this.movie.currentTime);
961
936
  var h = val(this, 'height', this.currentTime) || val(this.movie, 'height', this.movie.currentTime);
962
- if (w * h > 0) {
937
+ if (w * h > 0)
963
938
  this._applyEffects();
964
- }
965
939
  // else InvalidStateError for drawing zero-area image in some effects, right?
966
940
  };
967
941
  Visual.prototype._applyEffects = function () {
968
942
  for (var i = 0; i < this.effects.length; i++) {
969
943
  var effect = this.effects[i];
970
- if (effect.enabled) {
944
+ if (effect.enabled)
971
945
  // Pass relative time
972
946
  effect.apply(this, this.movie.currentTime - this.startTime);
973
- }
974
947
  }
975
948
  };
976
949
  /**
@@ -1214,28 +1187,43 @@ var etro = (function () {
1214
1187
  newThis._target = null;
1215
1188
  // Propogate up to target
1216
1189
  subscribe(newThis, 'effect.change.modify', function (event) {
1217
- if (!newThis._target) {
1190
+ if (!newThis._target)
1218
1191
  return;
1219
- }
1220
1192
  var type = newThis._target.type + ".change.effect.modify";
1221
1193
  publish(newThis._target, type, __assign(__assign({}, event), { target: newThis._target, source: newThis, type: type }));
1222
1194
  });
1223
1195
  return newThis;
1224
1196
  }
1225
- Base.prototype.attach = function (target) {
1197
+ /**
1198
+ * Attaches this effect to `target` if not already attached.
1199
+ * @ignore
1200
+ */
1201
+ Base.prototype.tryAttach = function (target) {
1202
+ if (this._occurrenceCount === 0)
1203
+ this.attach(target);
1226
1204
  this._occurrenceCount++;
1227
- this._target = target;
1228
1205
  };
1229
- Base.prototype.detach = function () {
1230
- if (this._target === null) {
1206
+ Base.prototype.attach = function (movie) {
1207
+ this._target = movie;
1208
+ };
1209
+ /**
1210
+ * Dettaches this effect from its target if the number of times `tryDetach`
1211
+ * has been called (including this call) equals the number of times
1212
+ * `tryAttach` has been called.
1213
+ *
1214
+ * @ignore
1215
+ */
1216
+ Base.prototype.tryDetach = function () {
1217
+ if (this._target === null)
1231
1218
  throw new Error('No movie to detach from');
1232
- }
1233
1219
  this._occurrenceCount--;
1234
1220
  // If this effect occurs in another place in the containing array, do not
1235
1221
  // unset _target. (For calling `unshift` on the `layers` proxy)
1236
- if (this._occurrenceCount === 0) {
1237
- this._target = null;
1238
- }
1222
+ if (this._occurrenceCount === 0)
1223
+ this.detach();
1224
+ };
1225
+ Base.prototype.detach = function () {
1226
+ this._target = null;
1239
1227
  };
1240
1228
  // subclasses must implement apply
1241
1229
  /**
@@ -1315,18 +1303,16 @@ var etro = (function () {
1315
1303
  Shader.prototype._initGl = function () {
1316
1304
  this._canvas = document.createElement('canvas');
1317
1305
  var gl = this._canvas.getContext('webgl');
1318
- if (gl === null) {
1306
+ if (gl === null)
1319
1307
  throw new Error('Unable to initialize WebGL. Your browser or machine may not support it.');
1320
- }
1321
1308
  this._gl = gl;
1322
1309
  return gl;
1323
1310
  };
1324
1311
  Shader.prototype._initTextures = function (userUniforms, userTextures, sourceTextureOptions) {
1325
1312
  var gl = this._gl;
1326
1313
  var maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
1327
- if (userTextures.length > maxTextures) {
1314
+ if (userTextures.length > maxTextures)
1328
1315
  console.warn('Too many textures!');
1329
- }
1330
1316
  this._userTextures = {};
1331
1317
  for (var name_1 in userTextures) {
1332
1318
  var userOptions = userTextures[name_1];
@@ -1339,9 +1325,8 @@ var etro = (function () {
1339
1325
  * textures, without having to define multiple properties in the effect
1340
1326
  * object.
1341
1327
  */
1342
- if (userUniforms[name_1]) {
1328
+ if (userUniforms[name_1])
1343
1329
  throw new Error("Texture - uniform naming conflict: " + name_1 + "!");
1344
- }
1345
1330
  // Add this as a "user uniform".
1346
1331
  userUniforms[name_1] = '1i'; // texture pointer
1347
1332
  }
@@ -1479,13 +1464,11 @@ var etro = (function () {
1479
1464
  // Set the shader uniforms.
1480
1465
  // Tell the shader we bound the texture to texture unit 0.
1481
1466
  // All base (Shader class) uniforms are optional.
1482
- if (this._uniformLocations.source) {
1467
+ if (this._uniformLocations.source)
1483
1468
  gl.uniform1i(this._uniformLocations.source, 0);
1484
- }
1485
1469
  // All base (Shader class) uniforms are optional.
1486
- if (this._uniformLocations.size) {
1470
+ if (this._uniformLocations.size)
1487
1471
  gl.uniform2iv(this._uniformLocations.size, [target.canvas.width, target.canvas.height]);
1488
- }
1489
1472
  for (var unprefixed in this._userUniforms) {
1490
1473
  var options = this._userUniforms[unprefixed];
1491
1474
  var value = val(this, unprefixed, reltime);
@@ -1536,40 +1519,35 @@ var etro = (function () {
1536
1519
  var i = 0;
1537
1520
  for (var name_4 in this._userTextures) {
1538
1521
  var testValue = val(this, name_4, reltime);
1539
- if (value === testValue) {
1522
+ if (value === testValue)
1540
1523
  value = Shader.INTERNAL_TEXTURE_UNITS + i; // after the internal texture units
1541
- }
1542
1524
  i++;
1543
1525
  }
1544
1526
  }
1545
1527
  if (outputType === '3fv') {
1546
1528
  // allow 4-component vectors; TODO: why?
1547
- if (Array.isArray(value) && (value.length === 3 || value.length === 4)) {
1529
+ if (Array.isArray(value) && (value.length === 3 || value.length === 4))
1548
1530
  return value;
1549
- }
1550
1531
  // kind of loose so this can be changed if needed
1551
- if (typeof value === 'object') {
1532
+ if (typeof value === 'object')
1552
1533
  return [
1553
1534
  value.r !== undefined ? value.r : def,
1554
1535
  value.g !== undefined ? value.g : def,
1555
1536
  value.b !== undefined ? value.b : def
1556
1537
  ];
1557
- }
1558
1538
  throw new Error("Invalid type: " + outputType + " or value: " + value);
1559
1539
  }
1560
1540
  if (outputType === '4fv') {
1561
- if (Array.isArray(value) && value.length === 4) {
1541
+ if (Array.isArray(value) && value.length === 4)
1562
1542
  return value;
1563
- }
1564
1543
  // kind of loose so this can be changed if needed
1565
- if (typeof value === 'object') {
1544
+ if (typeof value === 'object')
1566
1545
  return [
1567
1546
  value.r !== undefined ? value.r : def,
1568
1547
  value.g !== undefined ? value.g : def,
1569
1548
  value.b !== undefined ? value.b : def,
1570
1549
  value.a !== undefined ? value.a : def
1571
1550
  ];
1572
- }
1573
1551
  throw new Error("Invalid type: " + outputType + " or value: " + value);
1574
1552
  }
1575
1553
  return value;
@@ -1663,9 +1641,8 @@ var etro = (function () {
1663
1641
  else {
1664
1642
  // No, it's not a power of 2. Turn off mips and set
1665
1643
  // wrapping to clamp to edge
1666
- if (wrapS !== gl.CLAMP_TO_EDGE || wrapT !== gl.CLAMP_TO_EDGE) {
1644
+ if (wrapS !== gl.CLAMP_TO_EDGE || wrapT !== gl.CLAMP_TO_EDGE)
1667
1645
  console.warn('Wrap mode is not CLAMP_TO_EDGE for a non-power-of-two texture. Defaulting to CLAMP_TO_EDGE');
1668
- }
1669
1646
  gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
1670
1647
  gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
1671
1648
  }
@@ -1901,6 +1878,7 @@ var etro = (function () {
1901
1878
  function Stack(options) {
1902
1879
  var _this = _super.call(this) || this;
1903
1880
  _this._effectsBack = [];
1881
+ // TODO: Throw 'change' events in handlers
1904
1882
  _this.effects = new Proxy(_this._effectsBack, {
1905
1883
  deleteProperty: function (target, property) {
1906
1884
  var value = target[property];
@@ -1911,9 +1889,8 @@ var etro = (function () {
1911
1889
  set: function (target, property, value) {
1912
1890
  // TODO: make sure type check works
1913
1891
  if (!isNaN(Number(property))) { // if property is a number (index)
1914
- if (target[property]) {
1892
+ if (target[property])
1915
1893
  target[property].detach(); // Detach old effect from movie
1916
- }
1917
1894
  value.attach(this._target); // Attach effect to movie
1918
1895
  }
1919
1896
  target[property] = value;
@@ -1922,6 +1899,7 @@ var etro = (function () {
1922
1899
  });
1923
1900
  options.effects.forEach(function (effect) { return _this.effects.push(effect); });
1924
1901
  return _this;
1902
+ // TODO: Propogate 'change' events from children up
1925
1903
  }
1926
1904
  Stack.prototype.attach = function (movie) {
1927
1905
  _super.prototype.attach.call(this, movie);
@@ -2002,10 +1980,9 @@ var etro = (function () {
2002
1980
  }
2003
1981
  GaussianBlurComponent.prototype.apply = function (target, reltime) {
2004
1982
  var radiusVal = val(this, 'radius', reltime);
2005
- if (radiusVal !== this._radiusCache) {
1983
+ if (radiusVal !== this._radiusCache)
2006
1984
  // Regenerate gaussian distribution canvas.
2007
1985
  this.shape = GaussianBlurComponent._render1DKernel(GaussianBlurComponent._gen1DKernel(radiusVal));
2008
- }
2009
1986
  this._radiusCache = radiusVal;
2010
1987
  _super.prototype.apply.call(this, target, reltime);
2011
1988
  };
@@ -2038,27 +2015,23 @@ var etro = (function () {
2038
2015
  var pascal = GaussianBlurComponent._genPascalRow(2 * radius + 1);
2039
2016
  // don't use `reduce` and `map` (overhead?)
2040
2017
  var sum = 0;
2041
- for (var i = 0; i < pascal.length; i++) {
2018
+ for (var i = 0; i < pascal.length; i++)
2042
2019
  sum += pascal[i];
2043
- }
2044
- for (var i = 0; i < pascal.length; i++) {
2020
+ for (var i = 0; i < pascal.length; i++)
2045
2021
  pascal[i] /= sum;
2046
- }
2047
2022
  return pascal;
2048
2023
  };
2049
2024
  GaussianBlurComponent._genPascalRow = function (index) {
2050
- if (index < 0) {
2025
+ if (index < 0)
2051
2026
  throw new Error("Invalid index " + index);
2052
- }
2053
2027
  var currRow = [1];
2054
2028
  for (var i = 1; i < index; i++) {
2055
2029
  var nextRow = [];
2056
2030
  nextRow.length = currRow.length + 1;
2057
2031
  // edges are always 1's
2058
2032
  nextRow[0] = nextRow[nextRow.length - 1] = 1;
2059
- for (var j = 1; j < nextRow.length - 1; j++) {
2033
+ for (var j = 1; j < nextRow.length - 1; j++)
2060
2034
  nextRow[j] = currRow[j - 1] + currRow[j];
2061
- }
2062
2035
  currRow = nextRow;
2063
2036
  }
2064
2037
  return currRow;
@@ -2136,9 +2109,8 @@ var etro = (function () {
2136
2109
  }
2137
2110
  Pixelate.prototype.apply = function (target, reltime) {
2138
2111
  var ps = val(this, 'pixelSize', reltime);
2139
- if (ps % 1 !== 0 || ps < 0) {
2112
+ if (ps % 1 !== 0 || ps < 0)
2140
2113
  throw new Error('Pixel size must be a nonnegative integer');
2141
- }
2142
2114
  _super.prototype.apply.call(this, target, reltime);
2143
2115
  };
2144
2116
  return Pixelate;
@@ -2167,12 +2139,10 @@ var etro = (function () {
2167
2139
  return _this;
2168
2140
  }
2169
2141
  Transform.prototype.apply = function (target, reltime) {
2170
- if (target.canvas.width !== this._tmpCanvas.width) {
2142
+ if (target.canvas.width !== this._tmpCanvas.width)
2171
2143
  this._tmpCanvas.width = target.canvas.width;
2172
- }
2173
- if (target.canvas.height !== this._tmpCanvas.height) {
2144
+ if (target.canvas.height !== this._tmpCanvas.height)
2174
2145
  this._tmpCanvas.height = target.canvas.height;
2175
- }
2176
2146
  // Use data, since that's the underlying storage
2177
2147
  this._tmpMatrix.data = val(this, 'matrix.data', reltime);
2178
2148
  this._tmpCtx.setTransform(this._tmpMatrix.a, this._tmpMatrix.b, this._tmpMatrix.c, this._tmpMatrix.d, this._tmpMatrix.e, this._tmpMatrix.f);
@@ -2198,9 +2168,8 @@ var etro = (function () {
2198
2168
  ];
2199
2169
  }
2200
2170
  Matrix.prototype.identity = function () {
2201
- for (var i = 0; i < this.data.length; i++) {
2171
+ for (var i = 0; i < this.data.length; i++)
2202
2172
  this.data[i] = Matrix.IDENTITY.data[i];
2203
- }
2204
2173
  return this;
2205
2174
  };
2206
2175
  /**
@@ -2209,9 +2178,8 @@ var etro = (function () {
2209
2178
  * @param [val]
2210
2179
  */
2211
2180
  Matrix.prototype.cell = function (x, y, val) {
2212
- if (val !== undefined) {
2181
+ if (val !== undefined)
2213
2182
  this.data[3 * y + x] = val;
2214
- }
2215
2183
  return this.data[3 * y + x];
2216
2184
  };
2217
2185
  Object.defineProperty(Matrix.prototype, "a", {
@@ -2263,19 +2231,16 @@ var etro = (function () {
2263
2231
  */
2264
2232
  Matrix.prototype.multiply = function (other) {
2265
2233
  // copy to temporary matrix to avoid modifying `this` while reading from it
2266
- for (var x = 0; x < 3; x++) {
2234
+ for (var x = 0; x < 3; x++)
2267
2235
  for (var y = 0; y < 3; y++) {
2268
2236
  var sum = 0;
2269
- for (var i = 0; i < 3; i++) {
2237
+ for (var i = 0; i < 3; i++)
2270
2238
  sum += this.cell(x, i) * other.cell(i, y);
2271
- }
2272
2239
  Matrix._TMP_MATRIX.cell(x, y, sum);
2273
2240
  }
2274
- }
2275
2241
  // copy data from TMP_MATRIX to this
2276
- for (var i = 0; i < Matrix._TMP_MATRIX.data.length; i++) {
2242
+ for (var i = 0; i < Matrix._TMP_MATRIX.data.length; i++)
2277
2243
  this.data[i] = Matrix._TMP_MATRIX.data[i];
2278
- }
2279
2244
  return this;
2280
2245
  };
2281
2246
  /**
@@ -8805,7 +8770,7 @@ var etro = (function () {
8805
8770
  // Refresh screen when effect is removed, if the movie isn't playing
8806
8771
  // already.
8807
8772
  var value = target[property];
8808
- value.detach();
8773
+ value.tryDetach();
8809
8774
  delete target[property];
8810
8775
  publish(that, 'movie.change.effect.remove', { effect: value });
8811
8776
  return true;
@@ -8817,10 +8782,10 @@ var etro = (function () {
8817
8782
  publish(that, 'movie.change.effect.remove', {
8818
8783
  effect: target[property]
8819
8784
  });
8820
- target[property].detach();
8785
+ target[property].tryDetach();
8821
8786
  }
8822
8787
  // Attach effect to movie
8823
- value.attach(that);
8788
+ value.tryAttach(that);
8824
8789
  target[property] = value;
8825
8790
  // Refresh screen when effect is set, if the movie isn't playing
8826
8791
  // already.
@@ -8837,12 +8802,11 @@ var etro = (function () {
8837
8802
  deleteProperty: function (target, property) {
8838
8803
  var oldDuration = this.duration;
8839
8804
  var value = target[property];
8840
- value.detach(that);
8805
+ value.tryDetach(that);
8841
8806
  delete target[property];
8842
8807
  var current = that.currentTime >= value.startTime && that.currentTime < value.startTime + value.duration;
8843
- if (current) {
8808
+ if (current)
8844
8809
  publish(that, 'movie.change.layer.remove', { layer: value });
8845
- }
8846
8810
  publish(that, 'movie.change.duration', { oldDuration: oldDuration });
8847
8811
  return true;
8848
8812
  },
@@ -8854,16 +8818,15 @@ var etro = (function () {
8854
8818
  publish(that, 'movie.change.layer.remove', {
8855
8819
  layer: target[property]
8856
8820
  });
8857
- target[property].detach();
8821
+ target[property].tryDetach();
8858
8822
  }
8859
8823
  // Attach layer to movie
8860
- value.attach(that);
8824
+ value.tryAttach(that);
8861
8825
  target[property] = value;
8862
8826
  // Refresh screen when a relevant layer is added or removed
8863
8827
  var current = that.currentTime >= value.startTime && that.currentTime < value.startTime + value.duration;
8864
- if (current) {
8828
+ if (current)
8865
8829
  publish(that, 'movie.change.layer.add', { layer: value });
8866
- }
8867
8830
  publish(that, 'movie.change.duration', { oldDuration: oldDuration });
8868
8831
  }
8869
8832
  else {
@@ -8887,14 +8850,12 @@ var etro = (function () {
8887
8850
  this._lastPlayedOffset = -1;
8888
8851
  // newThis._updateInterval = 0.1; // time in seconds between each "timeupdate" event
8889
8852
  // newThis._lastUpdate = -1;
8890
- if (newThis.autoRefresh) {
8853
+ if (newThis.autoRefresh)
8891
8854
  newThis.refresh(); // render single frame on creation
8892
- }
8893
8855
  // Subscribe to own event "change" (child events propogate up)
8894
8856
  subscribe(newThis, 'movie.change', function () {
8895
- if (newThis.autoRefresh && !newThis.rendering) {
8857
+ if (newThis.autoRefresh && !newThis.rendering)
8896
8858
  newThis.refresh();
8897
- }
8898
8859
  });
8899
8860
  // Subscribe to own event "ended"
8900
8861
  subscribe(newThis, 'movie.recordended', function () {
@@ -8912,16 +8873,14 @@ var etro = (function () {
8912
8873
  Movie.prototype.play = function () {
8913
8874
  var _this = this;
8914
8875
  return new Promise(function (resolve) {
8915
- if (!_this.paused) {
8876
+ if (!_this.paused)
8916
8877
  throw new Error('Already playing');
8917
- }
8918
8878
  _this._paused = _this._ended = false;
8919
8879
  _this._lastPlayed = performance.now();
8920
8880
  _this._lastPlayedOffset = _this.currentTime;
8921
- if (!_this.renderingFrame) {
8881
+ if (!_this.renderingFrame)
8922
8882
  // Not rendering (and not playing), so play.
8923
8883
  _this._render(true, undefined, resolve);
8924
- }
8925
8884
  // Stop rendering frame if currently doing so, because playing has higher
8926
8885
  // priority. This will effect the next _render call.
8927
8886
  _this._renderingFrame = false;
@@ -8945,12 +8904,10 @@ var etro = (function () {
8945
8904
  // TODO: improve recording performance to increase frame rate?
8946
8905
  Movie.prototype.record = function (options) {
8947
8906
  var _this = this;
8948
- if (options.video === false && options.audio === false) {
8907
+ if (options.video === false && options.audio === false)
8949
8908
  throw new Error('Both video and audio cannot be disabled');
8950
- }
8951
- if (!this.paused) {
8909
+ if (!this.paused)
8952
8910
  throw new Error('Cannot record movie while already playing or recording');
8953
- }
8954
8911
  return new Promise(function (resolve, reject) {
8955
8912
  var canvasCache = _this.canvas;
8956
8913
  // Record on a temporary canvas context
@@ -8981,9 +8938,8 @@ var etro = (function () {
8981
8938
  var mediaRecorder = new MediaRecorder(stream, options.mediaRecorderOptions);
8982
8939
  mediaRecorder.ondataavailable = function (event) {
8983
8940
  // if (this._paused) reject(new Error("Recording was interrupted"));
8984
- if (event.data.size > 0) {
8941
+ if (event.data.size > 0)
8985
8942
  recordedChunks.push(event.data);
8986
- }
8987
8943
  };
8988
8944
  // TODO: publish to movie, not layers
8989
8945
  mediaRecorder.onstop = function () {
@@ -9042,17 +8998,15 @@ var etro = (function () {
9042
8998
  if (!this.rendering) {
9043
8999
  // (!this.paused || this._renderingFrame) is true so it's playing or it's
9044
9000
  // rendering a single frame.
9045
- if (done) {
9001
+ if (done)
9046
9002
  done();
9047
- }
9048
9003
  return;
9049
9004
  }
9050
9005
  this._updateCurrentTime(timestamp);
9051
9006
  var recordingEnd = this.recording ? this._recordEndTime : this.duration;
9052
9007
  var recordingEnded = this.currentTime > recordingEnd;
9053
- if (recordingEnded) {
9008
+ if (recordingEnded)
9054
9009
  publish(this, 'movie.recordended', { movie: this });
9055
- }
9056
9010
  // Bad for performance? (remember, it's calling Array.reduce)
9057
9011
  var end = this.duration;
9058
9012
  var ended = this.currentTime > end;
@@ -9071,9 +9025,8 @@ var etro = (function () {
9071
9025
  var layer = this.layers[i];
9072
9026
  // A layer that has been deleted before layers.length has been updated
9073
9027
  // (see the layers proxy in the constructor).
9074
- if (!layer) {
9028
+ if (!layer)
9075
9029
  continue;
9076
- }
9077
9030
  layer.stop();
9078
9031
  layer.active = false;
9079
9032
  }
@@ -9081,27 +9034,24 @@ var etro = (function () {
9081
9034
  }
9082
9035
  // Stop playback or recording if done
9083
9036
  if (recordingEnded || (ended && !this.repeat)) {
9084
- if (done) {
9037
+ if (done)
9085
9038
  done();
9086
- }
9087
9039
  return;
9088
9040
  }
9089
9041
  // Do render
9090
9042
  this._renderBackground(timestamp);
9091
9043
  var frameFullyLoaded = this._renderLayers();
9092
9044
  this._applyEffects();
9093
- if (frameFullyLoaded) {
9045
+ if (frameFullyLoaded)
9094
9046
  publish(this, 'movie.loadeddata', { movie: this });
9095
- }
9096
9047
  // If didn't load in this instant, repeatedly frame-render until frame is
9097
9048
  // loaded.
9098
9049
  // If the expression below is false, don't publish an event, just silently
9099
9050
  // stop render loop.
9100
9051
  if (!repeat || (this._renderingFrame && frameFullyLoaded)) {
9101
9052
  this._renderingFrame = false;
9102
- if (done) {
9053
+ if (done)
9103
9054
  done();
9104
- }
9105
9055
  return;
9106
9056
  }
9107
9057
  window.requestAnimationFrame(function (timestamp) {
@@ -9139,9 +9089,8 @@ var etro = (function () {
9139
9089
  var layer = this.layers[i];
9140
9090
  // A layer that has been deleted before layers.length has been updated
9141
9091
  // (see the layers proxy in the constructor).
9142
- if (!layer) {
9092
+ if (!layer)
9143
9093
  continue;
9144
- }
9145
9094
  var reltime = this.currentTime - layer.startTime;
9146
9095
  // Cancel operation if layer disabled or outside layer time interval
9147
9096
  if (!val(layer, 'enabled', reltime) ||
@@ -9163,18 +9112,16 @@ var etro = (function () {
9163
9112
  layer.active = true;
9164
9113
  }
9165
9114
  // if the layer has an input file
9166
- if ('source' in layer) {
9115
+ if ('source' in layer)
9167
9116
  frameFullyLoaded = frameFullyLoaded && layer.source.readyState >= 2;
9168
- }
9169
9117
  layer.render();
9170
9118
  // if the layer has visual component
9171
9119
  if (layer instanceof Visual) {
9172
9120
  var canvas = layer.canvas;
9173
9121
  // layer.canvas.width and layer.canvas.height should already be interpolated
9174
9122
  // if the layer has an area (else InvalidStateError from canvas)
9175
- if (canvas.width * canvas.height > 0) {
9123
+ if (canvas.width * canvas.height > 0)
9176
9124
  this.cctx.drawImage(canvas, val(layer, 'x', reltime), val(layer, 'y', reltime), canvas.width, canvas.height);
9177
- }
9178
9125
  }
9179
9126
  }
9180
9127
  return frameFullyLoaded;
@@ -9184,9 +9131,8 @@ var etro = (function () {
9184
9131
  var effect = this.effects[i];
9185
9132
  // An effect that has been deleted before effects.length has been updated
9186
9133
  // (see the effectsproxy in the constructor).
9187
- if (!effect) {
9134
+ if (!effect)
9188
9135
  continue;
9189
- }
9190
9136
  effect.apply(this, this.currentTime);
9191
9137
  }
9192
9138
  };
@@ -9205,9 +9151,8 @@ var etro = (function () {
9205
9151
  * Convienence method
9206
9152
  */
9207
9153
  Movie.prototype._publishToLayers = function (type, event) {
9208
- for (var i = 0; i < this.layers.length; i++) {
9154
+ for (var i = 0; i < this.layers.length; i++)
9209
9155
  publish(this.layers[i], type, event);
9210
- }
9211
9156
  };
9212
9157
  Object.defineProperty(Movie.prototype, "rendering", {
9213
9158
  /**
@@ -9320,13 +9265,11 @@ var etro = (function () {
9320
9265
  return new Promise(function (resolve, reject) {
9321
9266
  _this._currentTime = time;
9322
9267
  publish(_this, 'movie.seek', {});
9323
- if (refresh) {
9268
+ if (refresh)
9324
9269
  // Pass promise callbacks to `refresh`
9325
9270
  _this.refresh().then(resolve).catch(reject);
9326
- }
9327
- else {
9271
+ else
9328
9272
  resolve();
9329
- }
9330
9273
  });
9331
9274
  };
9332
9275
  Object.defineProperty(Movie.prototype, "canvas", {