@mulsense/xnew 0.3.7 → 0.4.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.
package/dist/xnew.mjs CHANGED
@@ -98,7 +98,7 @@ class MapMap extends Map {
98
98
  //----------------------------------------------------------------------------------------------------
99
99
  // ticker
100
100
  //----------------------------------------------------------------------------------------------------
101
- class Ticker {
101
+ class AnimationTicker {
102
102
  constructor(callback, fps = 60) {
103
103
  const self = this;
104
104
  this.id = null;
@@ -129,7 +129,7 @@ class Timer {
129
129
  this.counter = 0;
130
130
  this.offset = 0.0;
131
131
  this.status = 0;
132
- this.ticker = new Ticker((time) => {
132
+ this.ticker = new AnimationTicker((time) => {
133
133
  var _a, _b;
134
134
  let p = Math.min(this.elapsed() / this.options.duration, 1.0);
135
135
  if (this.options.easing === 'ease-out') {
@@ -200,10 +200,323 @@ class Timer {
200
200
  }
201
201
  }
202
202
 
203
- //----------------------------------------------------------------------------------------------------
204
- // utils
205
- //----------------------------------------------------------------------------------------------------
206
- const SYSTEM_EVENTS = ['start', 'process', 'update', 'stop', 'finalize'];
203
+ const SYSTEM_EVENTS = ['start', 'update', 'render', 'stop', 'finalize'];
204
+
205
+ class EventManager {
206
+ constructor() {
207
+ this.map = new MapMap();
208
+ }
209
+ add(props) {
210
+ let finalize;
211
+ if (props.type === 'resize') {
212
+ finalize = this.resize(props);
213
+ }
214
+ else if (props.type === 'wheel') {
215
+ finalize = this.wheel(props);
216
+ }
217
+ else if (props.type === 'click') {
218
+ finalize = this.click(props);
219
+ }
220
+ else if (props.type === 'click.outside') {
221
+ finalize = this.click_outside(props);
222
+ }
223
+ else if (['pointerdown', 'pointermove', 'pointerup', 'pointerover', 'pointerout'].includes(props.type)) {
224
+ finalize = this.pointer(props);
225
+ }
226
+ else if (['pointerdown.outside', 'pointermove.outside', 'pointerup.outside'].includes(props.type)) {
227
+ finalize = this.pointer_outside(props);
228
+ }
229
+ else if (['mousedown', 'mousemove', 'mouseup', 'mouseover', 'mouseout'].includes(props.type)) {
230
+ finalize = this.mouse(props);
231
+ }
232
+ else if (['touchstart', 'touchmove', 'touchend', 'touchcancel'].includes(props.type)) {
233
+ finalize = this.touch(props);
234
+ }
235
+ else if (['dragstart', 'dragmove', 'dragend'].includes(props.type)) {
236
+ finalize = this.drag(props);
237
+ }
238
+ else if (['gesturestart', 'gesturemove', 'gestureend'].includes(props.type)) {
239
+ finalize = this.gesture(props);
240
+ }
241
+ else if (['keydown', 'keyup'].includes(props.type)) {
242
+ finalize = this.key(props);
243
+ }
244
+ else if (['keydown.arrow', 'keyup.arrow'].includes(props.type)) {
245
+ finalize = this.key_arrow(props);
246
+ }
247
+ else {
248
+ finalize = this.basic(props);
249
+ }
250
+ this.map.set(props.type, props.listener, finalize);
251
+ }
252
+ remove({ type, listener }) {
253
+ const finalize = this.map.get(type, listener);
254
+ if (finalize) {
255
+ finalize();
256
+ this.map.delete(type, listener);
257
+ }
258
+ }
259
+ basic(props) {
260
+ const execute = (event) => {
261
+ props.listener({ event, type: event.type });
262
+ };
263
+ props.element.addEventListener(props.type, execute, props.options);
264
+ return () => {
265
+ props.element.removeEventListener(props.type, execute);
266
+ };
267
+ }
268
+ resize(props) {
269
+ const observer = new ResizeObserver((entries) => {
270
+ for (const entry of entries) {
271
+ props.listener({ type: 'resize' });
272
+ break;
273
+ }
274
+ });
275
+ observer.observe(props.element);
276
+ return () => {
277
+ observer.unobserve(props.element);
278
+ };
279
+ }
280
+ click(props) {
281
+ const execute = (event) => {
282
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
283
+ };
284
+ props.element.addEventListener(props.type, execute, props.options);
285
+ return () => {
286
+ props.element.removeEventListener(props.type, execute);
287
+ };
288
+ }
289
+ click_outside(props) {
290
+ const execute = (event) => {
291
+ if (props.element.contains(event.target) === false) {
292
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
293
+ }
294
+ };
295
+ document.addEventListener(props.type.split('.')[0], execute, props.options);
296
+ return () => {
297
+ document.removeEventListener(props.type.split('.')[0], execute);
298
+ };
299
+ }
300
+ pointer(props) {
301
+ const execute = (event) => {
302
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
303
+ };
304
+ props.element.addEventListener(props.type, execute, props.options);
305
+ return () => {
306
+ props.element.removeEventListener(props.type, execute);
307
+ };
308
+ }
309
+ mouse(props) {
310
+ const execute = (event) => {
311
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
312
+ };
313
+ props.element.addEventListener(props.type, execute, props.options);
314
+ return () => {
315
+ props.element.removeEventListener(props.type, execute);
316
+ };
317
+ }
318
+ touch(props) {
319
+ const execute = (event) => {
320
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
321
+ };
322
+ props.element.addEventListener(props.type, execute, props.options);
323
+ return () => {
324
+ props.element.removeEventListener(props.type, execute);
325
+ };
326
+ }
327
+ pointer_outside(props) {
328
+ const execute = (event) => {
329
+ if (props.element.contains(event.target) === false) {
330
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
331
+ }
332
+ };
333
+ document.addEventListener(props.type.split('.')[0], execute, props.options);
334
+ return () => {
335
+ document.removeEventListener(props.type.split('.')[0], execute);
336
+ };
337
+ }
338
+ wheel(props) {
339
+ const execute = (event) => {
340
+ props.listener({ event, type: props.type, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } });
341
+ };
342
+ props.element.addEventListener(props.type, execute, props.options);
343
+ return () => {
344
+ props.element.removeEventListener(props.type, execute);
345
+ };
346
+ }
347
+ drag(props) {
348
+ let pointermove = null;
349
+ let pointerup = null;
350
+ let pointercancel = null;
351
+ const pointerdown = (event) => {
352
+ const id = event.pointerId;
353
+ const position = pointer(props.element, event).position;
354
+ let previous = position;
355
+ pointermove = (event) => {
356
+ if (event.pointerId === id) {
357
+ const position = pointer(props.element, event).position;
358
+ const delta = { x: position.x - previous.x, y: position.y - previous.y };
359
+ if (props.type === 'dragmove') {
360
+ props.listener({ event, type: props.type, position, delta });
361
+ }
362
+ previous = position;
363
+ }
364
+ };
365
+ pointerup = (event) => {
366
+ if (event.pointerId === id) {
367
+ const position = pointer(props.element, event).position;
368
+ if (props.type === 'dragend') {
369
+ props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
370
+ }
371
+ remove();
372
+ }
373
+ };
374
+ pointercancel = (event) => {
375
+ if (event.pointerId === id) {
376
+ const position = pointer(props.element, event).position;
377
+ if (props.type === 'dragend') {
378
+ props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
379
+ }
380
+ remove();
381
+ }
382
+ };
383
+ window.addEventListener('pointermove', pointermove);
384
+ window.addEventListener('pointerup', pointerup);
385
+ window.addEventListener('pointercancel', pointercancel);
386
+ if (props.type === 'dragstart') {
387
+ props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
388
+ }
389
+ };
390
+ function remove() {
391
+ if (pointermove)
392
+ window.removeEventListener('pointermove', pointermove);
393
+ if (pointerup)
394
+ window.removeEventListener('pointerup', pointerup);
395
+ if (pointercancel)
396
+ window.removeEventListener('pointercancel', pointercancel);
397
+ pointermove = null;
398
+ pointerup = null;
399
+ pointercancel = null;
400
+ }
401
+ props.element.addEventListener('pointerdown', pointerdown, props.options);
402
+ return () => {
403
+ props.element.removeEventListener('pointerdown', pointerdown);
404
+ remove();
405
+ };
406
+ }
407
+ gesture(props) {
408
+ let isActive = false;
409
+ const map = new Map();
410
+ const element = props.element;
411
+ const options = props.options;
412
+ const dragstart = ({ event, position }) => {
413
+ map.set(event.pointerId, position);
414
+ isActive = map.size === 2 ? true : false;
415
+ if (isActive === true && props.type === 'gesturestart') {
416
+ props.listener({ event, type: props.type });
417
+ }
418
+ };
419
+ const dragmove = ({ event, position, delta }) => {
420
+ if (map.size >= 2 && isActive === true) {
421
+ const a = map.get(event.pointerId);
422
+ const b = getOthers(event.pointerId)[0];
423
+ let scale = 0.0;
424
+ {
425
+ const v = { x: a.x - b.x, y: a.y - b.y };
426
+ const s = v.x * v.x + v.y * v.y;
427
+ scale = 1 + (s > 0.0 ? (v.x * delta.x + v.y * delta.y) / s : 0);
428
+ }
429
+ // let rotate = 0.0;
430
+ // {
431
+ // const c = { x: a.x + delta.x, y: a.y + delta.y };
432
+ // const v1 = { x: a.x - b.x, y: a.y - b.y };
433
+ // const v2 = { x: c.x - b.x, y: c.y - b.y };
434
+ // const l1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y);
435
+ // const l2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y);
436
+ // if (l1 > 0.0 && l2 > 0.0) {
437
+ // const angle = Math.acos((v1.x * v2.x + v1.y * v2.y) / (l1 * l2));
438
+ // const sign = v1.x * v2.y - v1.y * v2.x;
439
+ // rotate = sign > 0.0 ? +angle : -angle;
440
+ // }
441
+ // }
442
+ if (props.type === 'gesturemove') {
443
+ props.listener({ event, type: props.type, scale });
444
+ }
445
+ }
446
+ map.set(event.pointerId, position);
447
+ };
448
+ const dragend = ({ event }) => {
449
+ map.delete(event.pointerId);
450
+ if (isActive === true && props.type === 'gestureend') {
451
+ props.listener({ event, type: props.type, scale: 1.0 });
452
+ }
453
+ isActive = false;
454
+ };
455
+ this.add({ element, options, type: 'dragstart', listener: dragstart });
456
+ this.add({ element, options, type: 'dragmove', listener: dragmove });
457
+ this.add({ element, options, type: 'dragend', listener: dragend });
458
+ function getOthers(id) {
459
+ const backup = map.get(id);
460
+ map.delete(id);
461
+ const others = [...map.values()];
462
+ map.set(id, backup);
463
+ return others;
464
+ }
465
+ return () => {
466
+ this.remove({ type: 'dragstart', listener: dragstart });
467
+ this.remove({ type: 'dragmove', listener: dragmove });
468
+ this.remove({ type: 'dragend', listener: dragend });
469
+ };
470
+ }
471
+ key(props) {
472
+ const execute = (event) => {
473
+ if (props.type === 'keydown' && event.repeat)
474
+ return;
475
+ props.listener({ event, type: props.type, code: event.code });
476
+ };
477
+ window.addEventListener(props.type, execute, props.options);
478
+ return () => {
479
+ window.removeEventListener(props.type, execute);
480
+ };
481
+ }
482
+ key_arrow(props) {
483
+ const keymap = {};
484
+ const keydown = (event) => {
485
+ if (event.repeat)
486
+ return;
487
+ keymap[event.code] = 1;
488
+ if (props.type === 'keydown.arrow' && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
489
+ const vector = {
490
+ x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
491
+ y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
492
+ };
493
+ props.listener({ event, type: props.type, code: event.code, vector });
494
+ }
495
+ };
496
+ const keyup = (event) => {
497
+ keymap[event.code] = 0;
498
+ if (props.type === 'keyup.arrow' && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
499
+ const vector = {
500
+ x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
501
+ y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
502
+ };
503
+ props.listener({ event, type: props.type, code: event.code, vector });
504
+ }
505
+ };
506
+ window.addEventListener('keydown', keydown, props.options);
507
+ window.addEventListener('keyup', keyup, props.options);
508
+ return () => {
509
+ window.removeEventListener('keydown', keydown);
510
+ window.removeEventListener('keyup', keyup);
511
+ };
512
+ }
513
+ }
514
+ function pointer(element, event) {
515
+ const rect = element.getBoundingClientRect();
516
+ const position = { x: event.clientX - rect.left, y: event.clientY - rect.top };
517
+ return { position };
518
+ }
519
+
207
520
  //----------------------------------------------------------------------------------------------------
208
521
  // unit
209
522
  //----------------------------------------------------------------------------------------------------
@@ -279,8 +592,8 @@ class Unit {
279
592
  }
280
593
  static initialize(unit, anchor) {
281
594
  var _a, _b;
282
- const backup = Unit.current;
283
- Unit.current = unit;
595
+ const backup = Unit.currentUnit;
596
+ Unit.currentUnit = unit;
284
597
  unit._ = Object.assign(unit._, {
285
598
  currentElement: unit._.baseElement,
286
599
  currentContext: unit._.baseContext,
@@ -296,7 +609,8 @@ class Unit {
296
609
  components: [],
297
610
  listeners: new MapMap(),
298
611
  defines: {},
299
- systems: { start: [], process: [], update: [], stop: [], finalize: [] },
612
+ systems: { start: [], update: [], render: [], stop: [], finalize: [] },
613
+ eventManager: new EventManager(),
300
614
  });
301
615
  // nest html element
302
616
  if (typeof unit._.target === 'string') {
@@ -306,13 +620,13 @@ class Unit {
306
620
  Unit.extend(unit, unit._.baseComponent, unit._.props);
307
621
  // whether the unit promise was resolved
308
622
  Promise.all(unit._.promises.map(p => p.promise)).then(() => unit._.state = 'initialized');
309
- Unit.current = backup;
623
+ Unit.currentUnit = backup;
310
624
  }
311
625
  static finalize(unit) {
312
626
  if (unit._.state !== 'finalized' && unit._.state !== 'finalizing') {
313
627
  unit._.state = 'finalizing';
314
628
  unit._.children.forEach((child) => child.finalize());
315
- unit._.systems.finalize.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
629
+ unit._.systems.finalize.forEach(({ execute }) => execute());
316
630
  unit.off();
317
631
  unit._.components.forEach((component) => Unit.component2units.delete(component, unit));
318
632
  if (unit._.elements.length > 0) {
@@ -392,7 +706,7 @@ class Unit {
392
706
  if (unit._.state === 'initialized' || unit._.state === 'stopped') {
393
707
  unit._.state = 'started';
394
708
  unit._.children.forEach((child) => Unit.start(child));
395
- unit._.systems.start.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
709
+ unit._.systems.start.forEach(({ execute }) => execute());
396
710
  }
397
711
  else if (unit._.state === 'started') {
398
712
  unit._.children.forEach((child) => Unit.start(child));
@@ -402,31 +716,31 @@ class Unit {
402
716
  if (unit._.state === 'started') {
403
717
  unit._.state = 'stopped';
404
718
  unit._.children.forEach((child) => Unit.stop(child));
405
- unit._.systems.stop.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
719
+ unit._.systems.stop.forEach(({ execute }) => execute());
406
720
  }
407
721
  }
408
722
  static update(unit) {
409
- if (unit._.state === 'started' || unit._.state === 'stopped') {
723
+ if (unit._.state === 'started') {
410
724
  unit._.children.forEach((child) => Unit.update(child));
411
- unit._.systems.update.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
725
+ unit._.systems.update.forEach(({ execute }) => execute());
412
726
  }
413
727
  }
414
- static process(unit) {
415
- if (unit._.state === 'started') {
416
- unit._.children.forEach((child) => Unit.process(child));
417
- unit._.systems.process.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
728
+ static render(unit) {
729
+ if (unit._.state === 'started' || unit._.state === 'started' || unit._.state === 'stopped') {
730
+ unit._.children.forEach((child) => Unit.render(child));
731
+ unit._.systems.render.forEach(({ execute }) => execute());
418
732
  }
419
733
  }
420
734
  static reset() {
421
- var _a, _b;
422
- (_a = Unit.root) === null || _a === void 0 ? void 0 : _a.finalize();
423
- Unit.current = Unit.root = new Unit(null, null);
424
- (_b = Unit.ticker) === null || _b === void 0 ? void 0 : _b.clear();
425
- Unit.ticker = new Ticker(() => {
426
- Unit.start(Unit.root);
427
- Unit.process(Unit.root);
428
- Unit.update(Unit.root);
735
+ var _a;
736
+ (_a = Unit.rootUnit) === null || _a === void 0 ? void 0 : _a.finalize();
737
+ Unit.currentUnit = Unit.rootUnit = new Unit(null, null);
738
+ const ticker = new AnimationTicker(() => {
739
+ Unit.start(Unit.rootUnit);
740
+ Unit.update(Unit.rootUnit);
741
+ Unit.render(Unit.rootUnit);
429
742
  });
743
+ Unit.rootUnit.on('finalize', () => ticker.clear());
430
744
  }
431
745
  static wrap(unit, listener) {
432
746
  const snapshot = Unit.snapshot(unit);
@@ -436,25 +750,27 @@ class Unit {
436
750
  if (snapshot.unit._.state === 'finalized') {
437
751
  return;
438
752
  }
439
- const current = Unit.current;
753
+ const currentUnit = Unit.currentUnit;
440
754
  const backup = Unit.snapshot(snapshot.unit);
441
755
  try {
442
- Unit.current = snapshot.unit;
756
+ Unit.currentUnit = snapshot.unit;
443
757
  snapshot.unit._.currentContext = snapshot.context;
444
758
  snapshot.unit._.currentElement = snapshot.element;
759
+ snapshot.unit._.currentComponent = snapshot.component;
445
760
  return func(...args);
446
761
  }
447
762
  catch (error) {
448
763
  throw error;
449
764
  }
450
765
  finally {
451
- Unit.current = current;
766
+ Unit.currentUnit = currentUnit;
452
767
  snapshot.unit._.currentContext = backup.context;
453
768
  snapshot.unit._.currentElement = backup.element;
769
+ snapshot.unit._.currentComponent = backup.component;
454
770
  }
455
771
  }
456
772
  static snapshot(unit) {
457
- return { unit, context: unit._.currentContext, element: unit._.currentElement };
773
+ return { unit, context: unit._.currentContext, element: unit._.currentElement, component: unit._.currentComponent };
458
774
  }
459
775
  static context(unit, key, value) {
460
776
  if (value !== undefined) {
@@ -472,43 +788,47 @@ class Unit {
472
788
  return [...((_a = Unit.component2units.get(component)) !== null && _a !== void 0 ? _a : [])];
473
789
  }
474
790
  on(type, listener, options) {
475
- type.trim().split(/\s+/).forEach((type) => {
476
- if (SYSTEM_EVENTS.includes(type)) {
477
- this._.systems[type].push(listener);
478
- }
479
- if (this._.listeners.has(type, listener) === false) {
480
- const execute = Unit.wrap(Unit.current, listener);
481
- this._.listeners.set(type, listener, { element: this.element, execute });
482
- Unit.type2units.add(type, this);
483
- if (/^[A-Za-z]/.test(type)) {
484
- this.element.addEventListener(type, execute, options);
485
- }
486
- }
487
- });
791
+ const types = type.trim().split(/\s+/);
792
+ types.forEach((type) => Unit.on(this, type, listener, options));
488
793
  }
489
794
  off(type, listener) {
490
795
  const types = typeof type === 'string' ? type.trim().split(/\s+/) : [...this._.listeners.keys()];
491
- types.forEach((type) => {
492
- if (SYSTEM_EVENTS.includes(type)) {
493
- this._.systems[type] = this._.systems[type].filter((lis) => listener ? lis !== listener : false);
796
+ types.forEach((type) => Unit.off(this, type, listener));
797
+ }
798
+ static on(unit, type, listener, options) {
799
+ if (SYSTEM_EVENTS.includes(type)) {
800
+ const execute = Unit.wrap(Unit.currentUnit, listener);
801
+ unit._.systems[type].push({ listener, execute });
802
+ }
803
+ if (unit._.listeners.has(type, listener) === false) {
804
+ const execute = Unit.wrap(Unit.currentUnit, listener);
805
+ unit._.listeners.set(type, listener, { element: unit.element, component: unit._.currentComponent, execute });
806
+ Unit.type2units.add(type, unit);
807
+ if (/^[A-Za-z]/.test(type)) {
808
+ unit._.eventManager.add({ element: unit.element, type, listener: execute, options });
494
809
  }
495
- (listener ? [listener] : [...this._.listeners.keys(type)]).forEach((listener) => {
496
- const item = this._.listeners.get(type, listener);
497
- if (item !== undefined) {
498
- this._.listeners.delete(type, listener);
499
- if (/^[A-Za-z]/.test(type)) {
500
- item.element.removeEventListener(type, item.execute);
501
- }
502
- }
503
- });
504
- if (this._.listeners.has(type) === false) {
505
- Unit.type2units.delete(type, this);
810
+ }
811
+ }
812
+ static off(unit, type, listener) {
813
+ if (SYSTEM_EVENTS.includes(type)) {
814
+ unit._.systems[type] = unit._.systems[type].filter(({ listener: lis }) => listener ? lis !== listener : false);
815
+ }
816
+ (listener ? [listener] : [...unit._.listeners.keys(type)]).forEach((listener) => {
817
+ const item = unit._.listeners.get(type, listener);
818
+ if (item === undefined)
819
+ return;
820
+ unit._.listeners.delete(type, listener);
821
+ if (/^[A-Za-z]/.test(type)) {
822
+ unit._.eventManager.remove({ type, listener: item.execute });
506
823
  }
507
824
  });
825
+ if (unit._.listeners.has(type) === false) {
826
+ Unit.type2units.delete(type, unit);
827
+ }
508
828
  }
509
829
  static emit(type, ...args) {
510
830
  var _a, _b;
511
- const current = Unit.current;
831
+ const current = Unit.currentUnit;
512
832
  if (type[0] === '+') {
513
833
  (_a = Unit.type2units.get(type)) === null || _a === void 0 ? void 0 : _a.forEach((unit) => {
514
834
  var _a;
@@ -523,6 +843,7 @@ class Unit {
523
843
  }
524
844
  }
525
845
  }
846
+ Unit.currentComponent = () => { };
526
847
  Unit.component2units = new MapSet();
527
848
  //----------------------------------------------------------------------------------------------------
528
849
  // event
@@ -537,15 +858,15 @@ class UnitPromise {
537
858
  this.component = component;
538
859
  }
539
860
  then(callback) {
540
- this.promise = this.promise.then(Unit.wrap(Unit.current, callback));
861
+ this.promise = this.promise.then(Unit.wrap(Unit.currentUnit, callback));
541
862
  return this;
542
863
  }
543
864
  catch(callback) {
544
- this.promise = this.promise.catch(Unit.wrap(Unit.current, callback));
865
+ this.promise = this.promise.catch(Unit.wrap(Unit.currentUnit, callback));
545
866
  return this;
546
867
  }
547
868
  finally(callback) {
548
- this.promise = this.promise.finally(Unit.wrap(Unit.current, callback));
869
+ this.promise = this.promise.finally(Unit.wrap(Unit.currentUnit, callback));
549
870
  return this;
550
871
  }
551
872
  }
@@ -555,7 +876,7 @@ class UnitPromise {
555
876
  class UnitTimer {
556
877
  constructor(options) {
557
878
  this.stack = [];
558
- this.unit = new Unit(Unit.current, UnitTimer.Component, Object.assign({ snapshot: Unit.snapshot(Unit.current) }, options));
879
+ this.unit = new Unit(Unit.currentUnit, UnitTimer.Component, Object.assign({ snapshot: Unit.snapshot(Unit.currentUnit) }, options));
559
880
  }
560
881
  clear() {
561
882
  this.stack = [];
@@ -575,19 +896,19 @@ class UnitTimer {
575
896
  }
576
897
  static execute(timer, options) {
577
898
  if (timer.unit._.state === 'finalized') {
578
- timer.unit = new Unit(Unit.current, UnitTimer.Component, Object.assign({ snapshot: Unit.snapshot(Unit.current) }, options));
899
+ timer.unit = new Unit(Unit.currentUnit, UnitTimer.Component, Object.assign({ snapshot: Unit.snapshot(Unit.currentUnit) }, options));
579
900
  }
580
901
  else if (timer.stack.length === 0) {
581
- timer.stack.push(Object.assign({ snapshot: Unit.snapshot(Unit.current) }, options));
902
+ timer.stack.push(Object.assign({ snapshot: Unit.snapshot(Unit.currentUnit) }, options));
582
903
  timer.unit.on('finalize', () => { UnitTimer.next(timer); });
583
904
  }
584
905
  else {
585
- timer.stack.push(Object.assign({ snapshot: Unit.snapshot(Unit.current) }, options));
906
+ timer.stack.push(Object.assign({ snapshot: Unit.snapshot(Unit.currentUnit) }, options));
586
907
  }
587
908
  }
588
909
  static next(timer) {
589
910
  if (timer.stack.length > 0) {
590
- timer.unit = new Unit(Unit.current, UnitTimer.Component, timer.stack.shift());
911
+ timer.unit = new Unit(Unit.currentUnit, UnitTimer.Component, timer.stack.shift());
591
912
  timer.unit.on('finalize', () => { UnitTimer.next(timer); });
592
913
  }
593
914
  }
@@ -614,10 +935,10 @@ class UnitTimer {
614
935
  }
615
936
 
616
937
  const xnew$1 = Object.assign(function (...args) {
617
- if (Unit.root === undefined) {
938
+ if (Unit.rootUnit === undefined) {
618
939
  Unit.reset();
619
940
  }
620
- return new Unit(Unit.current, ...args);
941
+ return new Unit(Unit.currentUnit, ...args);
621
942
  }, {
622
943
  /**
623
944
  * Creates a nested HTML/SVG element within the current component
@@ -630,7 +951,7 @@ const xnew$1 = Object.assign(function (...args) {
630
951
  */
631
952
  nest(tag) {
632
953
  try {
633
- return Unit.nest(Unit.current, tag);
954
+ return Unit.nest(Unit.currentUnit, tag);
634
955
  }
635
956
  catch (error) {
636
957
  console.error('xnew.nest(tag: string): ', error);
@@ -648,7 +969,7 @@ const xnew$1 = Object.assign(function (...args) {
648
969
  */
649
970
  extend(component, props) {
650
971
  try {
651
- return Unit.extend(Unit.current, component, props);
972
+ return Unit.extend(Unit.currentUnit, component, props);
652
973
  }
653
974
  catch (error) {
654
975
  console.error('xnew.extend(component: Function, props?: Object): ', error);
@@ -669,7 +990,7 @@ const xnew$1 = Object.assign(function (...args) {
669
990
  */
670
991
  context(key, value = undefined) {
671
992
  try {
672
- return Unit.context(Unit.current, key, value);
993
+ return Unit.context(Unit.currentUnit, key, value);
673
994
  }
674
995
  catch (error) {
675
996
  console.error('xnew.context(key: string, value?: any): ', error);
@@ -685,9 +1006,9 @@ const xnew$1 = Object.assign(function (...args) {
685
1006
  */
686
1007
  promise(promise) {
687
1008
  try {
688
- const component = Unit.current._.currentComponent;
689
- Unit.current._.promises.push(new UnitPromise(promise, component));
690
- return Unit.current._.promises[Unit.current._.promises.length - 1];
1009
+ const component = Unit.currentUnit._.currentComponent;
1010
+ Unit.currentUnit._.promises.push(new UnitPromise(promise, component));
1011
+ return Unit.currentUnit._.promises[Unit.currentUnit._.promises.length - 1];
691
1012
  }
692
1013
  catch (error) {
693
1014
  console.error('xnew.promise(promise: Promise<any>): ', error);
@@ -703,8 +1024,8 @@ const xnew$1 = Object.assign(function (...args) {
703
1024
  */
704
1025
  then(callback) {
705
1026
  try {
706
- const component = Unit.current._.currentComponent;
707
- const promises = Unit.current._.promises;
1027
+ const component = Unit.currentUnit._.currentComponent;
1028
+ const promises = Unit.currentUnit._.promises;
708
1029
  return new UnitPromise(Promise.all(promises.map(p => p.promise)), null)
709
1030
  .then((results) => {
710
1031
  callback(results.filter((_result, index) => promises[index].component !== null && promises[index].component === component));
@@ -724,7 +1045,7 @@ const xnew$1 = Object.assign(function (...args) {
724
1045
  */
725
1046
  catch(callback) {
726
1047
  try {
727
- const promises = Unit.current._.promises;
1048
+ const promises = Unit.currentUnit._.promises;
728
1049
  return new UnitPromise(Promise.all(promises.map(p => p.promise)), null)
729
1050
  .catch(callback);
730
1051
  }
@@ -742,7 +1063,7 @@ const xnew$1 = Object.assign(function (...args) {
742
1063
  */
743
1064
  finally(callback) {
744
1065
  try {
745
- const promises = Unit.current._.promises;
1066
+ const promises = Unit.currentUnit._.promises;
746
1067
  return new UnitPromise(Promise.all(promises.map(p => p.promise)), null)
747
1068
  .finally(callback);
748
1069
  }
@@ -761,7 +1082,7 @@ const xnew$1 = Object.assign(function (...args) {
761
1082
  * }), 1000)
762
1083
  */
763
1084
  scope(callback) {
764
- const snapshot = Unit.snapshot(Unit.current);
1085
+ const snapshot = Unit.snapshot(Unit.currentUnit);
765
1086
  return (...args) => Unit.scope(snapshot, callback, ...args);
766
1087
  },
767
1088
  /**
@@ -832,7 +1153,7 @@ const xnew$1 = Object.assign(function (...args) {
832
1153
  return new UnitTimer({ transition, duration, easing, iterations: 1 });
833
1154
  },
834
1155
  protect() {
835
- Unit.current._.protected = true;
1156
+ Unit.currentUnit._.protected = true;
836
1157
  }
837
1158
  });
838
1159
 
@@ -867,12 +1188,12 @@ function AccordionFrame(frame, { open = false, duration = 200, easing = 'ease' }
867
1188
  }
868
1189
  };
869
1190
  }
870
- function AccordionHeader(header, {} = {}) {
1191
+ function AccordionHeader(unit, {} = {}) {
871
1192
  const internal = xnew$1.context('xnew.accordionframe');
872
1193
  xnew$1.nest('<button style="display: flex; align-items: center; margin: 0; padding: 0; width: 100%; text-align: left; border: none; font: inherit; color: inherit; background: none; cursor: pointer;">');
873
- header.on('click', () => internal.frame.toggle());
1194
+ unit.on('click', () => internal.frame.toggle());
874
1195
  }
875
- function AccordionBullet(bullet, { type = 'arrow' } = {}) {
1196
+ function AccordionBullet(unit, { type = 'arrow' } = {}) {
876
1197
  const internal = xnew$1.context('xnew.accordionframe');
877
1198
  xnew$1.nest('<div style="display:inline-block; position: relative; width: 0.55em; margin: 0 0.3em;">');
878
1199
  if (type === 'arrow') {
@@ -893,12 +1214,12 @@ function AccordionBullet(bullet, { type = 'arrow' } = {}) {
893
1214
  });
894
1215
  }
895
1216
  }
896
- function AccordionContent(content, {} = {}) {
1217
+ function AccordionContent(unit, {} = {}) {
897
1218
  const internal = xnew$1.context('xnew.accordionframe');
898
1219
  xnew$1.nest(`<div style="display: ${internal.open ? 'block' : 'none'};">`);
899
1220
  xnew$1.nest('<div style="padding: 0; display: flex; flex-direction: column; box-sizing: border-box;">');
900
1221
  internal.on('-transition', ({ rate }) => {
901
- content.transition({ element: content.element, rate });
1222
+ unit.transition({ element: unit.element, rate });
902
1223
  });
903
1224
  return {
904
1225
  transition({ element, rate }) {
@@ -917,209 +1238,12 @@ function AccordionContent(content, {} = {}) {
917
1238
  };
918
1239
  }
919
1240
 
920
- function ResizeEvent(resize) {
921
- const observer = new ResizeObserver(xnew$1.scope((entries) => {
922
- for (const entry of entries) {
923
- xnew$1.emit('-resize');
924
- break;
925
- }
926
- }));
927
- if (resize.element) {
928
- observer.observe(resize.element);
929
- }
930
- resize.on('finalize', () => {
931
- if (resize.element) {
932
- observer.unobserve(resize.element);
933
- }
934
- });
935
- }
936
- function DirectEvent(unit) {
937
- const state = {};
938
- const keydown = xnew$1.scope((event) => {
939
- if (event.repeat)
940
- return;
941
- state[event.code] = 1;
942
- xnew$1.emit('-keydown', { event, type: '-keydown', code: event.code });
943
- if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
944
- xnew$1.emit('-keydown.arrow', { event, type: '-keydown.arrow', code: event.code, vector: getVector() });
945
- }
946
- });
947
- const keyup = xnew$1.scope((event) => {
948
- state[event.code] = 0;
949
- xnew$1.emit('-keyup', { event, type: '-keyup', code: event.code });
950
- if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
951
- xnew$1.emit('-keyup.arrow', { event, type: '-keyup.arrow', code: event.code, vector: getVector() });
952
- }
953
- });
954
- window.addEventListener('keydown', keydown);
955
- window.addEventListener('keyup', keyup);
956
- unit.on('finalize', () => {
957
- window.removeEventListener('keydown', keydown);
958
- window.removeEventListener('keyup', keyup);
959
- });
960
- function getVector() {
961
- return {
962
- x: (state['ArrowLeft'] ? -1 : 0) + (state['ArrowRight'] ? +1 : 0),
963
- y: (state['ArrowUp'] ? -1 : 0) + (state['ArrowDown'] ? +1 : 0)
964
- };
965
- }
966
- const internal = xnew$1();
967
- internal.on('pointerdown', (event) => xnew$1.emit('-pointerdown', { event, position: getPosition(unit.element, event) }));
968
- internal.on('pointermove', (event) => xnew$1.emit('-pointermove', { event, position: getPosition(unit.element, event) }));
969
- internal.on('pointerup', (event) => xnew$1.emit('-pointerup', { event, position: getPosition(unit.element, event) }));
970
- internal.on('wheel', (event) => xnew$1.emit('-wheel', { event, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } }));
971
- internal.on('click', (event) => xnew$1.emit('-click', { event, position: getPosition(unit.element, event) }));
972
- internal.on('pointerover', (event) => xnew$1.emit('-pointerover', { event, position: getPosition(unit.element, event) }));
973
- internal.on('pointerout', (event) => xnew$1.emit('-pointerout', { event, position: getPosition(unit.element, event) }));
974
- const pointerdownoutside = xnew$1.scope((event) => {
975
- if (unit.element.contains(event.target) === false) {
976
- xnew$1.emit('-pointerdown.outside', { event, position: getPosition(unit.element, event) });
977
- }
978
- });
979
- const pointerupoutside = xnew$1.scope((event) => {
980
- if (unit.element.contains(event.target) === false) {
981
- xnew$1.emit('-pointerup.outside', { event, position: getPosition(unit.element, event) });
982
- }
983
- });
984
- const clickoutside = xnew$1.scope((event) => {
985
- if (unit.element.contains(event.target) === false) {
986
- xnew$1.emit('-click.outside', { event, position: getPosition(unit.element, event) });
987
- }
988
- });
989
- document.addEventListener('pointerdown', pointerdownoutside);
990
- document.addEventListener('pointerup', pointerupoutside);
991
- document.addEventListener('click', clickoutside);
992
- unit.on('finalize', () => {
993
- document.removeEventListener('pointerdown', pointerdownoutside);
994
- document.removeEventListener('pointerup', pointerupoutside);
995
- document.removeEventListener('click', clickoutside);
996
- });
997
- const drag = xnew$1(DragEvent);
998
- drag.on('-dragstart', (...args) => xnew$1.emit('-dragstart', ...args));
999
- drag.on('-dragmove', (...args) => xnew$1.emit('-dragmove', ...args));
1000
- drag.on('-dragend', (...args) => xnew$1.emit('-dragend', ...args));
1001
- drag.on('-dragcancel', (...args) => xnew$1.emit('-dragcancel', ...args));
1002
- const gesture = xnew$1(GestureEvent);
1003
- gesture.on('-gesturestart', (...args) => xnew$1.emit('-gesturestart', ...args));
1004
- gesture.on('-gesturemove', (...args) => xnew$1.emit('-gesturemove', ...args));
1005
- gesture.on('-gestureend', (...args) => xnew$1.emit('-gestureend', ...args));
1006
- gesture.on('-gesturecancel', (...args) => xnew$1.emit('-gesturecancel', ...args));
1007
- }
1008
- function DragEvent(unit) {
1009
- const pointerdown = xnew$1.scope((event) => {
1010
- const id = event.pointerId;
1011
- const position = getPosition(unit.element, event);
1012
- let previous = position;
1013
- let connect = true;
1014
- const pointermove = xnew$1.scope((event) => {
1015
- if (event.pointerId === id) {
1016
- const position = getPosition(unit.element, event);
1017
- const delta = { x: position.x - previous.x, y: position.y - previous.y };
1018
- xnew$1.emit('-dragmove', { event, position, delta });
1019
- previous = position;
1020
- }
1021
- });
1022
- const pointerup = xnew$1.scope((event) => {
1023
- if (event.pointerId === id) {
1024
- const position = getPosition(unit.element, event);
1025
- xnew$1.emit('-dragend', { event, position, });
1026
- remove();
1027
- }
1028
- });
1029
- const pointercancel = xnew$1.scope((event) => {
1030
- if (event.pointerId === id) {
1031
- const position = getPosition(unit.element, event);
1032
- xnew$1.emit('-dragcancel', { event, position, });
1033
- remove();
1034
- }
1035
- });
1036
- window.addEventListener('pointermove', pointermove);
1037
- window.addEventListener('pointerup', pointerup);
1038
- window.addEventListener('pointercancel', pointercancel);
1039
- function remove() {
1040
- if (connect === true) {
1041
- window.removeEventListener('pointermove', pointermove);
1042
- window.removeEventListener('pointerup', pointerup);
1043
- window.removeEventListener('pointercancel', pointercancel);
1044
- connect = false;
1045
- }
1046
- }
1047
- xnew$1((unit) => unit.on('-finalize', remove));
1048
- xnew$1.emit('-dragstart', { event, position });
1049
- });
1050
- unit.on('pointerdown', pointerdown);
1051
- }
1052
- function GestureEvent(unit) {
1053
- const drag = xnew$1(DragEvent);
1054
- let isActive = false;
1055
- const map = new Map();
1056
- drag.on('-dragstart', ({ event, position }) => {
1057
- map.set(event.pointerId, Object.assign({}, position));
1058
- isActive = map.size === 2 ? true : false;
1059
- if (isActive === true) {
1060
- xnew$1.emit('-gesturestart', {});
1061
- }
1062
- });
1063
- drag.on('-dragmove', ({ event, position, delta }) => {
1064
- if (map.size >= 2 && isActive === true) {
1065
- const a = map.get(event.pointerId);
1066
- const b = getOthers(event.pointerId)[0];
1067
- let scale = 0.0;
1068
- {
1069
- const v = { x: a.x - b.x, y: a.y - b.y };
1070
- const s = v.x * v.x + v.y * v.y;
1071
- scale = 1 + (s > 0.0 ? (v.x * delta.x + v.y * delta.y) / s : 0);
1072
- }
1073
- // let rotate = 0.0;
1074
- // {
1075
- // const c = { x: a.x + delta.x, y: a.y + delta.y };
1076
- // const v1 = { x: a.x - b.x, y: a.y - b.y };
1077
- // const v2 = { x: c.x - b.x, y: c.y - b.y };
1078
- // const l1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y);
1079
- // const l2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y);
1080
- // if (l1 > 0.0 && l2 > 0.0) {
1081
- // const angle = Math.acos((v1.x * v2.x + v1.y * v2.y) / (l1 * l2));
1082
- // const sign = v1.x * v2.y - v1.y * v2.x;
1083
- // rotate = sign > 0.0 ? +angle : -angle;
1084
- // }
1085
- // }
1086
- xnew$1.emit('-gesturemove', { event, position, delta, scale });
1087
- }
1088
- map.set(event.pointerId, position);
1089
- });
1090
- drag.on('-dragend', ({ event }) => {
1091
- if (isActive === true) {
1092
- xnew$1.emit('-gestureend', {});
1093
- }
1094
- isActive = false;
1095
- map.delete(event.pointerId);
1096
- });
1097
- drag.on('-dragcancel', ({ event }) => {
1098
- if (isActive === true) {
1099
- xnew$1.emit('-gesturecancel', { event });
1100
- }
1101
- isActive = false;
1102
- map.delete(event.pointerId);
1103
- });
1104
- function getOthers(id) {
1105
- const backup = map.get(id);
1106
- map.delete(id);
1107
- const others = [...map.values()];
1108
- map.set(id, backup);
1109
- return others;
1110
- }
1111
- }
1112
- function getPosition(element, event) {
1113
- const rect = element.getBoundingClientRect();
1114
- return { x: event.clientX - rect.left, y: event.clientY - rect.top };
1115
- }
1116
-
1117
- function Screen(screen, { width = 640, height = 480, fit = 'contain' } = {}) {
1241
+ function Screen(unit, { width = 640, height = 480, fit = 'contain' } = {}) {
1118
1242
  const size = { width, height };
1119
1243
  const wrapper = xnew$1.nest('<div style="position: relative; width: 100%; height: 100%; overflow: hidden;">');
1244
+ unit.on('resize', resize);
1120
1245
  const absolute = xnew$1.nest('<div style="position: absolute; margin: auto; container-type: size; overflow: hidden;">');
1121
1246
  const canvas = xnew$1(`<canvas width="${width}" height="${height}" style="width: 100%; height: 100%; vertical-align: bottom; user-select: none; user-drag: none; pointer-events: auto;">`);
1122
- xnew$1(wrapper, ResizeEvent).on('-resize', resize);
1123
1247
  resize();
1124
1248
  function resize() {
1125
1249
  const aspect = size.width / size.height;
@@ -1260,15 +1384,14 @@ function DragFrame(frame, { x = 0, y = 0 } = {}) {
1260
1384
  const absolute = xnew$1.nest(`<div style="position: absolute; top: ${y}px; left: ${x}px;">`);
1261
1385
  xnew$1.context('xnew.dragframe', { frame, absolute });
1262
1386
  }
1263
- function DragTarget(target, {} = {}) {
1387
+ function DragTarget(unit, {} = {}) {
1264
1388
  const { frame, absolute } = xnew$1.context('xnew.dragframe');
1265
- xnew$1.nest('<div>');
1266
- const direct = xnew$1(absolute.parentElement, DirectEvent);
1389
+ const target = xnew$1(absolute.parentElement);
1267
1390
  const current = { x: 0, y: 0 };
1268
1391
  const offset = { x: 0, y: 0 };
1269
1392
  let dragged = false;
1270
- direct.on('-dragstart', ({ event, position }) => {
1271
- if (target.element.contains(event.target) === false)
1393
+ target.on('dragstart', ({ event, position }) => {
1394
+ if (unit.element.contains(event.target) === false)
1272
1395
  return;
1273
1396
  dragged = true;
1274
1397
  offset.x = position.x - parseFloat(absolute.style.left || '0');
@@ -1276,7 +1399,7 @@ function DragTarget(target, {} = {}) {
1276
1399
  current.x = position.x - offset.x;
1277
1400
  current.y = position.y - offset.y;
1278
1401
  });
1279
- direct.on('-dragmove', ({ event, delta }) => {
1402
+ target.on('dragmove', ({ event, delta }) => {
1280
1403
  if (dragged !== true)
1281
1404
  return;
1282
1405
  current.x += delta.x;
@@ -1284,9 +1407,10 @@ function DragTarget(target, {} = {}) {
1284
1407
  absolute.style.left = `${current.x}px`;
1285
1408
  absolute.style.top = `${current.y}px`;
1286
1409
  });
1287
- direct.on('-dragcancel -dragend', ({ event }) => {
1410
+ target.on('dragend', ({ event }) => {
1288
1411
  dragged = false;
1289
1412
  });
1413
+ xnew$1.nest('<div>');
1290
1414
  }
1291
1415
 
1292
1416
  //----------------------------------------------------------------------------------------------------
@@ -1295,7 +1419,7 @@ function DragTarget(target, {} = {}) {
1295
1419
  function SVGTemplate(self, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 2, strokeLinejoin = 'round', fill = null, fillOpacity = 0.8 }) {
1296
1420
  xnew$1.nest(`<svg
1297
1421
  viewBox="0 0 100 100"
1298
- style="position: absolute; width: 100%; height: 100%; pointer-select: none;
1422
+ style="position: absolute; width: 100%; height: 100%; select: none;
1299
1423
  stroke: ${stroke}; stroke-opacity: ${strokeOpacity}; stroke-width: ${strokeWidth}; stroke-linejoin: ${strokeLinejoin};
1300
1424
  ${fill ? `fill: ${fill}; fill-opacity: ${fillOpacity};` : ''}
1301
1425
  ">`);
@@ -1305,7 +1429,7 @@ function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strok
1305
1429
  const internal = xnew$1((unit) => {
1306
1430
  let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1307
1431
  const inner = xnew$1.nest(`<div style="position: absolute; width: ${newsize}px; height: ${newsize}px; margin: auto; inset: 0; cursor: pointer; pointer-select: none; pointer-events: auto; overflow: hidden;">`);
1308
- xnew$1(outer, ResizeEvent).on('-resize', () => {
1432
+ xnew$1(outer).on('resize', () => {
1309
1433
  newsize = Math.min(outer.clientWidth, outer.clientHeight);
1310
1434
  inner.style.width = `${newsize}px`;
1311
1435
  inner.style.height = `${newsize}px`;
@@ -1321,22 +1445,21 @@ function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strok
1321
1445
  xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1322
1446
  xnew$1('<circle cx="50" cy="50" r="23">');
1323
1447
  });
1324
- const direct = xnew$1(DirectEvent);
1325
- direct.on('-dragstart', ({ event, position }) => {
1448
+ unit.on('dragstart', ({ event, position }) => {
1326
1449
  const vector = getVector(position);
1327
1450
  target.element.style.filter = 'brightness(90%)';
1328
1451
  target.element.style.left = vector.x * newsize / 4 + 'px';
1329
1452
  target.element.style.top = vector.y * newsize / 4 + 'px';
1330
1453
  xnew$1.emit('-down', { vector });
1331
1454
  });
1332
- direct.on('-dragmove', ({ event, position }) => {
1455
+ unit.on('dragmove', ({ event, position }) => {
1333
1456
  const vector = getVector(position);
1334
1457
  target.element.style.filter = 'brightness(90%)';
1335
1458
  target.element.style.left = vector.x * newsize / 4 + 'px';
1336
1459
  target.element.style.top = vector.y * newsize / 4 + 'px';
1337
1460
  xnew$1.emit('-move', { vector });
1338
1461
  });
1339
- direct.on('-dragend', ({ event }) => {
1462
+ unit.on('dragend', ({ event }) => {
1340
1463
  const vector = { x: 0, y: 0 };
1341
1464
  target.element.style.filter = '';
1342
1465
  target.element.style.left = vector.x * newsize / 4 + 'px';
@@ -1360,7 +1483,7 @@ function DirectionalPad(unit, { diagonal = true, stroke = 'currentColor', stroke
1360
1483
  const internal = xnew$1((unit) => {
1361
1484
  let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1362
1485
  const inner = xnew$1.nest(`<div style="position: absolute; width: ${newsize}px; height: ${newsize}px; margin: auto; inset: 0; cursor: pointer; pointer-select: none; pointer-events: auto; overflow: hidden;">`);
1363
- xnew$1(outer, ResizeEvent).on('-resize', () => {
1486
+ xnew$1(outer).on('resize', () => {
1364
1487
  newsize = Math.min(outer.clientWidth, outer.clientHeight);
1365
1488
  inner.style.width = `${newsize}px`;
1366
1489
  inner.style.height = `${newsize}px`;
@@ -1388,8 +1511,7 @@ function DirectionalPad(unit, { diagonal = true, stroke = 'currentColor', stroke
1388
1511
  xnew$1('<polygon points="11 50 20 42 20 58">');
1389
1512
  xnew$1('<polygon points="89 50 80 42 80 58">');
1390
1513
  });
1391
- const direct = xnew$1(DirectEvent);
1392
- direct.on('-dragstart', ({ event, position }) => {
1514
+ unit.on('dragstart', ({ event, position }) => {
1393
1515
  const vector = getVector(position);
1394
1516
  targets[0].element.style.filter = (vector.y < 0) ? 'brightness(90%)' : '';
1395
1517
  targets[1].element.style.filter = (vector.y > 0) ? 'brightness(90%)' : '';
@@ -1397,7 +1519,7 @@ function DirectionalPad(unit, { diagonal = true, stroke = 'currentColor', stroke
1397
1519
  targets[3].element.style.filter = (vector.x > 0) ? 'brightness(90%)' : '';
1398
1520
  xnew$1.emit('-down', { vector });
1399
1521
  });
1400
- direct.on('-dragmove', ({ event, position }) => {
1522
+ unit.on('dragmove', ({ event, position }) => {
1401
1523
  const vector = getVector(position);
1402
1524
  targets[0].element.style.filter = (vector.y < 0) ? 'brightness(90%)' : '';
1403
1525
  targets[1].element.style.filter = (vector.y > 0) ? 'brightness(90%)' : '';
@@ -1405,7 +1527,7 @@ function DirectionalPad(unit, { diagonal = true, stroke = 'currentColor', stroke
1405
1527
  targets[3].element.style.filter = (vector.x > 0) ? 'brightness(90%)' : '';
1406
1528
  xnew$1.emit('-move', { vector });
1407
1529
  });
1408
- direct.on('-dragend', ({ event }) => {
1530
+ unit.on('dragend', ({ event }) => {
1409
1531
  const vector = { x: 0, y: 0 };
1410
1532
  targets[0].element.style.filter = '';
1411
1533
  targets[1].element.style.filter = '';
@@ -1457,249 +1579,29 @@ function TextStream(unit, { text = '', speed = 50, fade = 300 } = {}) {
1457
1579
  const index = Math.floor((new Date().getTime() - start) / speed);
1458
1580
  // Display characters up to the current index (fade in)
1459
1581
  for (let i = 0; i < chars.length; i++) {
1460
- if (i <= index) {
1461
- chars[i].element.style.opacity = '1';
1462
- }
1463
- }
1464
- if (state === 0 && index >= text.length) {
1465
- action();
1466
- }
1467
- });
1468
- xnew$1.timeout(() => {
1469
- xnew$1(document.body).on('click wheel', action);
1470
- xnew$1(DirectEvent).on('-keydown', action);
1471
- }, 100);
1472
- function action() {
1473
- if (state === 0) {
1474
- state = 1;
1475
- for (let i = 0; i < chars.length; i++) {
1476
- chars[i].element.style.opacity = '1';
1477
- }
1478
- xnew$1.emit('-complete');
1479
- }
1480
- else if (state === 1) {
1481
- state = 2;
1482
- xnew$1.emit('-next');
1483
- }
1484
- }
1485
- }
1486
-
1487
- const context = window.AudioContext ? new window.AudioContext() : (null);
1488
- const master = context ? context.createGain() : (null);
1489
- if (context) {
1490
- master.gain.value = 0.1;
1491
- master.connect(context.destination);
1492
- }
1493
- class AudioFile {
1494
- constructor(path) {
1495
- this.promise = fetch(path)
1496
- .then((response) => response.arrayBuffer())
1497
- .then((response) => context.decodeAudioData(response))
1498
- .then((response) => { this.buffer = response; })
1499
- .catch(() => {
1500
- console.warn(`"${path}" could not be loaded.`);
1501
- });
1502
- this.amp = context.createGain();
1503
- this.amp.gain.value = 1.0;
1504
- this.amp.connect(master);
1505
- this.fade = context.createGain();
1506
- this.fade.gain.value = 1.0;
1507
- this.fade.connect(this.amp);
1508
- this.source = null;
1509
- this.played = null;
1510
- }
1511
- set volume(value) {
1512
- this.amp.gain.value = value;
1513
- }
1514
- get volume() {
1515
- return this.amp.gain.value;
1516
- }
1517
- play({ offset = 0, fade = 0, loop = false } = {}) {
1518
- if (this.buffer !== undefined && this.played === null) {
1519
- this.source = context.createBufferSource();
1520
- this.source.buffer = this.buffer;
1521
- this.source.loop = loop;
1522
- this.source.connect(this.fade);
1523
- this.played = context.currentTime;
1524
- this.source.playbackRate.value = 1;
1525
- this.source.start(context.currentTime, offset / 1000);
1526
- // Apply fade-in effect if fade duration is specified
1527
- if (fade > 0) {
1528
- this.fade.gain.setValueAtTime(0, context.currentTime);
1529
- this.fade.gain.linearRampToValueAtTime(1.0, context.currentTime + fade / 1000);
1530
- }
1531
- this.source.onended = () => {
1532
- var _a;
1533
- this.played = null;
1534
- (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
1535
- this.source = null;
1536
- };
1537
- }
1538
- }
1539
- pause({ fade = 0 } = {}) {
1540
- var _a, _b;
1541
- if (this.buffer !== undefined && this.played !== null) {
1542
- const elapsed = (context.currentTime - this.played) % this.buffer.duration * 1000;
1543
- // Apply fade-out effect if fade duration is specified
1544
- if (fade > 0) {
1545
- this.fade.gain.setValueAtTime(1.0, context.currentTime);
1546
- this.fade.gain.linearRampToValueAtTime(0, context.currentTime + fade / 1000);
1547
- (_a = this.source) === null || _a === void 0 ? void 0 : _a.stop(context.currentTime + fade / 1000);
1548
- }
1549
- else {
1550
- (_b = this.source) === null || _b === void 0 ? void 0 : _b.stop(context.currentTime);
1551
- }
1552
- this.played = null;
1553
- return elapsed;
1554
- }
1555
- }
1556
- clear() {
1557
- var _a;
1558
- this.amp.disconnect();
1559
- this.fade.disconnect();
1560
- (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
1561
- }
1562
- }
1563
- const keymap = {
1564
- 'A0': 27.500, 'A#0': 29.135, 'B0': 30.868,
1565
- 'C1': 32.703, 'C#1': 34.648, 'D1': 36.708, 'D#1': 38.891, 'E1': 41.203, 'F1': 43.654, 'F#1': 46.249, 'G1': 48.999, 'G#1': 51.913, 'A1': 55.000, 'A#1': 58.270, 'B1': 61.735,
1566
- 'C2': 65.406, 'C#2': 69.296, 'D2': 73.416, 'D#2': 77.782, 'E2': 82.407, 'F2': 87.307, 'F#2': 92.499, 'G2': 97.999, 'G#2': 103.826, 'A2': 110.000, 'A#2': 116.541, 'B2': 123.471,
1567
- 'C3': 130.813, 'C#3': 138.591, 'D3': 146.832, 'D#3': 155.563, 'E3': 164.814, 'F3': 174.614, 'F#3': 184.997, 'G3': 195.998, 'G#3': 207.652, 'A3': 220.000, 'A#3': 233.082, 'B3': 246.942,
1568
- 'C4': 261.626, 'C#4': 277.183, 'D4': 293.665, 'D#4': 311.127, 'E4': 329.628, 'F4': 349.228, 'F#4': 369.994, 'G4': 391.995, 'G#4': 415.305, 'A4': 440.000, 'A#4': 466.164, 'B4': 493.883,
1569
- 'C5': 523.251, 'C#5': 554.365, 'D5': 587.330, 'D#5': 622.254, 'E5': 659.255, 'F5': 698.456, 'F#5': 739.989, 'G5': 783.991, 'G#5': 830.609, 'A5': 880.000, 'A#5': 932.328, 'B5': 987.767,
1570
- 'C6': 1046.502, 'C#6': 1108.731, 'D6': 1174.659, 'D#6': 1244.508, 'E6': 1318.510, 'F6': 1396.913, 'F#6': 1479.978, 'G6': 1567.982, 'G#6': 1661.219, 'A6': 1760.000, 'A#6': 1864.655, 'B6': 1975.533,
1571
- 'C7': 2093.005, 'C#7': 2217.461, 'D7': 2349.318, 'D#7': 2489.016, 'E7': 2637.020, 'F7': 2793.826, 'F#7': 2959.955, 'G7': 3135.963, 'G#7': 3322.438, 'A7': 3520.000, 'A#7': 3729.310, 'B7': 3951.066,
1572
- 'C8': 4186.009,
1573
- };
1574
- const notemap = {
1575
- '1m': 4.000, '2n': 2.000, '4n': 1.000, '8n': 0.500, '16n': 0.250, '32n': 0.125,
1576
- };
1577
- class Synthesizer {
1578
- constructor(props) { this.props = props; }
1579
- press(frequency, duration, wait) {
1580
- var _a;
1581
- const props = this.props;
1582
- const fv = typeof frequency === 'string' ? keymap[frequency] : frequency;
1583
- const dv = typeof duration === 'string' ? (notemap[duration] * 60 / ((_a = props.bpm) !== null && _a !== void 0 ? _a : 120)) : (typeof duration === 'number' ? (duration / 1000) : 0);
1584
- const start = context.currentTime + (wait !== null && wait !== void 0 ? wait : 0) / 1000;
1585
- const nodes = {};
1586
- nodes.oscillator = context.createOscillator();
1587
- nodes.oscillator.type = props.oscillator.type;
1588
- nodes.oscillator.frequency.value = fv;
1589
- if (props.oscillator.LFO) {
1590
- nodes.oscillatorLFO = context.createOscillator();
1591
- nodes.oscillatorLFODepth = context.createGain();
1592
- nodes.oscillatorLFODepth.gain.value = fv * (Math.pow(2.0, props.oscillator.LFO.amount / 12.0) - 1.0);
1593
- nodes.oscillatorLFO.type = props.oscillator.LFO.type;
1594
- nodes.oscillatorLFO.frequency.value = props.oscillator.LFO.rate;
1595
- nodes.oscillatorLFO.start(start);
1596
- nodes.oscillatorLFO.connect(nodes.oscillatorLFODepth);
1597
- nodes.oscillatorLFODepth.connect(nodes.oscillator.frequency);
1598
- }
1599
- nodes.amp = context.createGain();
1600
- nodes.amp.gain.value = 0.0;
1601
- nodes.target = context.createGain();
1602
- nodes.target.gain.value = 1.0;
1603
- nodes.amp.connect(nodes.target);
1604
- nodes.target.connect(master);
1605
- if (props.filter) {
1606
- nodes.filter = context.createBiquadFilter();
1607
- nodes.filter.type = props.filter.type;
1608
- nodes.filter.frequency.value = props.filter.cutoff;
1609
- nodes.oscillator.connect(nodes.filter);
1610
- nodes.filter.connect(nodes.amp);
1611
- }
1612
- else {
1613
- nodes.oscillator.connect(nodes.amp);
1614
- }
1615
- if (props.reverb) {
1616
- nodes.convolver = context.createConvolver();
1617
- nodes.convolver.buffer = impulseResponse({ time: props.reverb.time });
1618
- nodes.convolverDepth = context.createGain();
1619
- nodes.convolverDepth.gain.value = 1.0;
1620
- nodes.convolverDepth.gain.value *= props.reverb.mix;
1621
- nodes.target.gain.value *= (1.0 - props.reverb.mix);
1622
- nodes.amp.connect(nodes.convolver);
1623
- nodes.convolver.connect(nodes.convolverDepth);
1624
- nodes.convolverDepth.connect(master);
1625
- }
1626
- if (props.oscillator.envelope) {
1627
- const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
1628
- startEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
1629
- }
1630
- if (props.amp.envelope) {
1631
- startEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
1632
- }
1633
- nodes.oscillator.start(start);
1634
- if (dv > 0) {
1635
- release();
1636
- }
1637
- else {
1638
- return { release };
1639
- }
1640
- function release() {
1641
- let stop = null;
1642
- const end = dv > 0 ? dv : (context.currentTime - start);
1643
- if (props.amp.envelope) {
1644
- const ADSR = props.amp.envelope.ADSR;
1645
- const adsr = [ADSR[0] / 1000, ADSR[1] / 1000, ADSR[2], ADSR[3] / 1000];
1646
- const rate = adsr[0] === 0.0 ? 1.0 : Math.min(end / (adsr[0] + 0.001), 1.0);
1647
- stop = start + Math.max((adsr[0] + adsr[1]) * rate, end) + adsr[3];
1648
- }
1649
- else {
1650
- stop = start + end;
1651
- }
1652
- if (nodes.oscillatorLFO) {
1653
- nodes.oscillatorLFO.stop(stop);
1654
- }
1655
- if (props.oscillator.envelope) {
1656
- const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
1657
- stopEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
1658
- }
1659
- if (props.amp.envelope) {
1660
- stopEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
1661
- }
1662
- nodes.oscillator.stop(stop);
1663
- setTimeout(() => {
1664
- var _a, _b, _c, _d, _e;
1665
- nodes.oscillator.disconnect();
1666
- nodes.amp.disconnect();
1667
- nodes.target.disconnect();
1668
- (_a = nodes.oscillatorLFO) === null || _a === void 0 ? void 0 : _a.disconnect();
1669
- (_b = nodes.oscillatorLFODepth) === null || _b === void 0 ? void 0 : _b.disconnect();
1670
- (_c = nodes.filter) === null || _c === void 0 ? void 0 : _c.disconnect();
1671
- (_d = nodes.convolver) === null || _d === void 0 ? void 0 : _d.disconnect();
1672
- (_e = nodes.convolverDepth) === null || _e === void 0 ? void 0 : _e.disconnect();
1673
- }, 2000);
1674
- }
1675
- function stopEnvelope(param, base, amount, ADSR) {
1676
- const end = dv > 0 ? dv : (context.currentTime - start);
1677
- const rate = ADSR[0] === 0.0 ? 1.0 : Math.min(end / (ADSR[0] / 1000), 1.0);
1678
- if (rate < 1.0) {
1679
- param.cancelScheduledValues(start);
1680
- param.setValueAtTime(base, start);
1681
- param.linearRampToValueAtTime(base + amount * rate, start + ADSR[0] / 1000 * rate);
1682
- param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000 * rate);
1582
+ if (i <= index) {
1583
+ chars[i].element.style.opacity = '1';
1683
1584
  }
1684
- param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, dv));
1685
- param.linearRampToValueAtTime(base, start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, end) + ADSR[3] / 1000);
1686
1585
  }
1687
- function startEnvelope(param, base, amount, ADSR) {
1688
- param.value = base;
1689
- param.setValueAtTime(base, start);
1690
- param.linearRampToValueAtTime(base + amount, start + ADSR[0] / 1000);
1691
- param.linearRampToValueAtTime(base + amount * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000);
1586
+ if (state === 0 && index >= text.length) {
1587
+ action();
1692
1588
  }
1693
- function impulseResponse({ time, decay = 2.0 }) {
1694
- const length = context.sampleRate * time / 1000;
1695
- const impulse = context.createBuffer(2, length, context.sampleRate);
1696
- const ch0 = impulse.getChannelData(0);
1697
- const ch1 = impulse.getChannelData(1);
1698
- for (let i = 0; i < length; i++) {
1699
- ch0[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
1700
- ch1[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
1589
+ });
1590
+ xnew$1.timeout(() => {
1591
+ xnew$1(document.body).on('click wheel', action);
1592
+ unit.on('keydown', action);
1593
+ }, 100);
1594
+ function action() {
1595
+ if (state === 0) {
1596
+ state = 1;
1597
+ for (let i = 0; i < chars.length; i++) {
1598
+ chars[i].element.style.opacity = '1';
1701
1599
  }
1702
- return impulse;
1600
+ xnew$1.emit('-complete');
1601
+ }
1602
+ else if (state === 1) {
1603
+ state = 2;
1604
+ xnew$1.emit('-next');
1703
1605
  }
1704
1606
  }
1705
1607
  }
@@ -3346,35 +3248,228 @@ const icons = {
3346
3248
  XMark(unit, props) { icon('XMark', props); },
3347
3249
  };
3348
3250
 
3349
- function VolumeController(unit, {} = {}) {
3350
- xnew$1.nest(`<div style="position: relative; width: 100%; height: 100%; display: flex; align-items: center; justify-content: flex-end; pointer-events: none; container-type: size;">`);
3351
- xnew$1.extend(DirectEvent);
3352
- unit.on('-pointerdown', ({ event }) => event.stopPropagation());
3353
- const slider = xnew$1(`<input type="range" min="0" max="100" value="${master.gain.value * 100}"
3354
- style="display: none; width: calc(96cqw - 100cqh); margin: 0 2cqw; cursor: pointer; pointer-events: auto;"
3355
- >`);
3356
- unit.on('-click:outside', () => slider.element.style.display = 'none');
3357
- const button = xnew$1((button) => {
3358
- xnew$1.nest('<div style="position: relative; width: 100cqh; height: 100cqh; cursor: pointer; pointer-events: auto;">');
3359
- let icon = xnew$1(master.gain.value > 0 ? icons.SpeakerWave : icons.SpeakerXMark);
3360
- return {
3361
- update() {
3362
- icon === null || icon === void 0 ? void 0 : icon.finalize();
3363
- icon = xnew$1(master.gain.value > 0 ? icons.SpeakerWave : icons.SpeakerXMark);
3251
+ const context = window.AudioContext ? new window.AudioContext() : (null);
3252
+ const master = context ? context.createGain() : (null);
3253
+ if (context) {
3254
+ master.gain.value = 0.1;
3255
+ master.connect(context.destination);
3256
+ }
3257
+ class AudioFile {
3258
+ constructor(path) {
3259
+ this.promise = fetch(path)
3260
+ .then((response) => response.arrayBuffer())
3261
+ .then((response) => context.decodeAudioData(response))
3262
+ .then((response) => { this.buffer = response; })
3263
+ .catch(() => {
3264
+ console.warn(`"${path}" could not be loaded.`);
3265
+ });
3266
+ this.amp = context.createGain();
3267
+ this.amp.gain.value = 1.0;
3268
+ this.amp.connect(master);
3269
+ this.fade = context.createGain();
3270
+ this.fade.gain.value = 1.0;
3271
+ this.fade.connect(this.amp);
3272
+ this.source = null;
3273
+ this.played = null;
3274
+ }
3275
+ set volume(value) {
3276
+ this.amp.gain.value = value;
3277
+ }
3278
+ get volume() {
3279
+ return this.amp.gain.value;
3280
+ }
3281
+ play({ offset = 0, fade = 0, loop = false } = {}) {
3282
+ if (this.buffer !== undefined && this.played === null) {
3283
+ this.source = context.createBufferSource();
3284
+ this.source.buffer = this.buffer;
3285
+ this.source.loop = loop;
3286
+ this.source.connect(this.fade);
3287
+ this.played = context.currentTime;
3288
+ this.source.playbackRate.value = 1;
3289
+ this.source.start(context.currentTime, offset / 1000);
3290
+ // Apply fade-in effect if fade duration is specified
3291
+ if (fade > 0) {
3292
+ this.fade.gain.setValueAtTime(0, context.currentTime);
3293
+ this.fade.gain.linearRampToValueAtTime(1.0, context.currentTime + fade / 1000);
3364
3294
  }
3365
- };
3366
- });
3367
- button.on('click', () => slider.element.style.display = slider.element.style.display !== 'none' ? 'none' : 'flex');
3368
- slider.on('input', (event) => {
3369
- master.gain.value = parseFloat(event.target.value) / 100;
3370
- button.update();
3371
- });
3295
+ this.source.onended = () => {
3296
+ var _a;
3297
+ this.played = null;
3298
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
3299
+ this.source = null;
3300
+ };
3301
+ }
3302
+ }
3303
+ pause({ fade = 0 } = {}) {
3304
+ var _a, _b;
3305
+ if (this.buffer !== undefined && this.played !== null) {
3306
+ const elapsed = (context.currentTime - this.played) % this.buffer.duration * 1000;
3307
+ // Apply fade-out effect if fade duration is specified
3308
+ if (fade > 0) {
3309
+ this.fade.gain.setValueAtTime(1.0, context.currentTime);
3310
+ this.fade.gain.linearRampToValueAtTime(0, context.currentTime + fade / 1000);
3311
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.stop(context.currentTime + fade / 1000);
3312
+ }
3313
+ else {
3314
+ (_b = this.source) === null || _b === void 0 ? void 0 : _b.stop(context.currentTime);
3315
+ }
3316
+ this.played = null;
3317
+ return elapsed;
3318
+ }
3319
+ }
3320
+ clear() {
3321
+ var _a;
3322
+ this.amp.disconnect();
3323
+ this.fade.disconnect();
3324
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
3325
+ }
3326
+ }
3327
+ const keymap = {
3328
+ 'A0': 27.500, 'A#0': 29.135, 'B0': 30.868,
3329
+ 'C1': 32.703, 'C#1': 34.648, 'D1': 36.708, 'D#1': 38.891, 'E1': 41.203, 'F1': 43.654, 'F#1': 46.249, 'G1': 48.999, 'G#1': 51.913, 'A1': 55.000, 'A#1': 58.270, 'B1': 61.735,
3330
+ 'C2': 65.406, 'C#2': 69.296, 'D2': 73.416, 'D#2': 77.782, 'E2': 82.407, 'F2': 87.307, 'F#2': 92.499, 'G2': 97.999, 'G#2': 103.826, 'A2': 110.000, 'A#2': 116.541, 'B2': 123.471,
3331
+ 'C3': 130.813, 'C#3': 138.591, 'D3': 146.832, 'D#3': 155.563, 'E3': 164.814, 'F3': 174.614, 'F#3': 184.997, 'G3': 195.998, 'G#3': 207.652, 'A3': 220.000, 'A#3': 233.082, 'B3': 246.942,
3332
+ 'C4': 261.626, 'C#4': 277.183, 'D4': 293.665, 'D#4': 311.127, 'E4': 329.628, 'F4': 349.228, 'F#4': 369.994, 'G4': 391.995, 'G#4': 415.305, 'A4': 440.000, 'A#4': 466.164, 'B4': 493.883,
3333
+ 'C5': 523.251, 'C#5': 554.365, 'D5': 587.330, 'D#5': 622.254, 'E5': 659.255, 'F5': 698.456, 'F#5': 739.989, 'G5': 783.991, 'G#5': 830.609, 'A5': 880.000, 'A#5': 932.328, 'B5': 987.767,
3334
+ 'C6': 1046.502, 'C#6': 1108.731, 'D6': 1174.659, 'D#6': 1244.508, 'E6': 1318.510, 'F6': 1396.913, 'F#6': 1479.978, 'G6': 1567.982, 'G#6': 1661.219, 'A6': 1760.000, 'A#6': 1864.655, 'B6': 1975.533,
3335
+ 'C7': 2093.005, 'C#7': 2217.461, 'D7': 2349.318, 'D#7': 2489.016, 'E7': 2637.020, 'F7': 2793.826, 'F#7': 2959.955, 'G7': 3135.963, 'G#7': 3322.438, 'A7': 3520.000, 'A#7': 3729.310, 'B7': 3951.066,
3336
+ 'C8': 4186.009,
3337
+ };
3338
+ const notemap = {
3339
+ '1m': 4.000, '2n': 2.000, '4n': 1.000, '8n': 0.500, '16n': 0.250, '32n': 0.125,
3340
+ };
3341
+ class Synthesizer {
3342
+ constructor(props) { this.props = props; }
3343
+ press(frequency, duration, wait) {
3344
+ var _a;
3345
+ const props = this.props;
3346
+ const fv = typeof frequency === 'string' ? keymap[frequency] : frequency;
3347
+ const dv = typeof duration === 'string' ? (notemap[duration] * 60 / ((_a = props.bpm) !== null && _a !== void 0 ? _a : 120)) : (typeof duration === 'number' ? (duration / 1000) : 0);
3348
+ const start = context.currentTime + (wait !== null && wait !== void 0 ? wait : 0) / 1000;
3349
+ const nodes = {};
3350
+ nodes.oscillator = context.createOscillator();
3351
+ nodes.oscillator.type = props.oscillator.type;
3352
+ nodes.oscillator.frequency.value = fv;
3353
+ if (props.oscillator.LFO) {
3354
+ nodes.oscillatorLFO = context.createOscillator();
3355
+ nodes.oscillatorLFODepth = context.createGain();
3356
+ nodes.oscillatorLFODepth.gain.value = fv * (Math.pow(2.0, props.oscillator.LFO.amount / 12.0) - 1.0);
3357
+ nodes.oscillatorLFO.type = props.oscillator.LFO.type;
3358
+ nodes.oscillatorLFO.frequency.value = props.oscillator.LFO.rate;
3359
+ nodes.oscillatorLFO.start(start);
3360
+ nodes.oscillatorLFO.connect(nodes.oscillatorLFODepth);
3361
+ nodes.oscillatorLFODepth.connect(nodes.oscillator.frequency);
3362
+ }
3363
+ nodes.amp = context.createGain();
3364
+ nodes.amp.gain.value = 0.0;
3365
+ nodes.target = context.createGain();
3366
+ nodes.target.gain.value = 1.0;
3367
+ nodes.amp.connect(nodes.target);
3368
+ nodes.target.connect(master);
3369
+ if (props.filter) {
3370
+ nodes.filter = context.createBiquadFilter();
3371
+ nodes.filter.type = props.filter.type;
3372
+ nodes.filter.frequency.value = props.filter.cutoff;
3373
+ nodes.oscillator.connect(nodes.filter);
3374
+ nodes.filter.connect(nodes.amp);
3375
+ }
3376
+ else {
3377
+ nodes.oscillator.connect(nodes.amp);
3378
+ }
3379
+ if (props.reverb) {
3380
+ nodes.convolver = context.createConvolver();
3381
+ nodes.convolver.buffer = impulseResponse({ time: props.reverb.time });
3382
+ nodes.convolverDepth = context.createGain();
3383
+ nodes.convolverDepth.gain.value = 1.0;
3384
+ nodes.convolverDepth.gain.value *= props.reverb.mix;
3385
+ nodes.target.gain.value *= (1.0 - props.reverb.mix);
3386
+ nodes.amp.connect(nodes.convolver);
3387
+ nodes.convolver.connect(nodes.convolverDepth);
3388
+ nodes.convolverDepth.connect(master);
3389
+ }
3390
+ if (props.oscillator.envelope) {
3391
+ const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
3392
+ startEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
3393
+ }
3394
+ if (props.amp.envelope) {
3395
+ startEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
3396
+ }
3397
+ nodes.oscillator.start(start);
3398
+ if (dv > 0) {
3399
+ release();
3400
+ }
3401
+ else {
3402
+ return { release };
3403
+ }
3404
+ function release() {
3405
+ let stop = null;
3406
+ const end = dv > 0 ? dv : (context.currentTime - start);
3407
+ if (props.amp.envelope) {
3408
+ const ADSR = props.amp.envelope.ADSR;
3409
+ const adsr = [ADSR[0] / 1000, ADSR[1] / 1000, ADSR[2], ADSR[3] / 1000];
3410
+ const rate = adsr[0] === 0.0 ? 1.0 : Math.min(end / (adsr[0] + 0.001), 1.0);
3411
+ stop = start + Math.max((adsr[0] + adsr[1]) * rate, end) + adsr[3];
3412
+ }
3413
+ else {
3414
+ stop = start + end;
3415
+ }
3416
+ if (nodes.oscillatorLFO) {
3417
+ nodes.oscillatorLFO.stop(stop);
3418
+ }
3419
+ if (props.oscillator.envelope) {
3420
+ const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
3421
+ stopEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
3422
+ }
3423
+ if (props.amp.envelope) {
3424
+ stopEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
3425
+ }
3426
+ nodes.oscillator.stop(stop);
3427
+ setTimeout(() => {
3428
+ var _a, _b, _c, _d, _e;
3429
+ nodes.oscillator.disconnect();
3430
+ nodes.amp.disconnect();
3431
+ nodes.target.disconnect();
3432
+ (_a = nodes.oscillatorLFO) === null || _a === void 0 ? void 0 : _a.disconnect();
3433
+ (_b = nodes.oscillatorLFODepth) === null || _b === void 0 ? void 0 : _b.disconnect();
3434
+ (_c = nodes.filter) === null || _c === void 0 ? void 0 : _c.disconnect();
3435
+ (_d = nodes.convolver) === null || _d === void 0 ? void 0 : _d.disconnect();
3436
+ (_e = nodes.convolverDepth) === null || _e === void 0 ? void 0 : _e.disconnect();
3437
+ }, 2000);
3438
+ }
3439
+ function stopEnvelope(param, base, amount, ADSR) {
3440
+ const end = dv > 0 ? dv : (context.currentTime - start);
3441
+ const rate = ADSR[0] === 0.0 ? 1.0 : Math.min(end / (ADSR[0] / 1000), 1.0);
3442
+ if (rate < 1.0) {
3443
+ param.cancelScheduledValues(start);
3444
+ param.setValueAtTime(base, start);
3445
+ param.linearRampToValueAtTime(base + amount * rate, start + ADSR[0] / 1000 * rate);
3446
+ param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000 * rate);
3447
+ }
3448
+ param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, dv));
3449
+ param.linearRampToValueAtTime(base, start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, end) + ADSR[3] / 1000);
3450
+ }
3451
+ function startEnvelope(param, base, amount, ADSR) {
3452
+ param.value = base;
3453
+ param.setValueAtTime(base, start);
3454
+ param.linearRampToValueAtTime(base + amount, start + ADSR[0] / 1000);
3455
+ param.linearRampToValueAtTime(base + amount * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000);
3456
+ }
3457
+ function impulseResponse({ time, decay = 2.0 }) {
3458
+ const length = context.sampleRate * time / 1000;
3459
+ const impulse = context.createBuffer(2, length, context.sampleRate);
3460
+ const ch0 = impulse.getChannelData(0);
3461
+ const ch1 = impulse.getChannelData(1);
3462
+ for (let i = 0; i < length; i++) {
3463
+ ch0[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
3464
+ ch1[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
3465
+ }
3466
+ return impulse;
3467
+ }
3468
+ }
3372
3469
  }
3373
3470
 
3374
3471
  const basics = {
3375
3472
  Screen,
3376
- DirectEvent,
3377
- ResizeEvent,
3378
3473
  ModalFrame,
3379
3474
  ModalContent,
3380
3475
  AccordionFrame,
@@ -3389,7 +3484,6 @@ const basics = {
3389
3484
  DragTarget,
3390
3485
  AnalogStick,
3391
3486
  DirectionalPad,
3392
- VolumeController
3393
3487
  };
3394
3488
  const audio = {
3395
3489
  load(path) {