etro 0.9.0 → 0.9.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 (38) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/CONTRIBUTING.md +5 -17
  3. package/README.md +7 -16
  4. package/dist/etro-cjs.js +37 -28
  5. package/dist/etro-iife.js +37 -28
  6. package/dist/layer/text.d.ts +3 -3
  7. package/dist/layer/visual-source.d.ts +18 -3
  8. package/dist/layer/visual.d.ts +5 -5
  9. package/dist/movie.d.ts +3 -3
  10. package/dist/object.d.ts +5 -1
  11. package/eslint.conf.js +2 -0
  12. package/eslint.test-conf.js +1 -2
  13. package/karma.conf.js +6 -7
  14. package/package.json +17 -17
  15. package/scripts/gen-effect-samples.html +24 -0
  16. package/scripts/save-effect-samples.js +1 -1
  17. package/src/effect/stack.ts +2 -2
  18. package/src/effect/transform.ts +2 -2
  19. package/src/layer/base.ts +2 -1
  20. package/src/layer/text.ts +4 -4
  21. package/src/layer/visual-source.ts +27 -7
  22. package/src/layer/visual.ts +7 -7
  23. package/src/movie.ts +16 -14
  24. package/src/object.ts +5 -1
  25. package/tsconfig.json +3 -1
  26. package/examples/application/readme-screenshot.html +0 -85
  27. package/examples/application/video-player.html +0 -130
  28. package/examples/application/webcam.html +0 -28
  29. package/examples/introduction/audio.html +0 -64
  30. package/examples/introduction/effects.html +0 -79
  31. package/examples/introduction/export.html +0 -83
  32. package/examples/introduction/functions.html +0 -37
  33. package/examples/introduction/hello-world1.html +0 -37
  34. package/examples/introduction/hello-world2.html +0 -32
  35. package/examples/introduction/keyframes.html +0 -79
  36. package/examples/introduction/media.html +0 -63
  37. package/examples/introduction/text.html +0 -31
  38. package/private-todo.txt +0 -70
package/CHANGELOG.md CHANGED
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
+ ## [0.9.1] - 2022-09-18
9
+ ### Fixed
10
+ - Update color types from `string` to `Color` ([#135](https://github.com/etro-js/etro/pull/135)).
11
+ - `Image` and `Video` classes now include missing properties.
12
+ - `Movie#currentTime` no longer exceeds the stop time.
13
+
8
14
  ## [0.9.0] - 2022-07-17
9
15
  ### Changed
10
16
  - Methods in the `Base` effect now accept `Base` layers instead of `Visual` layers.
@@ -227,6 +233,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
227
233
  - Gaussian blur
228
234
  - Transform
229
235
 
236
+ [0.9.1]: https://github.com/etro-js/etro/compare/v0.9.0...v0.9.1
230
237
  [0.9.0]: https://github.com/etro-js/etro/compare/v0.8.5...v0.9.0
231
238
  [0.8.5]: https://github.com/etro-js/etro/compare/v0.8.4...v0.8.5
232
239
  [0.8.4]: https://github.com/etro-js/etro/compare/v0.8.3...v0.8.4
package/CONTRIBUTING.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
- Thank you for considering contributing to Etro! There are many ways you can contribute to Etro, like creating issues for features or bugs, improving the docs or wiki, or writing the actual code for the library. This page covers how to make changes to the repository files (either code or docs).
5
+ Thank you for considering contributing to Etro! There are many ways you can contribute to Etro, like creating issues for features or bugs, improving the docs or wiki, or writing the code for the library. This page covers how to make changes to the repository files (either code or jsdocs).
6
6
 
7
7
  [Join our Discord](https://discord.gg/myrBsQ8Cht)
8
8
 
@@ -10,7 +10,7 @@ Thank you for considering contributing to Etro! There are many ways you can cont
10
10
 
11
11
  #### Step 0: Dependencies
12
12
 
13
- - You will need Git, Node, NPM (at least 7.x) and Firefox (for headless unit testing) installed
13
+ - You will need Git, Node, NPM (at least 7.x) and Firefox (for headless functional testing) installed.
14
14
 
15
15
  #### Step 1: Fork
16
16
 
@@ -40,12 +40,11 @@ Thank you for considering contributing to Etro! There are many ways you can cont
40
40
  to lint the code, generate the [dist](dist) files and run unit tests on them. It may be helpful to put these commands in a pre-commit hook.
41
41
 
42
42
  - Commit your changes
43
- - Please avoid squashing all your commits into one; we try to keep atomic commits.
44
43
  - Please follow these commit message guidelines:
45
- - Optionally, prefix each commit message with [an appropriate emoji](https://gitmoji.dev)
44
+ - Optionally, prefix each commit message with [an appropriate emoji](https://gitmoji.dev), such as `:bug:` for fixes.
46
45
  - Write in the imperative tense
47
46
  - Wrap lines after 72 characters (for Vim add `filetype indent plugin on` to ~/.vimrc, it's enabled by default in Atom).
48
- - Example:
47
+ - Format:
49
48
 
50
49
  ```
51
50
  :emoji: One-liner
@@ -81,18 +80,7 @@ Thank you for considering contributing to Etro! There are many ways you can cont
81
80
 
82
81
  Check out [the overview guide](https://etrojs.dev/docs/overview) for usage information
83
82
 
84
- ### API Structure
85
-
86
- * `etro.Movie` - the movie
87
- * `etro.layer.*` - all layers
88
- * `etro.effect.*` - all (visual) effects
89
- - `etro.event.publish` - emit an event
90
- - `etro.event.subscribe` - add an event listener
91
- - `etro.*` - other utility classes and methods (see **src/util.ts**)
92
-
93
- ### Etro concepts
94
-
95
- #### Pub/sub system
83
+ ### Pub/sub system
96
84
 
97
85
  Events emitted by Etro objects use a [pub/sub system](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern). To emit an event, use `event.publish(target, type, event)`. For instance,
98
86
 
package/README.md CHANGED
@@ -3,14 +3,10 @@
3
3
  [![](https://img.shields.io/npm/v/etro)](https://www.npmjs.com/package/etro)
4
4
  [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fetro-js%2Fetro%2Fbadge&style=flat)](https://actions-badge.atrox.dev/etro-js/etro/goto)
5
5
 
6
- > Etro was previously known as Etro, but it had to be renamed to avoid
7
- > confusion with an existing software product.
8
-
9
- Etro is a typescript framework for programmatically editing videos. Similar to
10
- GUI-based video-editing software, it lets you composite layers and add effects.
11
- Etro comes shipped with text, video, audio and image layers, along with a bunch
12
- of GLSL effects. You can also define your own layers and effects with javascript
13
- and GLSL.
6
+ Etro is a typescript framework for programmatically editing videos. It lets you
7
+ composite layers and add filters (effects). Etro comes shipped with text, video,
8
+ audio and image layers, along with a bunch of GLSL effects. You can also define
9
+ your own layers and effects with javascript and GLSL.
14
10
 
15
11
  [Join our Discord](https://discord.gg/myrBsQ8Cht)
16
12
 
@@ -74,16 +70,11 @@ To use Etro in Node, see the [wrapper](https://github.com/etro-js/etro-node):
74
70
 
75
71
  ## Running the Examples
76
72
 
77
- First, download the assets for the examples:
78
-
79
- ```
80
- npm run assets
81
- ```
82
-
83
- Then, start the development server (only used for convience while developing;
84
- you don't need a server to use Etro):
73
+ Start the development server (only used for convience while developing; you
74
+ don't need a server to use Etro):
85
75
 
86
76
  ```
77
+ npm i
87
78
  npm start
88
79
  ```
89
80
 
package/dist/etro-cjs.js CHANGED
@@ -125,6 +125,7 @@ function publish(target, type, event) {
125
125
  var listeners = new WeakMap();
126
126
 
127
127
  var event = /*#__PURE__*/Object.freeze({
128
+ __proto__: null,
128
129
  subscribe: subscribe,
129
130
  unsubscribe: unsubscribe,
130
131
  publish: publish
@@ -374,7 +375,7 @@ var Color = /** @class */ (function () {
374
375
  * Converts to a CSS color
375
376
  */
376
377
  Color.prototype.toString = function () {
377
- return "rgba(" + this.r + ", " + this.g + ", " + this.b + ", " + this.a + ")";
378
+ return "rgba(".concat(this.r, ", ").concat(this.g, ", ").concat(this.b, ", ").concat(this.a, ")");
378
379
  };
379
380
  return Color;
380
381
  }());
@@ -433,7 +434,7 @@ var Font = /** @class */ (function () {
433
434
  s += this.weight + ' ';
434
435
  if (this.stretch !== 'normal')
435
436
  s += this.stretch + ' ';
436
- s += "" + this.size + this.sizeUnit + " ";
437
+ s += "".concat(this.size).concat(this.sizeUnit, " ");
437
438
  if (this.lineHeight !== 'normal')
438
439
  s += this.lineHeight + ' ';
439
440
  s += this.family;
@@ -450,7 +451,7 @@ var parseFontEl = document.createElement('div');
450
451
  */
451
452
  function parseFont(str) {
452
453
  // Assign css string to html element
453
- parseFontEl.setAttribute('style', "font: " + str);
454
+ parseFontEl.setAttribute('style', "font: ".concat(str));
454
455
  var _a = parseFontEl.style, fontSize = _a.fontSize, fontFamily = _a.fontFamily, fontStyle = _a.fontStyle, fontVariant = _a.fontVariant, fontWeight = _a.fontWeight, lineHeight = _a.lineHeight;
455
456
  parseFontEl.removeAttribute('style');
456
457
  var size = parseFloat(fontSize);
@@ -495,7 +496,7 @@ function watchPublic(target) {
495
496
  };
496
497
  var callback = function (prop, val, receiver) {
497
498
  // Public API property updated, emit 'modify' event.
498
- publish(proxy, target.type + ".change.modify", { property: getPath(receiver, prop), newValue: val });
499
+ publish(proxy, "".concat(target.type, ".change.modify"), { property: getPath(receiver, prop), newValue: val });
499
500
  };
500
501
  var canWatch = function (receiver, prop) { return !prop.startsWith('_') &&
501
502
  (receiver.publicExcludes === undefined || !receiver.publicExcludes.includes(prop)); };
@@ -732,7 +733,7 @@ var Base = /** @class */ (function () {
732
733
  // Propogate up to target
733
734
  subscribe(newThis, 'layer.change', function (event) {
734
735
  var typeOfChange = event.type.substring(event.type.lastIndexOf('.') + 1);
735
- var type = "movie.change.layer." + typeOfChange;
736
+ var type = "movie.change.layer.".concat(typeOfChange);
736
737
  publish(newThis._movie, type, __assign(__assign({}, event), { target: newThis._movie, type: type }));
737
738
  });
738
739
  return newThis;
@@ -805,7 +806,8 @@ var Base = /** @class */ (function () {
805
806
  * The current time of the movie relative to this layer
806
807
  */
807
808
  get: function () {
808
- return this._movie ? this._movie.currentTime - this.startTime
809
+ return this._movie
810
+ ? this._movie.currentTime - this.startTime
809
811
  : undefined;
810
812
  },
811
813
  enumerable: false,
@@ -986,13 +988,13 @@ var Visual = /** @class */ (function (_super) {
986
988
  height: null,
987
989
  /**
988
990
  * @name module:layer.Visual#background
989
- * @desc The CSS color code for the background, or <code>null</code> for
991
+ * @desc The color code for the background, or <code>null</code> for
990
992
  * transparency
991
993
  */
992
994
  background: null,
993
995
  /**
994
996
  * @name module:layer.Visual#border
995
- * @desc The CSS border style, or <code>null</code> for no border
997
+ * @desc The border style, or <code>null</code> for no border
996
998
  */
997
999
  border: null,
998
1000
  /**
@@ -1067,19 +1069,23 @@ function VisualSourceMixin(superclass) {
1067
1069
  // instead. (TODO: fact check)
1068
1070
  /* eslint-disable eqeqeq */
1069
1071
  return destWidth != undefined
1070
- ? destWidth : val(this, 'sourceWidth', this.currentTime);
1072
+ ? destWidth
1073
+ : val(this, 'sourceWidth', this.currentTime);
1071
1074
  }, destHeight: function (destHeight) {
1072
1075
  /* eslint-disable eqeqeq */
1073
1076
  return destHeight != undefined
1074
- ? destHeight : val(this, 'sourceHeight', this.currentTime);
1077
+ ? destHeight
1078
+ : val(this, 'sourceHeight', this.currentTime);
1075
1079
  }, width: function (width) {
1076
1080
  /* eslint-disable eqeqeq */
1077
1081
  return width != undefined
1078
- ? width : val(this, 'destWidth', this.currentTime);
1082
+ ? width
1083
+ : val(this, 'destWidth', this.currentTime);
1079
1084
  }, height: function (height) {
1080
1085
  /* eslint-disable eqeqeq */
1081
1086
  return height != undefined
1082
- ? height : val(this, 'destHeight', this.currentTime);
1087
+ ? height
1088
+ : val(this, 'destHeight', this.currentTime);
1083
1089
  } });
1084
1090
  return MixedVisualSource;
1085
1091
  }
@@ -1151,7 +1157,7 @@ var Text = /** @class */ (function (_super) {
1151
1157
  return metrics;
1152
1158
  } */
1153
1159
  Text.prototype.getDefaultOptions = function () {
1154
- return __assign(__assign({}, Visual.prototype.getDefaultOptions()), { background: null, text: undefined, font: '10px sans-serif', color: '#fff', textX: 0, textY: 0, maxWidth: null, textAlign: 'start', textBaseline: 'top', textDirection: 'ltr' });
1160
+ return __assign(__assign({}, Visual.prototype.getDefaultOptions()), { background: null, text: undefined, font: '10px sans-serif', color: parseColor('#fff'), textX: 0, textY: 0, maxWidth: null, textAlign: 'start', textBaseline: 'top', textDirection: 'ltr' });
1155
1161
  };
1156
1162
  return Text;
1157
1163
  }(Visual));
@@ -1175,6 +1181,7 @@ var Video = /** @class */ (function (_super) {
1175
1181
  */
1176
1182
 
1177
1183
  var index = /*#__PURE__*/Object.freeze({
1184
+ __proto__: null,
1178
1185
  AudioSourceMixin: AudioSourceMixin,
1179
1186
  Audio: Audio,
1180
1187
  Base: Base,
@@ -1198,7 +1205,7 @@ var Base$1 = /** @class */ (function () {
1198
1205
  subscribe(newThis, 'effect.change.modify', function (event) {
1199
1206
  if (!newThis._target)
1200
1207
  return;
1201
- var type = newThis._target.type + ".change.effect.modify";
1208
+ var type = "".concat(newThis._target.type, ".change.effect.modify");
1202
1209
  publish(newThis._target, type, __assign(__assign({}, event), { target: newThis._target, source: newThis, type: type }));
1203
1210
  });
1204
1211
  return newThis;
@@ -1358,7 +1365,7 @@ var Shader = /** @class */ (function (_super) {
1358
1365
  * object.
1359
1366
  */
1360
1367
  if (userUniforms[name_1])
1361
- throw new Error("Texture - uniform naming conflict: " + name_1 + "!");
1368
+ throw new Error("Texture - uniform naming conflict: ".concat(name_1, "!"));
1362
1369
  // Add this as a "user uniform".
1363
1370
  userUniforms[name_1] = '1i'; // texture pointer
1364
1371
  }
@@ -1567,7 +1574,7 @@ var Shader = /** @class */ (function (_super) {
1567
1574
  value.g !== undefined ? value.g : def,
1568
1575
  value.b !== undefined ? value.b : def
1569
1576
  ];
1570
- throw new Error("Invalid type: " + outputType + " or value: " + value);
1577
+ throw new Error("Invalid type: ".concat(outputType, " or value: ").concat(value));
1571
1578
  }
1572
1579
  if (outputType === '4fv') {
1573
1580
  if (Array.isArray(value) && value.length === 4)
@@ -1580,7 +1587,7 @@ var Shader = /** @class */ (function (_super) {
1580
1587
  value.b !== undefined ? value.b : def,
1581
1588
  value.a !== undefined ? value.a : def
1582
1589
  ];
1583
- throw new Error("Invalid type: " + outputType + " or value: " + value);
1590
+ throw new Error("Invalid type: ".concat(outputType, " or value: ").concat(value));
1584
1591
  }
1585
1592
  return value;
1586
1593
  };
@@ -2057,7 +2064,7 @@ var GaussianBlurComponent = /** @class */ (function (_super) {
2057
2064
  };
2058
2065
  GaussianBlurComponent._genPascalRow = function (index) {
2059
2066
  if (index < 0)
2060
- throw new Error("Invalid index " + index);
2067
+ throw new Error("Invalid index ".concat(index));
2061
2068
  var currRow = [1];
2062
2069
  for (var i = 1; i < index; i++) {
2063
2070
  var nextRow = [];
@@ -2329,6 +2336,7 @@ var Transform = /** @class */ (function (_super) {
2329
2336
  */
2330
2337
 
2331
2338
  var index$1 = /*#__PURE__*/Object.freeze({
2339
+ __proto__: null,
2332
2340
  Base: Base$1,
2333
2341
  Brightness: Brightness,
2334
2342
  Channels: Channels,
@@ -2360,8 +2368,6 @@ var MovieOptions = /** @class */ (function () {
2360
2368
  *
2361
2369
  * Implements a pub/sub system.
2362
2370
  */
2363
- // TODO: Implement event "durationchange", and more
2364
- // TODO: Add width and height options
2365
2371
  // TODO: Make record option to make recording video output to the user while
2366
2372
  // it's recording
2367
2373
  // TODO: rename renderingFrame -> refreshing
@@ -2631,14 +2637,14 @@ var Movie = /** @class */ (function () {
2631
2637
  done();
2632
2638
  return;
2633
2639
  }
2634
- this._updateCurrentTime(timestamp);
2640
+ var end = this.recording ? this._recordEndTime : this.duration;
2641
+ this._updateCurrentTime(timestamp, end);
2635
2642
  // TODO: Is calling duration every frame bad for performance? (remember,
2636
2643
  // it's calling Array.reduce)
2637
- var end = this.recording ? this._recordEndTime : this.duration;
2638
- if (this.currentTime > end) {
2644
+ if (this.currentTime === end) {
2639
2645
  if (this.recording)
2640
2646
  publish(this, 'movie.recordended', { movie: this });
2641
- if (this.currentTime > this.duration)
2647
+ if (this.currentTime === this.duration)
2642
2648
  publish(this, 'movie.ended', { movie: this, repeat: this.repeat });
2643
2649
  // TODO: only reset currentTime if repeating
2644
2650
  if (this.repeat) {
@@ -2691,12 +2697,12 @@ var Movie = /** @class */ (function () {
2691
2697
  _this._render(repeat, undefined, done);
2692
2698
  }); // TODO: research performance cost
2693
2699
  };
2694
- Movie.prototype._updateCurrentTime = function (timestamp) {
2700
+ Movie.prototype._updateCurrentTime = function (timestampMs, end) {
2695
2701
  // If we're only instant-rendering (current frame only), it doens't matter
2696
2702
  // if it's paused or not.
2697
2703
  if (!this._renderingFrame) {
2698
2704
  // if ((timestamp - this._lastUpdate) >= this._updateInterval) {
2699
- var sinceLastPlayed = (timestamp - this._lastPlayed) / 1000;
2705
+ var sinceLastPlayed = (timestampMs - this._lastPlayed) / 1000;
2700
2706
  var currentTime = this._lastPlayedOffset + sinceLastPlayed; // don't use setter
2701
2707
  if (this.currentTime !== currentTime) {
2702
2708
  this._currentTime = currentTime;
@@ -2704,6 +2710,8 @@ var Movie = /** @class */ (function () {
2704
2710
  }
2705
2711
  // this._lastUpdate = timestamp;
2706
2712
  // }
2713
+ if (this.currentTime > end)
2714
+ this.currentTime = end;
2707
2715
  }
2708
2716
  };
2709
2717
  Movie.prototype._renderBackground = function (timestamp) {
@@ -2970,9 +2978,9 @@ var Movie = /** @class */ (function () {
2970
2978
  canvas: undefined,
2971
2979
  /**
2972
2980
  * @name module:movie#background
2973
- * @desc The css color for the background, or <code>null</code> for transparency
2981
+ * @desc The color for the background, or <code>null</code> for transparency
2974
2982
  */
2975
- background: '#000',
2983
+ background: parseColor('#000'),
2976
2984
  /**
2977
2985
  * @name module:movie#repeat
2978
2986
  */
@@ -3000,6 +3008,7 @@ Movie.prototype.propertyFilters = {};
3000
3008
  */
3001
3009
 
3002
3010
  var etro = /*#__PURE__*/Object.freeze({
3011
+ __proto__: null,
3003
3012
  layer: index,
3004
3013
  effect: index$1,
3005
3014
  event: event,
package/dist/etro-iife.js CHANGED
@@ -126,6 +126,7 @@ var etro = (function () {
126
126
  var listeners = new WeakMap();
127
127
 
128
128
  var event = /*#__PURE__*/Object.freeze({
129
+ __proto__: null,
129
130
  subscribe: subscribe,
130
131
  unsubscribe: unsubscribe,
131
132
  publish: publish
@@ -375,7 +376,7 @@ var etro = (function () {
375
376
  * Converts to a CSS color
376
377
  */
377
378
  Color.prototype.toString = function () {
378
- return "rgba(" + this.r + ", " + this.g + ", " + this.b + ", " + this.a + ")";
379
+ return "rgba(".concat(this.r, ", ").concat(this.g, ", ").concat(this.b, ", ").concat(this.a, ")");
379
380
  };
380
381
  return Color;
381
382
  }());
@@ -434,7 +435,7 @@ var etro = (function () {
434
435
  s += this.weight + ' ';
435
436
  if (this.stretch !== 'normal')
436
437
  s += this.stretch + ' ';
437
- s += "" + this.size + this.sizeUnit + " ";
438
+ s += "".concat(this.size).concat(this.sizeUnit, " ");
438
439
  if (this.lineHeight !== 'normal')
439
440
  s += this.lineHeight + ' ';
440
441
  s += this.family;
@@ -451,7 +452,7 @@ var etro = (function () {
451
452
  */
452
453
  function parseFont(str) {
453
454
  // Assign css string to html element
454
- parseFontEl.setAttribute('style', "font: " + str);
455
+ parseFontEl.setAttribute('style', "font: ".concat(str));
455
456
  var _a = parseFontEl.style, fontSize = _a.fontSize, fontFamily = _a.fontFamily, fontStyle = _a.fontStyle, fontVariant = _a.fontVariant, fontWeight = _a.fontWeight, lineHeight = _a.lineHeight;
456
457
  parseFontEl.removeAttribute('style');
457
458
  var size = parseFloat(fontSize);
@@ -496,7 +497,7 @@ var etro = (function () {
496
497
  };
497
498
  var callback = function (prop, val, receiver) {
498
499
  // Public API property updated, emit 'modify' event.
499
- publish(proxy, target.type + ".change.modify", { property: getPath(receiver, prop), newValue: val });
500
+ publish(proxy, "".concat(target.type, ".change.modify"), { property: getPath(receiver, prop), newValue: val });
500
501
  };
501
502
  var canWatch = function (receiver, prop) { return !prop.startsWith('_') &&
502
503
  (receiver.publicExcludes === undefined || !receiver.publicExcludes.includes(prop)); };
@@ -733,7 +734,7 @@ var etro = (function () {
733
734
  // Propogate up to target
734
735
  subscribe(newThis, 'layer.change', function (event) {
735
736
  var typeOfChange = event.type.substring(event.type.lastIndexOf('.') + 1);
736
- var type = "movie.change.layer." + typeOfChange;
737
+ var type = "movie.change.layer.".concat(typeOfChange);
737
738
  publish(newThis._movie, type, __assign(__assign({}, event), { target: newThis._movie, type: type }));
738
739
  });
739
740
  return newThis;
@@ -806,7 +807,8 @@ var etro = (function () {
806
807
  * The current time of the movie relative to this layer
807
808
  */
808
809
  get: function () {
809
- return this._movie ? this._movie.currentTime - this.startTime
810
+ return this._movie
811
+ ? this._movie.currentTime - this.startTime
810
812
  : undefined;
811
813
  },
812
814
  enumerable: false,
@@ -987,13 +989,13 @@ var etro = (function () {
987
989
  height: null,
988
990
  /**
989
991
  * @name module:layer.Visual#background
990
- * @desc The CSS color code for the background, or <code>null</code> for
992
+ * @desc The color code for the background, or <code>null</code> for
991
993
  * transparency
992
994
  */
993
995
  background: null,
994
996
  /**
995
997
  * @name module:layer.Visual#border
996
- * @desc The CSS border style, or <code>null</code> for no border
998
+ * @desc The border style, or <code>null</code> for no border
997
999
  */
998
1000
  border: null,
999
1001
  /**
@@ -1068,19 +1070,23 @@ var etro = (function () {
1068
1070
  // instead. (TODO: fact check)
1069
1071
  /* eslint-disable eqeqeq */
1070
1072
  return destWidth != undefined
1071
- ? destWidth : val(this, 'sourceWidth', this.currentTime);
1073
+ ? destWidth
1074
+ : val(this, 'sourceWidth', this.currentTime);
1072
1075
  }, destHeight: function (destHeight) {
1073
1076
  /* eslint-disable eqeqeq */
1074
1077
  return destHeight != undefined
1075
- ? destHeight : val(this, 'sourceHeight', this.currentTime);
1078
+ ? destHeight
1079
+ : val(this, 'sourceHeight', this.currentTime);
1076
1080
  }, width: function (width) {
1077
1081
  /* eslint-disable eqeqeq */
1078
1082
  return width != undefined
1079
- ? width : val(this, 'destWidth', this.currentTime);
1083
+ ? width
1084
+ : val(this, 'destWidth', this.currentTime);
1080
1085
  }, height: function (height) {
1081
1086
  /* eslint-disable eqeqeq */
1082
1087
  return height != undefined
1083
- ? height : val(this, 'destHeight', this.currentTime);
1088
+ ? height
1089
+ : val(this, 'destHeight', this.currentTime);
1084
1090
  } });
1085
1091
  return MixedVisualSource;
1086
1092
  }
@@ -1152,7 +1158,7 @@ var etro = (function () {
1152
1158
  return metrics;
1153
1159
  } */
1154
1160
  Text.prototype.getDefaultOptions = function () {
1155
- return __assign(__assign({}, Visual.prototype.getDefaultOptions()), { background: null, text: undefined, font: '10px sans-serif', color: '#fff', textX: 0, textY: 0, maxWidth: null, textAlign: 'start', textBaseline: 'top', textDirection: 'ltr' });
1161
+ return __assign(__assign({}, Visual.prototype.getDefaultOptions()), { background: null, text: undefined, font: '10px sans-serif', color: parseColor('#fff'), textX: 0, textY: 0, maxWidth: null, textAlign: 'start', textBaseline: 'top', textDirection: 'ltr' });
1156
1162
  };
1157
1163
  return Text;
1158
1164
  }(Visual));
@@ -1176,6 +1182,7 @@ var etro = (function () {
1176
1182
  */
1177
1183
 
1178
1184
  var index = /*#__PURE__*/Object.freeze({
1185
+ __proto__: null,
1179
1186
  AudioSourceMixin: AudioSourceMixin,
1180
1187
  Audio: Audio,
1181
1188
  Base: Base,
@@ -1199,7 +1206,7 @@ var etro = (function () {
1199
1206
  subscribe(newThis, 'effect.change.modify', function (event) {
1200
1207
  if (!newThis._target)
1201
1208
  return;
1202
- var type = newThis._target.type + ".change.effect.modify";
1209
+ var type = "".concat(newThis._target.type, ".change.effect.modify");
1203
1210
  publish(newThis._target, type, __assign(__assign({}, event), { target: newThis._target, source: newThis, type: type }));
1204
1211
  });
1205
1212
  return newThis;
@@ -1359,7 +1366,7 @@ var etro = (function () {
1359
1366
  * object.
1360
1367
  */
1361
1368
  if (userUniforms[name_1])
1362
- throw new Error("Texture - uniform naming conflict: " + name_1 + "!");
1369
+ throw new Error("Texture - uniform naming conflict: ".concat(name_1, "!"));
1363
1370
  // Add this as a "user uniform".
1364
1371
  userUniforms[name_1] = '1i'; // texture pointer
1365
1372
  }
@@ -1568,7 +1575,7 @@ var etro = (function () {
1568
1575
  value.g !== undefined ? value.g : def,
1569
1576
  value.b !== undefined ? value.b : def
1570
1577
  ];
1571
- throw new Error("Invalid type: " + outputType + " or value: " + value);
1578
+ throw new Error("Invalid type: ".concat(outputType, " or value: ").concat(value));
1572
1579
  }
1573
1580
  if (outputType === '4fv') {
1574
1581
  if (Array.isArray(value) && value.length === 4)
@@ -1581,7 +1588,7 @@ var etro = (function () {
1581
1588
  value.b !== undefined ? value.b : def,
1582
1589
  value.a !== undefined ? value.a : def
1583
1590
  ];
1584
- throw new Error("Invalid type: " + outputType + " or value: " + value);
1591
+ throw new Error("Invalid type: ".concat(outputType, " or value: ").concat(value));
1585
1592
  }
1586
1593
  return value;
1587
1594
  };
@@ -2058,7 +2065,7 @@ var etro = (function () {
2058
2065
  };
2059
2066
  GaussianBlurComponent._genPascalRow = function (index) {
2060
2067
  if (index < 0)
2061
- throw new Error("Invalid index " + index);
2068
+ throw new Error("Invalid index ".concat(index));
2062
2069
  var currRow = [1];
2063
2070
  for (var i = 1; i < index; i++) {
2064
2071
  var nextRow = [];
@@ -2330,6 +2337,7 @@ var etro = (function () {
2330
2337
  */
2331
2338
 
2332
2339
  var index$1 = /*#__PURE__*/Object.freeze({
2340
+ __proto__: null,
2333
2341
  Base: Base$1,
2334
2342
  Brightness: Brightness,
2335
2343
  Channels: Channels,
@@ -2361,8 +2369,6 @@ var etro = (function () {
2361
2369
  *
2362
2370
  * Implements a pub/sub system.
2363
2371
  */
2364
- // TODO: Implement event "durationchange", and more
2365
- // TODO: Add width and height options
2366
2372
  // TODO: Make record option to make recording video output to the user while
2367
2373
  // it's recording
2368
2374
  // TODO: rename renderingFrame -> refreshing
@@ -2632,14 +2638,14 @@ var etro = (function () {
2632
2638
  done();
2633
2639
  return;
2634
2640
  }
2635
- this._updateCurrentTime(timestamp);
2641
+ var end = this.recording ? this._recordEndTime : this.duration;
2642
+ this._updateCurrentTime(timestamp, end);
2636
2643
  // TODO: Is calling duration every frame bad for performance? (remember,
2637
2644
  // it's calling Array.reduce)
2638
- var end = this.recording ? this._recordEndTime : this.duration;
2639
- if (this.currentTime > end) {
2645
+ if (this.currentTime === end) {
2640
2646
  if (this.recording)
2641
2647
  publish(this, 'movie.recordended', { movie: this });
2642
- if (this.currentTime > this.duration)
2648
+ if (this.currentTime === this.duration)
2643
2649
  publish(this, 'movie.ended', { movie: this, repeat: this.repeat });
2644
2650
  // TODO: only reset currentTime if repeating
2645
2651
  if (this.repeat) {
@@ -2692,12 +2698,12 @@ var etro = (function () {
2692
2698
  _this._render(repeat, undefined, done);
2693
2699
  }); // TODO: research performance cost
2694
2700
  };
2695
- Movie.prototype._updateCurrentTime = function (timestamp) {
2701
+ Movie.prototype._updateCurrentTime = function (timestampMs, end) {
2696
2702
  // If we're only instant-rendering (current frame only), it doens't matter
2697
2703
  // if it's paused or not.
2698
2704
  if (!this._renderingFrame) {
2699
2705
  // if ((timestamp - this._lastUpdate) >= this._updateInterval) {
2700
- var sinceLastPlayed = (timestamp - this._lastPlayed) / 1000;
2706
+ var sinceLastPlayed = (timestampMs - this._lastPlayed) / 1000;
2701
2707
  var currentTime = this._lastPlayedOffset + sinceLastPlayed; // don't use setter
2702
2708
  if (this.currentTime !== currentTime) {
2703
2709
  this._currentTime = currentTime;
@@ -2705,6 +2711,8 @@ var etro = (function () {
2705
2711
  }
2706
2712
  // this._lastUpdate = timestamp;
2707
2713
  // }
2714
+ if (this.currentTime > end)
2715
+ this.currentTime = end;
2708
2716
  }
2709
2717
  };
2710
2718
  Movie.prototype._renderBackground = function (timestamp) {
@@ -2971,9 +2979,9 @@ var etro = (function () {
2971
2979
  canvas: undefined,
2972
2980
  /**
2973
2981
  * @name module:movie#background
2974
- * @desc The css color for the background, or <code>null</code> for transparency
2982
+ * @desc The color for the background, or <code>null</code> for transparency
2975
2983
  */
2976
- background: '#000',
2984
+ background: parseColor('#000'),
2977
2985
  /**
2978
2986
  * @name module:movie#repeat
2979
2987
  */
@@ -3001,6 +3009,7 @@ var etro = (function () {
3001
3009
  */
3002
3010
 
3003
3011
  var etro = /*#__PURE__*/Object.freeze({
3012
+ __proto__: null,
3004
3013
  layer: index,
3005
3014
  effect: index$1,
3006
3015
  event: event,
@@ -1,9 +1,9 @@
1
- import { Dynamic } from '../util';
1
+ import { Dynamic, Color } from '../util';
2
2
  import { Visual, VisualOptions } from './visual';
3
3
  interface TextOptions extends VisualOptions {
4
4
  text: Dynamic<string>;
5
5
  font?: Dynamic<string>;
6
- color?: Dynamic<string>;
6
+ color?: Dynamic<Color>;
7
7
  /** The text's horizontal offset from the layer */
8
8
  textX?: Dynamic<number>;
9
9
  /** The text's vertical offset from the layer */
@@ -27,7 +27,7 @@ interface TextOptions extends VisualOptions {
27
27
  declare class Text extends Visual {
28
28
  text: Dynamic<string>;
29
29
  font: Dynamic<string>;
30
- color: Dynamic<string>;
30
+ color: Dynamic<Color>;
31
31
  /** The text's horizontal offset from the layer */
32
32
  textX: Dynamic<number>;
33
33
  /** The text's vertical offset from the layer */
@@ -1,9 +1,24 @@
1
1
  import { Dynamic } from '../util';
2
- import { Base, BaseOptions } from './base';
3
2
  import { Visual, VisualOptions } from './visual';
4
3
  declare type Constructor<T> = new (...args: unknown[]) => T;
5
- interface VisualSource extends Base {
4
+ interface VisualSource extends Visual {
6
5
  readonly source: HTMLImageElement | HTMLVideoElement;
6
+ /** What part of {@link source} to render */
7
+ sourceX: Dynamic<number>;
8
+ /** What part of {@link source} to render */
9
+ sourceY: Dynamic<number>;
10
+ /** What part of {@link source} to render, or undefined for the entire width */
11
+ sourceWidth: Dynamic<number>;
12
+ /** What part of {@link source} to render, or undefined for the entire height */
13
+ sourceHeight: Dynamic<number>;
14
+ /** Where to render {@link source} onto the layer */
15
+ destX: Dynamic<number>;
16
+ /** Where to render {@link source} onto the layer */
17
+ destY: Dynamic<number>;
18
+ /** Where to render {@link source} onto the layer, or undefined to fill the layer's width */
19
+ destWidth: Dynamic<number>;
20
+ /** Where to render {@link source} onto the layer, or undefined to fill the layer's height */
21
+ destHeight: Dynamic<number>;
7
22
  }
8
23
  interface VisualSourceOptions extends VisualOptions {
9
24
  source: HTMLImageElement | HTMLVideoElement;
@@ -28,5 +43,5 @@ interface VisualSourceOptions extends VisualOptions {
28
43
  * A layer that gets its image data from an HTML image or video element
29
44
  * @mixin VisualSourceMixin
30
45
  */
31
- declare function VisualSourceMixin<OptionsSuperclass extends BaseOptions>(superclass: Constructor<Visual>): Constructor<VisualSource>;
46
+ declare function VisualSourceMixin<OptionsSuperclass extends VisualOptions>(superclass: Constructor<Visual>): Constructor<VisualSource>;
32
47
  export { VisualSource, VisualSourceOptions, VisualSourceMixin };