@mulsense/xnew 0.1.6 → 0.1.8

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
@@ -1,53 +1,35 @@
1
- function ResizeEvent(resize) {
2
- const observer = new ResizeObserver((entries) => {
3
- for (const entry of entries) {
4
- resize.emit('-resize');
5
- break;
6
- }
7
- });
8
- if (resize.element) {
9
- observer.observe(resize.element);
10
- }
11
- resize.on('finalize', () => {
12
- if (resize.element) {
13
- observer.unobserve(resize.element);
14
- }
15
- });
16
- }
17
-
18
1
  //----------------------------------------------------------------------------------------------------
19
2
  // ticker
20
3
  //----------------------------------------------------------------------------------------------------
21
4
  class Ticker {
22
- static ticker() {
23
- const time = Date.now();
24
- const interval = 1000 / 60;
25
- if (time - Ticker.previous > interval * 0.8) {
26
- Ticker.callbacks.forEach((callback) => callback(time));
27
- Ticker.previous = time;
5
+ constructor(callback) {
6
+ const self = this;
7
+ this.id = null;
8
+ let previous = 0;
9
+ ticker();
10
+ function ticker() {
11
+ const time = Date.now();
12
+ const interval = 1000 / 60;
13
+ if (time - previous > interval * 0.9) {
14
+ callback(time);
15
+ previous = time;
16
+ }
17
+ self.id = requestAnimationFrame(ticker);
28
18
  }
29
- Ticker.animation = requestAnimationFrame(Ticker.ticker);
30
19
  }
31
- static set(callback) {
32
- if (Ticker.animation === null && typeof requestAnimationFrame === 'function' && typeof cancelAnimationFrame === 'function') {
33
- Ticker.previous = Date.now();
34
- Ticker.animation = requestAnimationFrame(Ticker.ticker);
20
+ clear() {
21
+ if (this.id !== null) {
22
+ cancelAnimationFrame(this.id);
23
+ this.id = null;
35
24
  }
36
- Ticker.callbacks.add(callback);
37
- }
38
- static clear(callback) {
39
- Ticker.callbacks.delete(callback);
40
25
  }
41
26
  }
42
- Ticker.animation = null;
43
- Ticker.callbacks = new Set;
44
- Ticker.previous = 0.0;
45
27
  //----------------------------------------------------------------------------------------------------
46
28
  // timer
47
29
  //----------------------------------------------------------------------------------------------------
48
30
  class Timer {
49
31
  constructor(timeout, transition, delay, loop = false) {
50
- var _a, _b;
32
+ var _a;
51
33
  this.timeout = timeout;
52
34
  this.transition = transition;
53
35
  this.delay = delay;
@@ -56,33 +38,21 @@ class Timer {
56
38
  this.time = 0.0;
57
39
  this.offset = 0.0;
58
40
  this.status = 0;
59
- this.ticker = (time) => {
60
- var _a;
61
- (_a = this.transition) === null || _a === void 0 ? void 0 : _a.call(this, this.elapsed() / this.delay);
62
- };
63
- (_a = this.transition) === null || _a === void 0 ? void 0 : _a.call(this, 0.0);
64
- if (this.delay <= 0) {
65
- timeout();
66
- (_b = this.transition) === null || _b === void 0 ? void 0 : _b.call(this, 1.0);
67
- }
68
- else {
69
- if (document instanceof Document) {
70
- this.visibilitychange = () => document.hidden === false ? this._start() : this._stop();
71
- document.addEventListener('visibilitychange', this.visibilitychange);
72
- }
73
- this.start();
74
- Ticker.set(this.ticker);
41
+ this.ticker = new Ticker((time) => { var _a; return (_a = this.transition) === null || _a === void 0 ? void 0 : _a.call(this, this.elapsed() / this.delay); });
42
+ this.visibilitychange = () => document.hidden === false ? this._start() : this._stop();
43
+ document.addEventListener('visibilitychange', this.visibilitychange);
44
+ if (this.delay > 0.0) {
45
+ (_a = this.transition) === null || _a === void 0 ? void 0 : _a.call(this, 0.0);
75
46
  }
47
+ this.start();
76
48
  }
77
49
  clear() {
78
50
  if (this.id !== null) {
79
51
  clearTimeout(this.id);
80
52
  this.id = null;
81
53
  }
82
- if (document instanceof Document && this.visibilitychange !== undefined) {
83
- document.removeEventListener('visibilitychange', this.visibilitychange);
84
- }
85
- Ticker.clear(this.ticker);
54
+ document.removeEventListener('visibilitychange', this.visibilitychange);
55
+ this.ticker.clear();
86
56
  }
87
57
  elapsed() {
88
58
  return this.offset + (this.id !== null ? (Date.now() - this.time) : 0);
@@ -98,13 +68,18 @@ class Timer {
98
68
  _start() {
99
69
  if (this.status === 1 && this.id === null) {
100
70
  this.id = setTimeout(() => {
71
+ var _a;
101
72
  this.timeout();
73
+ (_a = this.transition) === null || _a === void 0 ? void 0 : _a.call(this, 1.0);
102
74
  this.id = null;
103
75
  this.time = 0.0;
104
76
  this.offset = 0.0;
105
77
  if (this.loop) {
106
78
  this.start();
107
79
  }
80
+ else {
81
+ this.clear();
82
+ }
108
83
  }, this.delay - this.offset);
109
84
  this.time = Date.now();
110
85
  }
@@ -217,13 +192,11 @@ class MapMap extends Map {
217
192
  }
218
193
 
219
194
  //----------------------------------------------------------------------------------------------------
220
- // Utils
195
+ // utils
221
196
  //----------------------------------------------------------------------------------------------------
222
197
  const SYSTEM_EVENTS = ['start', 'update', 'stop', 'finalize'];
223
198
  class UnitPromise {
224
- constructor(promise) {
225
- this.promise = promise;
226
- }
199
+ constructor(promise) { this.promise = promise; }
227
200
  then(callback) {
228
201
  this.promise = this.promise.then(Unit.wrap(Unit.current, callback));
229
202
  return this;
@@ -238,7 +211,7 @@ class UnitPromise {
238
211
  }
239
212
  }
240
213
  //----------------------------------------------------------------------------------------------------
241
- // Unit
214
+ // unit
242
215
  //----------------------------------------------------------------------------------------------------
243
216
  class Unit {
244
217
  constructor(parent, target, component, props) {
@@ -258,10 +231,10 @@ class Unit {
258
231
  baseComponent = component;
259
232
  }
260
233
  else if (typeof component === 'string') {
261
- baseComponent = (self) => { self.element.textContent = component; };
234
+ baseComponent = (unit) => { unit.element.textContent = component; };
262
235
  }
263
236
  else {
264
- baseComponent = (self) => { };
237
+ baseComponent = (unit) => { };
265
238
  }
266
239
  const baseContext = (_a = parent === null || parent === void 0 ? void 0 : parent._.currentContext) !== null && _a !== void 0 ? _a : { stack: null };
267
240
  this._ = { parent, target, baseElement, baseContext, baseComponent, props };
@@ -290,10 +263,10 @@ class Unit {
290
263
  }
291
264
  reboot() {
292
265
  var _a, _b;
266
+ const anchor = (_b = (_a = this._.elements[0]) === null || _a === void 0 ? void 0 : _a.nextElementSibling) !== null && _b !== void 0 ? _b : null;
293
267
  Unit.stop(this);
294
- const anchorElement = (_b = (_a = this._.elements[0]) === null || _a === void 0 ? void 0 : _a.nextElementSibling) !== null && _b !== void 0 ? _b : null;
295
268
  Unit.finalize(this);
296
- Unit.initialize(this, anchorElement);
269
+ Unit.initialize(this, anchor);
297
270
  }
298
271
  static initialize(unit, anchor) {
299
272
  const backup = Unit.current;
@@ -323,27 +296,20 @@ class Unit {
323
296
  // whether the unit promise was resolved
324
297
  Promise.all(unit._.promises).then(() => unit._.state = 'initialized');
325
298
  // setup capture
326
- let captured = false;
327
- for (let current = unit; current !== null && captured === false; current = current._.parent) {
328
- for (const capture of current._.captures) {
329
- if (capture.checker(unit)) {
330
- capture.execute(unit);
331
- captured = true;
332
- }
333
- }
299
+ for (let current = unit; current !== null; current = current._.parent) {
300
+ if (current._.captures.find((capture) => capture(unit)) !== undefined)
301
+ break;
334
302
  }
335
303
  Unit.current = backup;
336
304
  }
337
305
  static finalize(unit) {
338
- if (unit._.state !== 'finalized' && unit._.state !== 'pre-finalized') {
339
- unit._.state = 'pre-finalized';
306
+ if (unit._.state !== 'finalized') {
307
+ unit._.state = 'finalized';
340
308
  unit._.children.forEach((child) => child.finalize());
341
309
  unit._.systems.finalize.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
342
310
  unit.off();
343
311
  Unit.suboff(unit, null);
344
- unit._.components.forEach((component) => {
345
- Unit.componentUnits.delete(component, unit);
346
- });
312
+ unit._.components.forEach((component) => Unit.componentUnits.delete(component, unit));
347
313
  if (unit._.elements.length > 0) {
348
314
  unit._.baseElement.removeChild(unit._.elements[0]);
349
315
  unit._.currentElement = unit._.baseElement;
@@ -355,7 +321,6 @@ class Unit {
355
321
  }
356
322
  });
357
323
  unit._.defines = {};
358
- unit._.state = 'finalized';
359
324
  }
360
325
  }
361
326
  static nest(unit, tag) {
@@ -389,34 +354,35 @@ class Unit {
389
354
  throw new Error(`The property "${key}" already exists.`);
390
355
  }
391
356
  const descriptor = Object.getOwnPropertyDescriptor(defines, key);
392
- const wrappedDesc = { configurable: true, enumerable: true };
357
+ const wrapper = { configurable: true, enumerable: true };
393
358
  if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.get) {
394
- wrappedDesc.get = Unit.wrap(unit, descriptor.get);
359
+ wrapper.get = Unit.wrap(unit, descriptor.get);
395
360
  }
396
361
  if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set) {
397
- wrappedDesc.set = Unit.wrap(unit, descriptor.set);
362
+ wrapper.set = Unit.wrap(unit, descriptor.set);
398
363
  }
399
364
  if (typeof (descriptor === null || descriptor === void 0 ? void 0 : descriptor.value) === 'function') {
400
- wrappedDesc.value = Unit.wrap(unit, descriptor.value);
365
+ wrapper.value = Unit.wrap(unit, descriptor.value);
401
366
  }
402
367
  else if ((descriptor === null || descriptor === void 0 ? void 0 : descriptor.value) !== undefined) {
403
- wrappedDesc.writable = true;
404
- wrappedDesc.value = descriptor.value;
368
+ wrapper.writable = true;
369
+ wrapper.value = descriptor.value;
405
370
  }
406
- Object.defineProperty(unit._.defines, key, wrappedDesc);
407
- Object.defineProperty(unit, key, wrappedDesc);
371
+ Object.defineProperty(unit._.defines, key, wrapper);
372
+ Object.defineProperty(unit, key, wrapper);
408
373
  });
374
+ return Object.assign({}, unit._.defines);
409
375
  }
410
- static start(unit, time) {
376
+ static start(unit) {
411
377
  if (unit._.tostart === false)
412
378
  return;
413
379
  if (unit._.state === 'initialized' || unit._.state === 'stopped') {
414
380
  unit._.state = 'started';
415
- unit._.children.forEach((child) => Unit.start(child, time));
381
+ unit._.children.forEach((child) => Unit.start(child));
416
382
  unit._.systems.start.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
417
383
  }
418
384
  else if (unit._.state === 'started') {
419
- unit._.children.forEach((child) => Unit.start(child, time));
385
+ unit._.children.forEach((child) => Unit.start(child));
420
386
  }
421
387
  }
422
388
  static stop(unit) {
@@ -426,29 +392,22 @@ class Unit {
426
392
  unit._.systems.stop.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
427
393
  }
428
394
  }
429
- static update(unit, time) {
395
+ static update(unit) {
430
396
  if (unit._.state === 'started') {
431
- unit._.children.forEach((child) => Unit.update(child, time));
397
+ unit._.children.forEach((child) => Unit.update(child));
432
398
  unit._.systems.update.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
433
399
  }
434
400
  }
435
- static ticker(time) {
436
- if (Unit.root !== null) {
437
- Unit.start(Unit.root, time);
438
- Unit.update(Unit.root, time);
439
- }
440
- }
441
401
  static reset() {
442
- var _a;
402
+ var _a, _b;
443
403
  (_a = Unit.root) === null || _a === void 0 ? void 0 : _a.finalize();
444
- Unit.root = new Unit(null, null);
445
- Unit.current = Unit.root;
446
- Ticker.clear(Unit.ticker);
447
- Ticker.set(Unit.ticker);
448
- }
449
- //----------------------------------------------------------------------------------------------------
450
- // scope
451
- //----------------------------------------------------------------------------------------------------
404
+ Unit.current = Unit.root = new Unit(null, null);
405
+ (_b = Unit.ticker) === null || _b === void 0 ? void 0 : _b.clear();
406
+ Unit.ticker = new Ticker((time) => {
407
+ Unit.start(Unit.root);
408
+ Unit.update(Unit.root);
409
+ });
410
+ }
452
411
  static wrap(unit, listener) {
453
412
  const snapshot = Unit.snapshot(unit);
454
413
  return (...args) => Unit.scope(snapshot, listener, ...args);
@@ -474,13 +433,14 @@ class Unit {
474
433
  static snapshot(unit) {
475
434
  return { unit, context: unit._.currentContext, element: unit._.currentElement };
476
435
  }
477
- static stack(unit, key, value) {
478
- unit._.currentContext = { stack: unit._.currentContext, key, value };
479
- }
480
- static trace(unit, key) {
481
- for (let context = unit._.currentContext; context !== null; context = context.stack) {
482
- if (context.key === key) {
483
- return context.value;
436
+ static context(unit, key, value) {
437
+ if (value !== undefined) {
438
+ unit._.currentContext = { stack: unit._.currentContext, key, value };
439
+ }
440
+ else {
441
+ for (let context = unit._.currentContext; context.stack !== null; context = context.stack) {
442
+ if (context.key === key)
443
+ return context.value;
484
444
  }
485
445
  }
486
446
  }
@@ -565,15 +525,14 @@ class Unit {
565
525
  });
566
526
  }
567
527
  }
568
- Unit.root = null;
569
528
  Unit.componentUnits = new MapSet();
570
529
  //----------------------------------------------------------------------------------------------------
571
530
  // event
572
531
  //----------------------------------------------------------------------------------------------------
573
532
  Unit.typeUnits = new MapSet();
574
533
 
575
- const xnew$1 = function (...args) {
576
- if (Unit.root === null) {
534
+ const xnew$1 = Object.assign(function (...args) {
535
+ if (Unit.root === undefined) {
577
536
  Unit.reset();
578
537
  }
579
538
  let target;
@@ -596,257 +555,403 @@ const xnew$1 = function (...args) {
596
555
  target = null;
597
556
  }
598
557
  return new Unit(Unit.current, target, ...args);
599
- };
600
- xnew$1.nest = (tag) => {
601
- var _a;
602
- if (((_a = Unit.current) === null || _a === void 0 ? void 0 : _a._.state) === 'invoked') {
603
- return Unit.nest(Unit.current, tag);
604
- }
605
- else {
606
- throw new Error('This function can not be called after initialized.');
607
- }
608
- };
609
- xnew$1.extend = (component, props) => {
610
- var _a;
611
- if (((_a = Unit.current) === null || _a === void 0 ? void 0 : _a._.state) === 'invoked') {
612
- return Unit.extend(Unit.current, component, props);
613
- }
614
- else {
615
- throw new Error('This function can not be called after initialized.');
616
- }
617
- };
618
- xnew$1.context = (key, value = undefined) => {
619
- try {
620
- if (value !== undefined) {
621
- Unit.stack(Unit.current, key, value);
558
+ }, {
559
+ /**
560
+ * Creates a nested HTML/SVG element within the current component
561
+ * @param tag - HTML or SVG tag name (e.g., '<div>', '<span>', '<svg>')
562
+ * @returns The created HTML/SVG element
563
+ * @throws Error if called after component initialization
564
+ * @example
565
+ * const div = xnew.nest('<div>')
566
+ * div.textContent = 'Hello'
567
+ */
568
+ nest(tag) {
569
+ var _a;
570
+ if (((_a = Unit.current) === null || _a === void 0 ? void 0 : _a._.state) === 'invoked') {
571
+ return Unit.nest(Unit.current, tag);
622
572
  }
623
573
  else {
624
- return Unit.trace(Unit.current, key);
574
+ throw new Error('xnew.nest: This function can not be called after initialized.');
575
+ }
576
+ },
577
+ /**
578
+ * Extends the current component with another component's functionality
579
+ * @param component - Component function to extend with
580
+ * @param props - Optional properties to pass to the extended component
581
+ * @returns The extended component's return value
582
+ * @throws Error if called after component initialization
583
+ * @example
584
+ * const api = xnew.extend(BaseComponent, { data: {} })
585
+ */
586
+ extend(component, props) {
587
+ var _a;
588
+ if (((_a = Unit.current) === null || _a === void 0 ? void 0 : _a._.state) === 'invoked') {
589
+ return Unit.extend(Unit.current, component, props);
625
590
  }
626
- }
627
- catch (error) {
628
- console.error('xnew.context(key, value?): ', error);
629
- }
630
- };
631
- xnew$1.promise = (promise) => {
632
- try {
633
- Unit.current._.promises.push(promise);
634
- return new UnitPromise(promise);
635
- }
636
- catch (error) {
637
- console.error('xnew.promise(mix): ', error);
638
- throw error;
639
- }
640
- };
641
- xnew$1.then = (callback) => {
642
- try {
643
- return new UnitPromise(Promise.all(Unit.current._.promises)).then(callback);
644
- }
645
- catch (error) {
646
- console.error('xnew.then(mix): ', error);
647
- throw error;
648
- }
649
- };
650
- xnew$1.catch = (callback) => {
651
- try {
652
- return new UnitPromise(Promise.all(Unit.current._.promises)).catch(callback);
653
- }
654
- catch (error) {
655
- console.error('xnew.catch(mix): ', error);
656
- throw error;
657
- }
658
- };
659
- xnew$1.finally = (callback) => {
660
- try {
661
- return new UnitPromise(Promise.all(Unit.current._.promises)).finally(callback);
662
- }
663
- catch (error) {
664
- console.error('xnew.finally(mix): ', error);
665
- throw error;
666
- }
667
- };
668
- xnew$1.fetch = (url, options) => {
669
- try {
670
- const promise = fetch(url, options);
671
- Unit.current._.promises.push(promise);
672
- return new UnitPromise(promise);
673
- }
674
- catch (error) {
675
- console.error('xnew.promise(mix): ', error);
676
- throw error;
677
- }
678
- };
679
- xnew$1.scope = (callback) => {
680
- const snapshot = Unit.snapshot(Unit.current);
681
- return (...args) => Unit.scope(snapshot, callback, ...args);
682
- };
683
- xnew$1.find = (component) => {
684
- if (typeof component === 'function') {
685
- return Unit.find(component);
686
- }
687
- else {
688
- throw new Error(`The argument [component] is invalid.`);
689
- }
690
- };
691
- xnew$1.append = (base, ...args) => {
692
- if (typeof base === 'function') {
693
- for (let unit of Unit.find(base)) {
694
- Unit.scope(Unit.snapshot(unit), xnew$1, ...args);
591
+ else {
592
+ throw new Error('xnew.extend: This function can not be called after initialized.');
593
+ }
594
+ },
595
+ /**
596
+ * Gets or sets a context value that can be accessed by child components
597
+ * @param key - Context key
598
+ * @param value - Optional value to set (if undefined, gets the value)
599
+ * @returns The context value if getting, undefined if setting
600
+ * @example
601
+ * // Set context in parent
602
+ * xnew.context('theme', 'dark')
603
+ *
604
+ * // Get context in child
605
+ * const theme = xnew.context('theme')
606
+ */
607
+ context(key, value = undefined) {
608
+ try {
609
+ return Unit.context(Unit.current, key, value);
695
610
  }
696
- }
697
- else if (base instanceof Unit) {
698
- Unit.scope(Unit.snapshot(base), xnew$1, ...args);
699
- }
700
- else {
701
- throw new Error(`The argument [component] is invalid.`);
702
- }
703
- };
704
- xnew$1.timeout = (callback, delay) => {
705
- const snapshot = Unit.snapshot(Unit.current);
706
- const unit = xnew$1((self) => {
707
- const timer = new Timer(() => {
708
- Unit.scope(snapshot, callback);
709
- self.finalize();
710
- }, null, delay);
711
- self.on('finalize', () => {
712
- timer.clear();
611
+ catch (error) {
612
+ console.error('xnew.context(key: string, value?: any): ', error);
613
+ throw error;
614
+ }
615
+ },
616
+ /**
617
+ * Registers a promise with the current component for lifecycle management
618
+ * @param promise - Promise to register
619
+ * @returns UnitPromise wrapper for chaining
620
+ * @example
621
+ * xnew.promise(fetchData()).then(data => console.log(data))
622
+ */
623
+ promise(promise) {
624
+ try {
625
+ Unit.current._.promises.push(promise);
626
+ return new UnitPromise(promise);
627
+ }
628
+ catch (error) {
629
+ console.error('xnew.promise(promise: Promise<any>): ', error);
630
+ throw error;
631
+ }
632
+ },
633
+ /**
634
+ * Handles successful resolution of all registered promises in the current component
635
+ * @param callback - Function to call when all promises resolve
636
+ * @returns UnitPromise for chaining
637
+ * @example
638
+ * xnew.then(results => console.log('All promises resolved', results))
639
+ */
640
+ then(callback) {
641
+ try {
642
+ return new UnitPromise(Promise.all(Unit.current._.promises)).then(callback);
643
+ }
644
+ catch (error) {
645
+ console.error('xnew.then(callback: Function): ', error);
646
+ throw error;
647
+ }
648
+ },
649
+ /**
650
+ * Handles rejection of any registered promise in the current component
651
+ * @param callback - Function to call if any promise rejects
652
+ * @returns UnitPromise for chaining
653
+ * @example
654
+ * xnew.catch(error => console.error('Promise failed', error))
655
+ */
656
+ catch(callback) {
657
+ try {
658
+ return new UnitPromise(Promise.all(Unit.current._.promises)).catch(callback);
659
+ }
660
+ catch (error) {
661
+ console.error('xnew.catch(callback: Function): ', error);
662
+ throw error;
663
+ }
664
+ },
665
+ /**
666
+ * Executes callback after all registered promises settle (resolve or reject)
667
+ * @param callback - Function to call after promises settle
668
+ * @returns UnitPromise for chaining
669
+ * @example
670
+ * xnew.finally(() => console.log('All promises settled'))
671
+ */
672
+ finally(callback) {
673
+ try {
674
+ return new UnitPromise(Promise.all(Unit.current._.promises)).finally(callback);
675
+ }
676
+ catch (error) {
677
+ console.error('xnew.finally(callback: Function): ', error);
678
+ throw error;
679
+ }
680
+ },
681
+ /**
682
+ * Fetches a resource and registers the promise with the current component
683
+ * @param url - URL to fetch
684
+ * @param options - Optional fetch options (method, headers, body, etc.)
685
+ * @returns UnitPromise wrapping the fetch promise
686
+ * @example
687
+ * xnew.fetch('/api/users').then(res => res.json()).then(data => console.log(data))
688
+ */
689
+ fetch(url, options) {
690
+ try {
691
+ const promise = fetch(url, options);
692
+ Unit.current._.promises.push(promise);
693
+ return new UnitPromise(promise);
694
+ }
695
+ catch (error) {
696
+ console.error('xnew.promise(url: string, options?: object): ', error);
697
+ throw error;
698
+ }
699
+ },
700
+ /**
701
+ * Creates a scoped callback that captures the current component context
702
+ * @param callback - Function to wrap with current scope
703
+ * @returns Function that executes callback in the captured scope
704
+ * @example
705
+ * setTimeout(xnew.scope(() => {
706
+ * console.log('This runs in the xnew component scope')
707
+ * }), 1000)
708
+ */
709
+ scope(callback) {
710
+ const snapshot = Unit.snapshot(Unit.current);
711
+ return (...args) => Unit.scope(snapshot, callback, ...args);
712
+ },
713
+ /**
714
+ * Finds all instances of a component in the component tree
715
+ * @param component - Component function to search for
716
+ * @returns Array of Unit instances matching the component
717
+ * @throws Error if component parameter is invalid
718
+ * @example
719
+ * const buttons = xnew.find(ButtonComponent)
720
+ * buttons.forEach(btn => btn.finalize())
721
+ */
722
+ find(component) {
723
+ if (typeof component === 'function') {
724
+ return Unit.find(component);
725
+ }
726
+ else {
727
+ throw new Error('xnew.find(component: Function): [component] is invalid.');
728
+ }
729
+ },
730
+ /**
731
+ * Appends new components to existing component(s) in the tree
732
+ * @param anchor - Component function or Unit instance to append to
733
+ * @param args - Arguments to pass to xnew for creating child components
734
+ * @throws Error if anchor parameter is invalid
735
+ * @example
736
+ * xnew.append(MyContainer, ChildComponent, { prop: 'value' })
737
+ * xnew.append(unitInstance, AnotherComponent)
738
+ */
739
+ append(anchor, ...args) {
740
+ if (typeof anchor === 'function') {
741
+ const units = Unit.find(anchor);
742
+ Unit.scope(Unit.snapshot(units[0]), xnew$1, ...args);
743
+ }
744
+ else if (anchor instanceof Unit) {
745
+ Unit.scope(Unit.snapshot(anchor), xnew$1, ...args);
746
+ }
747
+ else {
748
+ throw new Error('xnew.append(anchor: Function | Unit, xnew arguments): [anchor] is invalid.');
749
+ }
750
+ },
751
+ /**
752
+ * Executes a callback once after a delay, managed by component lifecycle
753
+ * @param callback - Function to execute after delay
754
+ * @param delay - Delay in milliseconds
755
+ * @returns Object with clear() method to cancel the timeout
756
+ * @example
757
+ * const timer = xnew.timeout(() => console.log('Delayed'), 1000)
758
+ * // Cancel if needed: timer.clear()
759
+ */
760
+ timeout(callback, delay) {
761
+ const snapshot = Unit.snapshot(Unit.current);
762
+ const unit = xnew$1((self) => {
763
+ const timer = new Timer(() => {
764
+ Unit.scope(snapshot, callback);
765
+ self.finalize();
766
+ }, null, delay, false);
767
+ self.on('finalize', () => timer.clear());
713
768
  });
714
- });
715
- return { clear: () => unit.finalize() };
716
- };
717
- xnew$1.interval = (callback, delay) => {
718
- const snapshot = Unit.snapshot(Unit.current);
719
- const unit = xnew$1((self) => {
720
- const timer = new Timer(() => {
721
- Unit.scope(snapshot, callback);
722
- }, null, delay, true);
723
- self.on('finalize', () => {
724
- timer.clear();
769
+ return { clear: () => unit.finalize() };
770
+ },
771
+ /**
772
+ * Executes a callback repeatedly at specified intervals, managed by component lifecycle
773
+ * @param callback - Function to execute at each interval
774
+ * @param delay - Interval duration in milliseconds
775
+ * @returns Object with clear() method to stop the interval
776
+ * @example
777
+ * const timer = xnew.interval(() => console.log('Tick'), 1000)
778
+ * // Stop when needed: timer.clear()
779
+ */
780
+ interval(callback, delay) {
781
+ const snapshot = Unit.snapshot(Unit.current);
782
+ const unit = xnew$1((self) => {
783
+ const timer = new Timer(() => {
784
+ Unit.scope(snapshot, callback);
785
+ }, null, delay, true);
786
+ self.on('finalize', () => timer.clear());
725
787
  });
726
- });
727
- return { clear: () => unit.finalize() };
728
- };
729
- xnew$1.transition = (callback, interval, easing = 'linear') => {
730
- const snapshot = Unit.snapshot(Unit.current);
731
- let stacks = [];
732
- let unit = xnew$1(Local, { callback, interval, easing });
733
- let isRunning = true;
734
- function Local(self, { callback, interval, easing }) {
735
- const timer = new Timer(() => {
736
- Unit.scope(snapshot, callback, 1.0);
737
- self.finalize();
738
- }, (progress) => {
739
- if (progress < 1.0) {
740
- if (easing === 'ease-out') {
741
- progress = Math.pow((1.0 - Math.pow((1.0 - progress), 2.0)), 0.5);
742
- }
743
- else if (easing === 'ease-in') {
744
- progress = Math.pow((1.0 - Math.pow((1.0 - progress), 0.5)), 2.0);
745
- }
746
- else if (easing === 'ease') {
747
- progress = (1.0 - Math.cos(progress * Math.PI)) / 2.0;
748
- }
749
- else if (easing === 'ease-in-out') {
750
- progress = (1.0 - Math.cos(progress * Math.PI)) / 2.0;
751
- }
752
- Unit.scope(snapshot, callback, progress);
788
+ return { clear: () => unit.finalize() };
789
+ },
790
+ /**
791
+ * Creates a transition animation with easing, executing callback with progress values
792
+ * @param callback - Function called with progress value (0.0 to 1.0)
793
+ * @param interval - Duration of transition in milliseconds
794
+ * @param easing - Easing function: 'linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out' (default: 'linear')
795
+ * @returns Object with clear() and next() methods for controlling transitions
796
+ * @example
797
+ * xnew.transition(progress => {
798
+ * element.style.opacity = progress
799
+ * }, 500, 'ease-out').next(progress => {
800
+ * element.style.transform = `scale(${progress})`
801
+ * }, 300)
802
+ */
803
+ transition(callback, interval, easing = 'linear') {
804
+ const snapshot = Unit.snapshot(Unit.current);
805
+ let stacks = [];
806
+ let unit = xnew$1(Local, { callback, interval, easing });
807
+ let isRunning = true;
808
+ const timer = { clear, next };
809
+ return timer;
810
+ function execute() {
811
+ if (isRunning === false && stacks.length > 0) {
812
+ unit = xnew$1(Local, stacks.shift());
813
+ isRunning = true;
753
814
  }
754
- }, interval);
755
- self.on('finalize', () => {
756
- timer.clear();
757
- isRunning = false;
815
+ }
816
+ function clear() {
817
+ stacks = [];
818
+ unit.finalize();
819
+ }
820
+ function next(callback, interval = 0, easing = 'linear') {
821
+ stacks.push({ callback, interval, easing });
758
822
  execute();
759
- });
760
- }
761
- let timer = null;
762
- function execute() {
763
- if (isRunning === false && stacks.length > 0) {
764
- const props = stacks.shift();
765
- unit = xnew$1(Local, props);
766
- isRunning = true;
823
+ return timer;
824
+ }
825
+ function Local(self, { callback, interval, easing }) {
826
+ const timer = new Timer(() => {
827
+ Unit.scope(snapshot, callback, 1.0);
828
+ self.finalize();
829
+ }, (progress) => {
830
+ if (progress < 1.0) {
831
+ if (easing === 'ease-out') {
832
+ progress = Math.pow((1.0 - Math.pow((1.0 - progress), 2.0)), 0.5);
833
+ }
834
+ else if (easing === 'ease-in') {
835
+ progress = Math.pow((1.0 - Math.pow((1.0 - progress), 0.5)), 2.0);
836
+ }
837
+ else if (easing === 'ease') {
838
+ progress = (1.0 - Math.cos(progress * Math.PI)) / 2.0;
839
+ }
840
+ else if (easing === 'ease-in-out') {
841
+ progress = (1.0 - Math.cos(progress * Math.PI)) / 2.0;
842
+ }
843
+ Unit.scope(snapshot, callback, progress);
844
+ }
845
+ }, interval);
846
+ self.on('finalize', () => {
847
+ timer.clear();
848
+ isRunning = false;
849
+ execute();
850
+ });
767
851
  }
852
+ },
853
+ /**
854
+ * Creates an event listener manager for a target element with automatic cleanup
855
+ * @param target - Element, Window, or Document to attach listeners to
856
+ * @returns Object with on() and off() methods for managing event listeners
857
+ * @example
858
+ * const mouse = xnew.listener(window)
859
+ * mouse.on('mousemove', (e) => console.log(e.clientX, e.clientY))
860
+ * // Automatically cleaned up when component finalizes
861
+ */
862
+ listener(target) {
863
+ return {
864
+ on(type, listener, options) {
865
+ Unit.subon(Unit.current, target, type, listener, options);
866
+ },
867
+ off(type, listener) {
868
+ Unit.suboff(Unit.current, target, type, listener);
869
+ }
870
+ };
871
+ },
872
+ /**
873
+ * Registers a capture function that can intercept and handle child component events
874
+ * @param execute - Function that receives child unit and returns boolean (true to stop propagation)
875
+ * @example
876
+ * xnew.capture((childUnit) => {
877
+ * console.log('Child component created:', childUnit)
878
+ * return false // Continue propagation
879
+ * })
880
+ */
881
+ capture(execute) {
882
+ Unit.current._.captures.push(Unit.wrap(Unit.current, (unit) => execute(unit)));
883
+ },
884
+ });
885
+
886
+ function ResizeEvent(resize) {
887
+ const observer = new ResizeObserver((entries) => {
888
+ for (const entry of entries) {
889
+ resize.emit('-resize');
890
+ break;
891
+ }
892
+ });
893
+ if (resize.element) {
894
+ observer.observe(resize.element);
768
895
  }
769
- function clear() {
770
- stacks = [];
771
- unit.finalize();
772
- }
773
- function next(callback, interval, easing = 'linear') {
774
- stacks.push({ callback, interval, easing });
775
- execute();
776
- return timer;
777
- }
778
- timer = { clear, next };
779
- return timer;
780
- };
781
- xnew$1.listener = function (target) {
782
- return {
783
- on(type, listener, options) {
784
- Unit.subon(Unit.current, target, type, listener, options);
785
- },
786
- off(type, listener) {
787
- Unit.suboff(Unit.current, target, type, listener);
896
+ resize.on('finalize', () => {
897
+ if (resize.element) {
898
+ observer.unobserve(resize.element);
788
899
  }
789
- };
790
- };
791
- xnew$1.capture = function (checker, execute) {
792
- Unit.current._.captures.push({ checker, execute: Unit.wrap(Unit.current, (unit) => execute(unit)) });
793
- };
900
+ });
901
+ }
794
902
 
795
- function UserEvent(self) {
796
- const unit = xnew$1();
797
- unit.on('pointerdown', (event) => self.emit('-pointerdown', { event, position: getPosition(self.element, event) }));
798
- unit.on('pointermove', (event) => self.emit('-pointermove', { event, position: getPosition(self.element, event) }));
799
- unit.on('pointerup', (event) => self.emit('-pointerup', { event, position: getPosition(self.element, event) }));
800
- unit.on('wheel', (event) => self.emit('-wheel', { event, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } }));
903
+ function PointerEvent(unit) {
904
+ const internal = xnew$1();
905
+ internal.on('pointerdown', (event) => unit.emit('-pointerdown', { event, position: getPosition(unit.element, event) }));
906
+ internal.on('pointermove', (event) => unit.emit('-pointermove', { event, position: getPosition(unit.element, event) }));
907
+ internal.on('pointerup', (event) => unit.emit('-pointerup', { event, position: getPosition(unit.element, event) }));
908
+ internal.on('wheel', (event) => unit.emit('-wheel', { event, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } }));
909
+ internal.on('mouseover', (event) => unit.emit('-mouseover', { event, position: getPosition(unit.element, event) }));
910
+ internal.on('mouseout', (event) => unit.emit('-mouseout', { event, position: getPosition(unit.element, event) }));
801
911
  const drag = xnew$1(DragEvent);
802
- drag.on('-dragstart', (...args) => self.emit('-dragstart', ...args));
803
- drag.on('-dragmove', (...args) => self.emit('-dragmove', ...args));
804
- drag.on('-dragend', (...args) => self.emit('-dragend', ...args));
805
- drag.on('-dragcancel', (...args) => self.emit('-dragcancel', ...args));
912
+ drag.on('-dragstart', (...args) => unit.emit('-dragstart', ...args));
913
+ drag.on('-dragmove', (...args) => unit.emit('-dragmove', ...args));
914
+ drag.on('-dragend', (...args) => unit.emit('-dragend', ...args));
915
+ drag.on('-dragcancel', (...args) => unit.emit('-dragcancel', ...args));
806
916
  const gesture = xnew$1(GestureEvent);
807
- gesture.on('-gesturestart', (...args) => self.emit('-gesturestart', ...args));
808
- gesture.on('-gesturemove', (...args) => self.emit('-gesturemove', ...args));
809
- gesture.on('-gestureend', (...args) => self.emit('-gestureend', ...args));
810
- gesture.on('-gesturecancel', (...args) => self.emit('-gesturecancel', ...args));
811
- const keyborad = xnew$1(Keyboard);
812
- keyborad.on('-keydown', (...args) => self.emit('-keydown', ...args));
813
- keyborad.on('-keyup', (...args) => self.emit('-keyup', ...args));
814
- keyborad.on('-arrowkeydown', (...args) => self.emit('-arrowkeydown', ...args));
815
- keyborad.on('-arrowkeyup', (...args) => self.emit('-arrowkeyup', ...args));
917
+ gesture.on('-gesturestart', (...args) => unit.emit('-gesturestart', ...args));
918
+ gesture.on('-gesturemove', (...args) => unit.emit('-gesturemove', ...args));
919
+ gesture.on('-gestureend', (...args) => unit.emit('-gestureend', ...args));
920
+ gesture.on('-gesturecancel', (...args) => unit.emit('-gesturecancel', ...args));
816
921
  }
817
- function DragEvent(self) {
922
+ function DragEvent(unit) {
818
923
  xnew$1().on('pointerdown', (event) => {
819
924
  const id = event.pointerId;
820
- const position = getPosition(self.element, event);
925
+ const position = getPosition(unit.element, event);
821
926
  let previous = position;
822
927
  xnew$1(() => {
823
928
  xnew$1.listener(window).on('pointermove', (event) => {
824
929
  if (event.pointerId === id) {
825
- const position = getPosition(self.element, event);
930
+ const position = getPosition(unit.element, event);
826
931
  const delta = { x: position.x - previous.x, y: position.y - previous.y };
827
- self.emit('-dragmove', { event, position, delta });
932
+ unit.emit('-dragmove', { event, position, delta });
828
933
  previous = position;
829
934
  }
830
935
  });
831
936
  xnew$1.listener(window).on('pointerup', (event) => {
832
937
  if (event.pointerId === id) {
833
- const position = getPosition(self.element, event);
834
- self.emit('-dragend', { event, position, });
938
+ const position = getPosition(unit.element, event);
939
+ unit.emit('-dragend', { event, position, });
835
940
  xnew$1.listener(window).off();
836
941
  }
837
942
  });
838
943
  xnew$1.listener(window).on('pointercancel', (event) => {
839
944
  if (event.pointerId === id) {
840
- const position = getPosition(self.element, event);
841
- self.emit('-dragcancel', { event, position, });
945
+ const position = getPosition(unit.element, event);
946
+ unit.emit('-dragcancel', { event, position, });
842
947
  xnew$1.listener(window).off();
843
948
  }
844
949
  });
845
950
  });
846
- self.emit('-dragstart', { event, position });
951
+ unit.emit('-dragstart', { event, position });
847
952
  });
848
953
  }
849
- function GestureEvent(self) {
954
+ function GestureEvent(unit) {
850
955
  const drag = xnew$1(DragEvent);
851
956
  let isActive = false;
852
957
  const map = new Map();
@@ -854,7 +959,7 @@ function GestureEvent(self) {
854
959
  map.set(event.pointerId, Object.assign({}, position));
855
960
  isActive = map.size === 2 ? true : false;
856
961
  if (isActive === true) {
857
- self.emit('-gesturestart', {});
962
+ unit.emit('-gesturestart', {});
858
963
  }
859
964
  });
860
965
  drag.on('-dragmove', ({ event, position, delta }) => {
@@ -880,20 +985,20 @@ function GestureEvent(self) {
880
985
  // rotate = sign > 0.0 ? +angle : -angle;
881
986
  // }
882
987
  // }
883
- self.emit('-gesturemove', { event, position, delta, scale });
988
+ unit.emit('-gesturemove', { event, position, delta, scale });
884
989
  }
885
990
  map.set(event.pointerId, position);
886
991
  });
887
992
  drag.on('-dragend', ({ event }) => {
888
993
  if (isActive === true) {
889
- self.emit('-gestureend', {});
994
+ unit.emit('-gestureend', {});
890
995
  }
891
996
  isActive = false;
892
997
  map.delete(event.pointerId);
893
998
  });
894
999
  drag.on('-dragcancel', ({ event }) => {
895
1000
  if (isActive === true) {
896
- self.emit('-gesturecancel', { event });
1001
+ unit.emit('-gesturecancel', { event });
897
1002
  }
898
1003
  isActive = false;
899
1004
  map.delete(event.pointerId);
@@ -906,21 +1011,26 @@ function GestureEvent(self) {
906
1011
  return others;
907
1012
  }
908
1013
  }
909
- function Keyboard(self) {
1014
+ function getPosition(element, event) {
1015
+ const rect = element.getBoundingClientRect();
1016
+ return { x: event.clientX - rect.left, y: event.clientY - rect.top };
1017
+ }
1018
+
1019
+ function KeyboardEvent(unit) {
910
1020
  const state = {};
911
1021
  xnew$1.listener(window).on('keydown', (event) => {
912
1022
  state[event.code] = 1;
913
- self.emit('-keydown', { event, code: event.code });
1023
+ unit.emit('-keydown', { event, type: '-keydown', code: event.code });
914
1024
  });
915
1025
  xnew$1.listener(window).on('keyup', (event) => {
916
1026
  state[event.code] = 0;
917
- self.emit('-keyup', { event, code: event.code });
1027
+ unit.emit('-keyup', { event, type: '-keyup', code: event.code });
918
1028
  });
919
1029
  xnew$1.listener(window).on('keydown', (event) => {
920
- self.emit('-arrowkeydown', { event, code: event.code, vector: getVector() });
1030
+ unit.emit('-arrowkeydown', { event, type: '-arrowkeydown', code: event.code, vector: getVector() });
921
1031
  });
922
1032
  xnew$1.listener(window).on('keyup', (event) => {
923
- self.emit('-arrowkeyup', { event, code: event.code, vector: getVector() });
1033
+ unit.emit('-arrowkeyup', { event, type: '-arrowkeyup', code: event.code, vector: getVector() });
924
1034
  });
925
1035
  function getVector() {
926
1036
  return {
@@ -929,16 +1039,12 @@ function Keyboard(self) {
929
1039
  };
930
1040
  }
931
1041
  }
932
- function getPosition(element, event) {
933
- const rect = element.getBoundingClientRect();
934
- return { x: event.clientX - rect.left, y: event.clientY - rect.top };
935
- }
936
1042
 
937
1043
  function Screen(screen, { width = 640, height = 480, fit = 'contain' } = {}) {
938
1044
  const size = { width, height };
939
1045
  const wrapper = xnew$1.nest('<div style="position: relative; width: 100%; height: 100%; overflow: hidden;">');
940
1046
  const absolute = xnew$1.nest('<div style="position: absolute; margin: auto;">');
941
- const canvas = xnew$1.nest(`<canvas width="${width}" height="${height}" style="width: 100%; height: 100%; vertical-align: bottom; user-select: none; user-drag: none;">`);
1047
+ 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;">`);
942
1048
  xnew$1(wrapper, ResizeEvent).on('-resize', resize);
943
1049
  resize();
944
1050
  function resize() {
@@ -969,36 +1075,34 @@ function Screen(screen, { width = 640, height = 480, fit = 'contain' } = {}) {
969
1075
  }
970
1076
  return {
971
1077
  get canvas() {
972
- return canvas;
1078
+ return canvas.element;
973
1079
  },
974
1080
  resize(width, height) {
975
1081
  size.width = width;
976
1082
  size.height = height;
977
- canvas.setAttribute('width', width + 'px');
978
- canvas.setAttribute('height', height + 'px');
1083
+ canvas.element.setAttribute('width', width + 'px');
1084
+ canvas.element.setAttribute('height', height + 'px');
979
1085
  resize();
980
1086
  },
981
- get scale() {
982
- return { x: size.width / canvas.clientWidth, y: size.height / canvas.clientHeight };
983
- }
984
1087
  };
985
1088
  }
986
1089
 
987
1090
  function InputFrame(frame, {} = {}) {
988
1091
  xnew$1.nest('<div>');
989
1092
  xnew$1.capture((unit) => {
990
- return unit.element.tagName.toLowerCase() === 'input';
991
- }, (unit) => {
992
- const element = unit.element;
993
- xnew$1.listener(element).on('input', (event) => {
994
- frame.emit('-input', { event });
995
- });
996
- xnew$1.listener(element).on('change', (event) => {
997
- frame.emit('-change', { event });
998
- });
999
- xnew$1.listener(element).on('click', (event) => {
1000
- frame.emit('-click', { event });
1001
- });
1093
+ if (unit.element.tagName.toLowerCase() === 'input') {
1094
+ const element = unit.element;
1095
+ xnew$1.listener(element).on('input', (event) => {
1096
+ frame.emit('-input', { event });
1097
+ });
1098
+ xnew$1.listener(element).on('change', (event) => {
1099
+ frame.emit('-change', { event });
1100
+ });
1101
+ xnew$1.listener(element).on('click', (event) => {
1102
+ frame.emit('-click', { event });
1103
+ });
1104
+ return true;
1105
+ }
1002
1106
  });
1003
1107
  }
1004
1108
 
@@ -1033,14 +1137,14 @@ function ModalContent(content, { background = 'rgba(0, 0, 0, 0.1)' } = {}) {
1033
1137
  };
1034
1138
  }
1035
1139
 
1036
- function TabFrame(frame, { key } = {}) {
1140
+ function TabFrame(frame, { select } = {}) {
1037
1141
  const internal = xnew$1((internal) => {
1038
1142
  const buttons = new Map();
1039
1143
  const contents = new Map();
1040
1144
  return { frame, buttons, contents };
1041
1145
  });
1042
1146
  xnew$1.context('xnew.tabframe', internal);
1043
- xnew$1.timeout(() => internal.emit('-select', { key: key !== null && key !== void 0 ? key : [...internal.buttons.keys()][0] }));
1147
+ xnew$1.timeout(() => internal.emit('-select', { key: select !== null && select !== void 0 ? select : [...internal.buttons.keys()][0] }));
1044
1148
  }
1045
1149
  function TabButton(button, { key } = {}) {
1046
1150
  const internal = xnew$1.context('xnew.tabframe');
@@ -1177,18 +1281,30 @@ function DragFrame(frame, { x = 0, y = 0 } = {}) {
1177
1281
  function DragTarget(target, {} = {}) {
1178
1282
  const { frame, absolute } = xnew$1.context('xnew.dragframe');
1179
1283
  xnew$1.nest('<div>');
1180
- const user = xnew$1(absolute.parentElement, UserEvent);
1284
+ const pointer = xnew$1(absolute.parentElement, PointerEvent);
1181
1285
  const current = { x: 0, y: 0 };
1182
- user.on('-dragstart', ({ event, position }) => {
1183
- current.x = parseFloat(absolute.style.left || '0') + position.x;
1184
- current.y = parseFloat(absolute.style.top || '0') + position.y;
1286
+ const offset = { x: 0, y: 0 };
1287
+ let dragged = false;
1288
+ pointer.on('-dragstart', ({ event, position }) => {
1289
+ if (target.element.contains(event.target) === false)
1290
+ return;
1291
+ dragged = true;
1292
+ offset.x = position.x - parseFloat(absolute.style.left || '0');
1293
+ offset.y = position.y - parseFloat(absolute.style.top || '0');
1294
+ current.x = position.x - offset.x;
1295
+ current.y = position.y - offset.y;
1185
1296
  });
1186
- user.on('-dragmove', ({ event, delta }) => {
1297
+ pointer.on('-dragmove', ({ event, delta }) => {
1298
+ if (dragged !== true)
1299
+ return;
1187
1300
  current.x += delta.x;
1188
1301
  current.y += delta.y;
1189
1302
  absolute.style.left = `${current.x}px`;
1190
1303
  absolute.style.top = `${current.y}px`;
1191
1304
  });
1305
+ pointer.on('-dragcancel -dragend', ({ event }) => {
1306
+ dragged = false;
1307
+ });
1192
1308
  }
1193
1309
 
1194
1310
  //----------------------------------------------------------------------------------------------------
@@ -1197,439 +1313,382 @@ function DragTarget(target, {} = {}) {
1197
1313
  function SVGTemplate(self, { fill = null, fillOpacity = 0.8, stroke = null, strokeOpacity = 0.8, strokeWidth = 2, strokeLinejoin = 'round' }) {
1198
1314
  xnew$1.nest(`<svg
1199
1315
  viewBox="0 0 100 100"
1200
- style="position: absolute; width: 100%; height: 100%; user-select: none;
1316
+ style="position: absolute; width: 100%; height: 100%; pointer-select: none;
1201
1317
  ${fill ? `fill: ${fill}; fill-opacity: ${fillOpacity};` : ''}
1202
1318
  ${stroke ? `stroke: ${stroke}; stroke-opacity: ${strokeOpacity}; stroke-width: ${strokeWidth}; stroke-linejoin: ${strokeLinejoin};` : ''}
1203
1319
  ">`);
1204
1320
  }
1205
- function TouchStick(self, { size = 130, fill = '#FFF', fillOpacity = 0.8, stroke = '#000', strokeOpacity = 0.8, strokeWidth = 2, strokeLinejoin = 'round' } = {}) {
1206
- strokeWidth /= (size / 100);
1207
- xnew$1.nest(`<div style="position: relative; width: ${size}px; height: ${size}px; cursor: pointer; user-select: none; overflow: hidden;">`);
1208
- xnew$1((self) => {
1209
- xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1210
- xnew$1('<polygon points="50 7 40 18 60 18">');
1211
- xnew$1('<polygon points="50 93 40 83 60 83">');
1212
- xnew$1('<polygon points=" 7 50 18 40 18 60">');
1213
- xnew$1('<polygon points="93 50 83 40 83 60">');
1214
- });
1215
- const target = xnew$1((self) => {
1216
- xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1217
- xnew$1('<circle cx="50" cy="50" r="23">');
1218
- });
1219
- const user = xnew$1(UserEvent);
1220
- user.on('-dragstart', ({ event, position }) => {
1221
- const vector = getVector(position);
1222
- target.element.style.filter = 'brightness(90%)';
1223
- target.element.style.left = vector.x * size / 4 + 'px';
1224
- target.element.style.top = vector.y * size / 4 + 'px';
1225
- self.emit('-down', { vector });
1226
- });
1227
- user.on('-dragmove', ({ event, position }) => {
1228
- const vector = getVector(position);
1229
- target.element.style.filter = 'brightness(90%)';
1230
- target.element.style.left = vector.x * size / 4 + 'px';
1231
- target.element.style.top = vector.y * size / 4 + 'px';
1232
- self.emit('-move', { vector });
1233
- });
1234
- user.on('-dragend', ({ event }) => {
1235
- const vector = { x: 0, y: 0 };
1236
- target.element.style.filter = '';
1237
- target.element.style.left = vector.x * size / 4 + 'px';
1238
- target.element.style.top = vector.y * size / 4 + 'px';
1239
- self.emit('-up', { vector });
1240
- });
1241
- function getVector(position) {
1242
- const x = position.x - size / 2;
1243
- const y = position.y - size / 2;
1244
- const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (size / 4));
1245
- const a = (y !== 0 || x !== 0) ? Math.atan2(y, x) : 0;
1246
- return { x: Math.cos(a) * d, y: Math.sin(a) * d };
1321
+ function AnalogStick(self, { size, fill = '#FFF', fillOpacity = 0.8, stroke = '#000', strokeOpacity = 0.8, strokeWidth = 2, strokeLinejoin = 'round' } = {}) {
1322
+ xnew$1.nest(`<div style="position: relative; width: 100%; height: 100%;">`);
1323
+ let internal;
1324
+ let newsize;
1325
+ if (size) {
1326
+ newsize = size;
1247
1327
  }
1248
- }
1249
- function TouchDPad(self, { size = 130, fill = '#FFF', fillOpacity = 0.8, stroke = '#000', strokeOpacity = 0.8, strokeWidth = 2, strokeLinejoin = 'round' } = {}) {
1250
- strokeWidth /= (size / 100);
1251
- xnew$1.nest(`<div style="position: relative; width: ${size}px; height: ${size}px; cursor: pointer; user-select: none; overflow: hidden;">`);
1252
- const polygons = [
1253
- '<polygon points="50 50 35 35 35 5 37 3 63 3 65 5 65 35">',
1254
- '<polygon points="50 50 35 65 35 95 37 97 63 97 65 95 65 65">',
1255
- '<polygon points="50 50 35 35 5 35 3 37 3 63 5 65 35 65">',
1256
- '<polygon points="50 50 65 35 95 35 97 37 97 63 95 65 65 65">'
1257
- ];
1258
- const targets = polygons.map((polygon) => {
1259
- return xnew$1((self) => {
1260
- xnew$1.extend(SVGTemplate, { fill, fillOpacity });
1261
- xnew$1(polygon);
1328
+ else {
1329
+ newsize = Math.min(self.element.clientWidth, self.element.clientHeight);
1330
+ xnew$1(self.element, ResizeEvent).on('-resize', () => {
1331
+ newsize = Math.min(self.element.clientWidth, self.element.clientHeight);
1332
+ internal === null || internal === void 0 ? void 0 : internal.reboot();
1262
1333
  });
1263
- });
1264
- xnew$1((self) => {
1265
- xnew$1.extend(SVGTemplate, { fill: 'none', stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1266
- xnew$1('<polyline points="35 35 35 5 37 3 63 3 65 5 65 35">');
1267
- xnew$1('<polyline points="35 65 35 95 37 97 63 97 65 95 65 65">');
1268
- xnew$1('<polyline points="35 35 5 35 3 37 3 63 5 65 35 65">');
1269
- xnew$1('<polyline points="65 35 95 35 97 37 97 63 95 65 65 65">');
1270
- xnew$1('<polygon points="50 11 42 20 58 20">');
1271
- xnew$1('<polygon points="50 89 42 80 58 80">');
1272
- xnew$1('<polygon points="11 50 20 42 20 58">');
1273
- xnew$1('<polygon points="89 50 80 42 80 58">');
1274
- });
1275
- const user = xnew$1(UserEvent);
1276
- user.on('-dragstart', ({ event, position }) => {
1277
- const vector = getVector(position);
1278
- targets[0].element.style.filter = (vector.y < 0) ? 'brightness(90%)' : '';
1279
- targets[1].element.style.filter = (vector.y > 0) ? 'brightness(90%)' : '';
1280
- targets[2].element.style.filter = (vector.x < 0) ? 'brightness(90%)' : '';
1281
- targets[3].element.style.filter = (vector.x > 0) ? 'brightness(90%)' : '';
1282
- self.emit('-down', { vector });
1283
- });
1284
- user.on('-dragmove', ({ event, position }) => {
1285
- const vector = getVector(position);
1286
- targets[0].element.style.filter = (vector.y < 0) ? 'brightness(90%)' : '';
1287
- targets[1].element.style.filter = (vector.y > 0) ? 'brightness(90%)' : '';
1288
- targets[2].element.style.filter = (vector.x < 0) ? 'brightness(90%)' : '';
1289
- targets[3].element.style.filter = (vector.x > 0) ? 'brightness(90%)' : '';
1290
- self.emit('-move', { vector });
1291
- });
1292
- user.on('-dragend', ({ event }) => {
1293
- const vector = { x: 0, y: 0 };
1294
- targets[0].element.style.filter = '';
1295
- targets[1].element.style.filter = '';
1296
- targets[2].element.style.filter = '';
1297
- targets[3].element.style.filter = '';
1298
- self.emit('-up', { vector });
1299
- });
1300
- function getVector(position) {
1301
- const x = position.x - size / 2;
1302
- const y = position.y - size / 2;
1303
- const a = (y !== 0 || x !== 0) ? Math.atan2(y, x) : 0;
1304
- const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (size / 4));
1305
- const vector = { x: Math.cos(a) * d, y: Math.sin(a) * d };
1306
- vector.x = Math.abs(vector.x) > 0.5 ? Math.sign(vector.x) : 0;
1307
- vector.y = Math.abs(vector.y) > 0.5 ? Math.sign(vector.y) : 0;
1308
- return vector;
1309
1334
  }
1310
- }
1311
- function TouchButton(self, { size = 80, fill = '#FFF', fillOpacity = 0.8, stroke = '#000', strokeOpacity = 0.8, strokeWidth = 2, strokeLinejoin = 'round' } = {}) {
1312
- strokeWidth /= (size / 100);
1313
- xnew$1.nest(`<div style="position: relative; width: ${size}px; height: ${size}px; cursor: pointer; user-select: none; overflow: hidden;">`);
1314
- const target = xnew$1((self) => {
1315
- xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1316
- xnew$1('<circle cx="50" cy="50" r="40">');
1317
- });
1318
- const user = xnew$1(UserEvent);
1319
- user.on('-dragstart', (event) => {
1320
- target.element.style.filter = 'brightness(90%)';
1321
- self.emit('-down', event);
1322
- });
1323
- user.on('-dragend', (event) => {
1324
- target.element.style.filter = '';
1325
- self.emit('-up', event);
1335
+ internal = xnew$1(() => {
1336
+ 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;">`);
1337
+ xnew$1((self) => {
1338
+ xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1339
+ xnew$1('<polygon points="50 7 40 18 60 18">');
1340
+ xnew$1('<polygon points="50 93 40 83 60 83">');
1341
+ xnew$1('<polygon points=" 7 50 18 40 18 60">');
1342
+ xnew$1('<polygon points="93 50 83 40 83 60">');
1343
+ });
1344
+ const target = xnew$1((self) => {
1345
+ xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1346
+ xnew$1('<circle cx="50" cy="50" r="23">');
1347
+ });
1348
+ const pointer = xnew$1(PointerEvent);
1349
+ pointer.on('-dragstart', ({ event, position }) => {
1350
+ const vector = getVector(position);
1351
+ target.element.style.filter = 'brightness(90%)';
1352
+ target.element.style.left = vector.x * newsize / 4 + 'px';
1353
+ target.element.style.top = vector.y * newsize / 4 + 'px';
1354
+ self.emit('-down', { vector });
1355
+ });
1356
+ pointer.on('-dragmove', ({ event, position }) => {
1357
+ const vector = getVector(position);
1358
+ target.element.style.filter = 'brightness(90%)';
1359
+ target.element.style.left = vector.x * newsize / 4 + 'px';
1360
+ target.element.style.top = vector.y * newsize / 4 + 'px';
1361
+ self.emit('-move', { vector });
1362
+ });
1363
+ pointer.on('-dragend', ({ event }) => {
1364
+ const vector = { x: 0, y: 0 };
1365
+ target.element.style.filter = '';
1366
+ target.element.style.left = vector.x * newsize / 4 + 'px';
1367
+ target.element.style.top = vector.y * newsize / 4 + 'px';
1368
+ self.emit('-up', { vector });
1369
+ });
1370
+ function getVector(position) {
1371
+ const x = position.x - newsize / 2;
1372
+ const y = position.y - newsize / 2;
1373
+ const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (newsize / 4));
1374
+ const a = (y !== 0 || x !== 0) ? Math.atan2(y, x) : 0;
1375
+ return { x: Math.cos(a) * d, y: Math.sin(a) * d };
1376
+ }
1326
1377
  });
1327
1378
  }
1328
-
1329
- class Audio {
1330
- static initialize() {
1331
- var _a;
1332
- if (typeof window !== 'undefined' && window instanceof Window) {
1333
- Audio.context = new ((_a = window.AudioContext) !== null && _a !== void 0 ? _a : window.webkitAudioContext)();
1334
- Audio.master = Audio.context.createGain();
1335
- Audio.master.gain.value = 1.0;
1336
- Audio.master.connect(Audio.context.destination);
1337
- }
1379
+ function DirectionalPad(self, { size, diagonal = true, fill = '#FFF', fillOpacity = 0.8, stroke = '#000', strokeOpacity = 0.8, strokeWidth = 2, strokeLinejoin = 'round' } = {}) {
1380
+ xnew$1.nest(`<div style="position: relative; width: 100%; height: 100%;">`);
1381
+ let internal;
1382
+ let newsize;
1383
+ if (size) {
1384
+ newsize = size;
1338
1385
  }
1339
- static connect(params) {
1340
- if (!Audio.context)
1341
- throw new Error("Audio context not initialized");
1342
- const nodes = {};
1343
- Object.keys(params).forEach((key) => {
1344
- const [type, props, ...to] = params[key];
1345
- nodes[key] = Audio.context[`create${type}`]();
1346
- Object.keys(props).forEach((name) => {
1347
- var _a;
1348
- if (((_a = nodes[key][name]) === null || _a === void 0 ? void 0 : _a.value) !== undefined) {
1349
- nodes[key][name].value = props[name];
1350
- }
1351
- else {
1352
- nodes[key][name] = props[name];
1353
- }
1354
- });
1386
+ else {
1387
+ newsize = Math.min(self.element.clientWidth, self.element.clientHeight);
1388
+ xnew$1(self.element, ResizeEvent).on('-resize', () => {
1389
+ newsize = Math.min(self.element.clientWidth, self.element.clientHeight);
1390
+ internal === null || internal === void 0 ? void 0 : internal.reboot();
1355
1391
  });
1356
- Object.keys(params).forEach((key) => {
1357
- const [type, props, ...to] = params[key];
1358
- to.forEach((to) => {
1359
- let dest = null;
1360
- if (to.indexOf('.') > 0) {
1361
- dest = nodes[to.split('.')[0]][to.split('.')[1]];
1362
- }
1363
- else if (nodes[to]) {
1364
- dest = nodes[to];
1365
- }
1366
- else if (to === 'master') {
1367
- dest = Audio.master;
1368
- }
1369
- nodes[key].connect(dest);
1392
+ }
1393
+ internal = xnew$1(() => {
1394
+ 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;">`);
1395
+ const polygons = [
1396
+ '<polygon points="50 50 35 35 35 5 37 3 63 3 65 5 65 35">',
1397
+ '<polygon points="50 50 35 65 35 95 37 97 63 97 65 95 65 65">',
1398
+ '<polygon points="50 50 35 35 5 35 3 37 3 63 5 65 35 65">',
1399
+ '<polygon points="50 50 65 35 95 35 97 37 97 63 95 65 65 65">'
1400
+ ];
1401
+ const targets = polygons.map((polygon) => {
1402
+ return xnew$1((self) => {
1403
+ xnew$1.extend(SVGTemplate, { fill, fillOpacity });
1404
+ xnew$1(polygon);
1370
1405
  });
1371
1406
  });
1372
- return nodes;
1373
- }
1407
+ xnew$1((self) => {
1408
+ xnew$1.extend(SVGTemplate, { fill: 'none', stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1409
+ xnew$1('<polyline points="35 35 35 5 37 3 63 3 65 5 65 35">');
1410
+ xnew$1('<polyline points="35 65 35 95 37 97 63 97 65 95 65 65">');
1411
+ xnew$1('<polyline points="35 35 5 35 3 37 3 63 5 65 35 65">');
1412
+ xnew$1('<polyline points="65 35 95 35 97 37 97 63 95 65 65 65">');
1413
+ xnew$1('<polygon points="50 11 42 20 58 20">');
1414
+ xnew$1('<polygon points="50 89 42 80 58 80">');
1415
+ xnew$1('<polygon points="11 50 20 42 20 58">');
1416
+ xnew$1('<polygon points="89 50 80 42 80 58">');
1417
+ });
1418
+ const pointer = xnew$1(PointerEvent);
1419
+ pointer.on('-dragstart', ({ event, position }) => {
1420
+ const vector = getVector(position);
1421
+ targets[0].element.style.filter = (vector.y < 0) ? 'brightness(90%)' : '';
1422
+ targets[1].element.style.filter = (vector.y > 0) ? 'brightness(90%)' : '';
1423
+ targets[2].element.style.filter = (vector.x < 0) ? 'brightness(90%)' : '';
1424
+ targets[3].element.style.filter = (vector.x > 0) ? 'brightness(90%)' : '';
1425
+ self.emit('-down', { vector });
1426
+ });
1427
+ pointer.on('-dragmove', ({ event, position }) => {
1428
+ const vector = getVector(position);
1429
+ targets[0].element.style.filter = (vector.y < 0) ? 'brightness(90%)' : '';
1430
+ targets[1].element.style.filter = (vector.y > 0) ? 'brightness(90%)' : '';
1431
+ targets[2].element.style.filter = (vector.x < 0) ? 'brightness(90%)' : '';
1432
+ targets[3].element.style.filter = (vector.x > 0) ? 'brightness(90%)' : '';
1433
+ self.emit('-move', { vector });
1434
+ });
1435
+ pointer.on('-dragend', ({ event }) => {
1436
+ const vector = { x: 0, y: 0 };
1437
+ targets[0].element.style.filter = '';
1438
+ targets[1].element.style.filter = '';
1439
+ targets[2].element.style.filter = '';
1440
+ targets[3].element.style.filter = '';
1441
+ self.emit('-up', { vector });
1442
+ });
1443
+ function getVector(position) {
1444
+ const x = position.x - newsize / 2;
1445
+ const y = position.y - newsize / 2;
1446
+ const a = (y !== 0 || x !== 0) ? Math.atan2(y, x) : 0;
1447
+ const d = Math.min(1.0, Math.sqrt(x * x + y * y) / (newsize / 4));
1448
+ const vector = { x: Math.cos(a) * d, y: Math.sin(a) * d };
1449
+ if (diagonal === true) {
1450
+ vector.x = Math.abs(vector.x) > 0.5 ? Math.sign(vector.x) : 0;
1451
+ vector.y = Math.abs(vector.y) > 0.5 ? Math.sign(vector.y) : 0;
1452
+ }
1453
+ else if (Math.abs(vector.x) > Math.abs(vector.y)) {
1454
+ vector.x = Math.abs(vector.x) > 0.5 ? Math.sign(vector.x) : 0;
1455
+ vector.y = 0;
1456
+ }
1457
+ else {
1458
+ vector.x = 0;
1459
+ vector.y = Math.abs(vector.y) > 0.5 ? Math.sign(vector.y) : 0;
1460
+ }
1461
+ return vector;
1462
+ }
1463
+ });
1374
1464
  }
1375
- Audio.context = null;
1376
- Audio.master = null;
1377
- Audio.initialize();
1378
1465
 
1379
- function synthesizer(props, effects) {
1380
- return new Synthesizer(props, effects);
1466
+ const context = new AudioContext();
1467
+ const master = context.createGain();
1468
+ master.gain.value = 1.0;
1469
+ master.connect(context.destination);
1470
+
1471
+ function load(path) {
1472
+ return new AudioFile(path);
1381
1473
  }
1382
- function clamp(value, min, max) {
1383
- return Math.min(Math.max(value, min), max);
1474
+ const store = new Map();
1475
+ class AudioFile {
1476
+ constructor(path) {
1477
+ if (store.has(path)) {
1478
+ this.buffer = store.get(path);
1479
+ this.promise = Promise.resolve();
1480
+ }
1481
+ else {
1482
+ this.promise = fetch(path)
1483
+ .then((response) => response.arrayBuffer())
1484
+ .then((response) => context.decodeAudioData(response))
1485
+ .then((response) => {
1486
+ this.buffer = response;
1487
+ })
1488
+ .catch(() => {
1489
+ console.warn(`"${path}" could not be loaded.`);
1490
+ });
1491
+ store.set(path, this.buffer);
1492
+ }
1493
+ this.start = null;
1494
+ }
1495
+ // set volume(value: number) {
1496
+ // this.amp.gain.value = value;
1497
+ // }
1498
+ // get volume(): number {
1499
+ // return this.amp.gain.value;
1500
+ // }
1501
+ play(offset = 0, loop = false) {
1502
+ if (this.buffer !== undefined && this.start === null) {
1503
+ this.source = context.createBufferSource();
1504
+ this.source.buffer = this.buffer;
1505
+ this.source.loop = loop;
1506
+ this.amp = context.createGain();
1507
+ this.amp.gain.value = 1.0;
1508
+ this.source.connect(this.amp);
1509
+ this.amp.connect(master);
1510
+ this.start = context.currentTime;
1511
+ this.source.playbackRate.value = 1;
1512
+ this.source.start(context.currentTime, offset / 1000);
1513
+ this.source.onended = () => {
1514
+ var _a, _b;
1515
+ this.start = null;
1516
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
1517
+ (_b = this.amp) === null || _b === void 0 ? void 0 : _b.disconnect();
1518
+ };
1519
+ }
1520
+ }
1521
+ pause() {
1522
+ var _a;
1523
+ if (this.buffer !== undefined && this.start !== null) {
1524
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.stop(context.currentTime);
1525
+ const elapsed = (context.currentTime - this.start) % this.buffer.duration * 1000;
1526
+ this.start = null;
1527
+ return elapsed;
1528
+ }
1529
+ }
1384
1530
  }
1385
- function isObject(val) {
1386
- return val !== null && typeof val === 'object';
1531
+
1532
+ function synthesizer(props) {
1533
+ return new Synthesizer(props);
1387
1534
  }
1388
- function isNumber(val) {
1389
- return typeof val === 'number' && !isNaN(val);
1535
+ window.addEventListener('touchstart', initialize, true);
1536
+ window.addEventListener('mousedown', initialize, true);
1537
+ function initialize() {
1538
+ new Synthesizer({ oscillator: { type: 'sine' }, amp: { envelope: { amount: 0, ADSR: [0, 0, 0, 0] } } }).press(440);
1539
+ window.removeEventListener('touchstart', initialize, true);
1540
+ window.removeEventListener('mousedown', initialize, true);
1390
1541
  }
1542
+ const keymap = {
1543
+ 'A0': 27.500, 'A#0': 29.135, 'B0': 30.868,
1544
+ '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,
1545
+ '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,
1546
+ '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,
1547
+ '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,
1548
+ '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,
1549
+ '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,
1550
+ '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,
1551
+ 'C8': 4186.009,
1552
+ };
1553
+ const notemap = {
1554
+ '1m': 4.000, '2n': 2.000, '4n': 1.000, '8n': 0.500, '16n': 0.250, '32n': 0.125,
1555
+ };
1391
1556
  class Synthesizer {
1392
- static initialize() {
1393
- window.addEventListener('touchstart', initialize, true);
1394
- window.addEventListener('mousedown', initialize, true);
1395
- function initialize() {
1396
- new Synthesizer().press(440);
1397
- window.removeEventListener('touchstart', initialize, true);
1398
- window.removeEventListener('mousedown', initialize, true);
1399
- }
1400
- }
1401
- constructor({ oscillator = null, filter = null, amp = null } = {}, { bmp = null, reverb = null, delay = null } = {}) {
1402
- this.oscillator = isObject(oscillator) ? oscillator : {};
1403
- this.oscillator.type = setType(this.oscillator.type, ['sine', 'triangle', 'square', 'sawtooth']);
1404
- this.oscillator.envelope = setEnvelope(this.oscillator.envelope, -36, +36);
1405
- this.oscillator.LFO = setLFO(this.oscillator.LFO, 36);
1406
- this.filter = isObject(filter) ? filter : {};
1407
- this.filter.type = setType(this.filter.type, ['lowpass', 'highpass', 'bandpass']);
1408
- this.filter.Q = isNumber(this.filter.Q) ? clamp(this.filter.Q, 0, 32) : 0;
1409
- // cutoffはundefinedを使う
1410
- this.filter.cutoff = isNumber(this.filter.cutoff) ? clamp(this.filter.cutoff, 4, 8192) : undefined;
1411
- this.filter.envelope = setEnvelope(this.filter.envelope, -36, +36);
1412
- this.filter.LFO = setLFO(this.filter.LFO, 36);
1413
- this.amp = isObject(amp) ? amp : {};
1414
- this.amp.envelope = setEnvelope(this.amp.envelope, 0, 1);
1415
- this.amp.LFO = setLFO(this.amp.LFO, 36);
1416
- this.bmp = isNumber(bmp) ? clamp(bmp, 60, 240) : 120;
1417
- this.options = { bmp: this.bmp };
1418
- this.reverb = isObject(reverb) ? reverb : {};
1419
- this.reverb.time = isNumber(this.reverb.time) ? clamp(this.reverb.time, 0, 2000) : 0.0;
1420
- this.reverb.mix = isNumber(this.reverb.mix) ? clamp(this.reverb.mix, 0, 1.0) : 0.0;
1421
- this.delay = isObject(delay) ? delay : {};
1422
- this.delay.time = isNumber(this.delay.time) ? clamp(this.delay.time, 0, 2000) : 0.0;
1423
- this.delay.feedback = isNumber(this.delay.feedback) ? clamp(this.delay.feedback, 0.0, 0.9) : 0.0;
1424
- this.delay.mix = isNumber(this.delay.mix) ? clamp(this.delay.mix, 0.0, 1.0) : 0.0;
1425
- function setType(type, list, value = 0) {
1426
- return list.includes(type) ? type : list[value];
1427
- }
1428
- function setEnvelope(envelope, minAmount, maxAmount) {
1429
- var _a, _b, _c, _d;
1430
- if (!isObject(envelope))
1431
- return null;
1432
- const result = {
1433
- amount: isNumber(envelope.amount) ? clamp(envelope.amount, minAmount, maxAmount) : 0,
1434
- ADSR: [
1435
- isNumber((_a = envelope.ADSR) === null || _a === void 0 ? void 0 : _a[0]) ? clamp(envelope.ADSR[0], 0, 8000) : 0,
1436
- isNumber((_b = envelope.ADSR) === null || _b === void 0 ? void 0 : _b[1]) ? clamp(envelope.ADSR[1], 0, 8000) : 0,
1437
- isNumber((_c = envelope.ADSR) === null || _c === void 0 ? void 0 : _c[2]) ? clamp(envelope.ADSR[2], 0, 1) : 0,
1438
- isNumber((_d = envelope.ADSR) === null || _d === void 0 ? void 0 : _d[3]) ? clamp(envelope.ADSR[3], 0, 8000) : 0,
1439
- ]
1440
- };
1441
- return result;
1442
- }
1443
- function setLFO(LFO, maxAmount) {
1444
- if (!isObject(LFO))
1445
- return null;
1446
- const oscTypes = ['sine', 'triangle', 'square', 'sawtooth'];
1447
- const type = oscTypes.includes(LFO.type)
1448
- ? LFO.type
1449
- : 'sine';
1450
- const result = {
1451
- amount: isNumber(LFO.amount) ? clamp(LFO.amount, 0, maxAmount) : 0,
1452
- type,
1453
- rate: isNumber(LFO.rate) ? clamp(LFO.rate, 1, 128) : 1,
1454
- };
1455
- return result;
1456
- }
1557
+ constructor(props) {
1558
+ this.props = props;
1457
1559
  }
1458
- press(frequency, duration = null, wait = 0.0) {
1459
- frequency = typeof frequency === 'string' ? Synthesizer.keymap[frequency] : frequency;
1460
- duration = typeof duration === 'string' ? (Synthesizer.notemap[duration] * 60 / this.options.bmp) : (duration !== null ? (duration / 1000) : duration);
1461
- const start = Audio.context.currentTime + wait / 1000;
1462
- let stop = null;
1463
- const params = {};
1464
- if (this.filter.type && this.filter.cutoff) {
1465
- params.oscillator = ['Oscillator', {}, 'filter'];
1466
- params.filter = ['BiquadFilter', {}, 'amp'];
1560
+ press(frequency, duration, wait) {
1561
+ var _a;
1562
+ const props = this.props;
1563
+ const freq = typeof frequency === 'string' ? keymap[frequency] : frequency;
1564
+ const dura = typeof duration === 'string' ? (notemap[duration] * 60 / ((_a = props.bpm) !== null && _a !== void 0 ? _a : 120)) : (typeof duration === 'number' ? (duration / 1000) : 0);
1565
+ const start = context.currentTime + (wait !== null && wait !== void 0 ? wait : 0) / 1000;
1566
+ const nodes = {};
1567
+ nodes.oscillator = context.createOscillator();
1568
+ nodes.oscillator.type = props.oscillator.type;
1569
+ nodes.oscillator.frequency.value = freq;
1570
+ if (props.oscillator.LFO) {
1571
+ nodes.oscillatorLFO = context.createOscillator();
1572
+ nodes.oscillatorLFODepth = context.createGain();
1573
+ nodes.oscillatorLFODepth.gain.value = freq * (Math.pow(2.0, props.oscillator.LFO.amount / 12.0) - 1.0);
1574
+ nodes.oscillatorLFO.type = props.oscillator.LFO.type;
1575
+ nodes.oscillatorLFO.frequency.value = props.oscillator.LFO.rate;
1576
+ nodes.oscillatorLFO.start(start);
1577
+ nodes.oscillatorLFO.connect(nodes.oscillatorLFODepth);
1578
+ nodes.oscillatorLFODepth.connect(nodes.oscillator.frequency);
1579
+ }
1580
+ nodes.amp = context.createGain();
1581
+ nodes.amp.gain.value = 0.0;
1582
+ nodes.target = context.createGain();
1583
+ nodes.target.gain.value = 1.0;
1584
+ nodes.amp.connect(nodes.target);
1585
+ nodes.target.connect(master);
1586
+ if (props.filter && props.filter.type && props.filter.cutoff) {
1587
+ nodes.filter = context.createBiquadFilter();
1588
+ nodes.filter.type = props.filter.type;
1589
+ nodes.filter.frequency.value = props.filter.cutoff;
1590
+ nodes.oscillator.connect(nodes.filter);
1591
+ nodes.filter.connect(nodes.amp);
1467
1592
  }
1468
1593
  else {
1469
- params.oscillator = ['Oscillator', {}, 'amp'];
1470
- }
1471
- params.amp = ['Gain', { gain: 0.0 }, 'target'];
1472
- params.target = ['Gain', { gain: 1.0 }, 'master'];
1473
- if (this.reverb.time > 0.0 && this.reverb.mix > 0.0) {
1474
- params.amp.push('convolver');
1475
- params.convolver = ['Convolver', { buffer: impulseResponse({ time: this.reverb.time }) }, 'convolverDepth'];
1476
- params.convolverDepth = ['Gain', { gain: 1.0 }, 'master'];
1477
- }
1478
- if (this.delay.time > 0.0 && this.delay.mix > 0.0) {
1479
- params.amp.push('delay');
1480
- params.delay = ['Delay', {}, 'delayDepth', 'delayFeedback'];
1481
- params.delayDepth = ['Gain', { gain: 1.0 }, 'master'];
1482
- params.delayFeedback = ['Gain', { gain: this.delay.feedback }, 'delay'];
1483
- }
1484
- if (this.oscillator.LFO) {
1485
- params.oscillatorLFO = ['Oscillator', {}, 'oscillatorLFODepth'];
1486
- params.oscillatorLFODepth = ['Gain', {}, 'oscillator.frequency'];
1487
- }
1488
- if (this.filter.LFO) {
1489
- params.filterLFO = ['Oscillator', {}, 'filterLFODepth'];
1490
- params.filterLFODepth = ['Gain', {}, 'filter.frequency'];
1491
- }
1492
- if (this.amp.LFO) {
1493
- params.ampLFO = ['Oscillator', {}, 'ampLFODepth'];
1494
- params.ampLFODepth = ['Gain', {}, 'amp.gain'];
1495
- }
1496
- const nodes = Audio.connect(params);
1497
- nodes.oscillator.type = this.oscillator.type;
1498
- nodes.oscillator.frequency.value = clamp(frequency, 10.0, 5000.0);
1499
- if (this.filter.type && this.filter.cutoff) {
1500
- nodes.filter.type = this.filter.type;
1501
- nodes.filter.frequency.value = this.filter.cutoff;
1502
- }
1503
- if (this.reverb.time > 0.0 && this.reverb.mix > 0.0) {
1504
- nodes.target.gain.value *= (1.0 - this.reverb.mix);
1505
- nodes.convolverDepth.gain.value *= this.reverb.mix;
1506
- }
1507
- if (this.delay.time > 0.0 && this.delay.mix > 0.0) {
1508
- console.log(this.delay.time / 1000);
1509
- nodes.delay.delayTime.value = this.delay.time / 1000;
1510
- nodes.target.gain.value *= (1.0 - this.delay.mix);
1511
- nodes.delayDepth.gain.value *= this.delay.mix;
1512
- }
1513
- {
1514
- if (this.oscillator.LFO) {
1515
- nodes.oscillatorLFODepth.gain.value = frequency * (Math.pow(2.0, this.oscillator.LFO.amount / 12.0) - 1.0);
1516
- nodes.oscillatorLFO.type = this.oscillator.LFO.type;
1517
- nodes.oscillatorLFO.frequency.value = this.oscillator.LFO.rate;
1518
- nodes.oscillatorLFO.start(start);
1519
- }
1520
- if (this.filter.LFO) {
1521
- nodes.filterLFODepth.gain.value = frequency * (Math.pow(2.0, this.filter.LFO.amount / 12.0) - 1.0);
1522
- nodes.filterLFO.type = this.filter.LFO.type;
1523
- nodes.filterLFO.frequency.value = this.filter.LFO.rate;
1524
- nodes.filterLFO.start(start);
1525
- }
1526
- if (this.amp.LFO) {
1527
- nodes.ampLFODepth.gain.value = this.amp.LFO.amount;
1528
- nodes.ampLFO.type = this.amp.LFO.type;
1529
- nodes.ampLFO.frequency.value = this.amp.LFO.rate;
1530
- nodes.ampLFO.start(start);
1531
- }
1532
- if (this.oscillator.envelope) {
1533
- const amount = frequency * (Math.pow(2.0, this.oscillator.envelope.amount / 12.0) - 1.0);
1534
- startEnvelope(nodes.oscillator.frequency, frequency, amount, this.oscillator.envelope.ADSR);
1535
- }
1536
- if (this.filter.envelope) {
1537
- const amount = this.filter.cutoff * (Math.pow(2.0, this.filter.envelope.amount / 12.0) - 1.0);
1538
- startEnvelope(nodes.filter.frequency, this.filter.cutoff, amount, this.filter.envelope.ADSR);
1539
- }
1540
- if (this.amp.envelope) {
1541
- startEnvelope(nodes.amp.gain, 0.0, this.amp.envelope.amount, this.amp.envelope.ADSR);
1542
- }
1543
- nodes.oscillator.start(start);
1594
+ nodes.oscillator.connect(nodes.amp);
1595
+ }
1596
+ if (props.reverb) {
1597
+ nodes.convolver = context.createConvolver();
1598
+ nodes.convolver.buffer = impulseResponse({ time: props.reverb.time });
1599
+ nodes.convolverDepth = context.createGain();
1600
+ nodes.convolverDepth.gain.value = 1.0;
1601
+ nodes.convolverDepth.gain.value *= props.reverb.mix;
1602
+ nodes.target.gain.value *= (1.0 - props.reverb.mix);
1603
+ nodes.amp.connect(nodes.convolver);
1604
+ nodes.convolver.connect(nodes.convolverDepth);
1605
+ nodes.convolverDepth.connect(master);
1606
+ }
1607
+ if (props.oscillator.envelope) {
1608
+ const amount = freq * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
1609
+ startEnvelope(nodes.oscillator.frequency, freq, amount, props.oscillator.envelope.ADSR);
1610
+ }
1611
+ if (props.amp.envelope) {
1612
+ startEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
1544
1613
  }
1545
- if (duration !== null) {
1546
- release.call(this);
1614
+ let stop = null;
1615
+ nodes.oscillator.start(start);
1616
+ if (dura > 0) {
1617
+ release();
1618
+ }
1619
+ else {
1620
+ return { release };
1547
1621
  }
1548
1622
  function release() {
1549
- duration = duration !== null && duration !== void 0 ? duration : (Audio.context.currentTime - start);
1550
- if (this.amp.envelope) {
1551
- const ADSR = this.amp.envelope.ADSR;
1623
+ const end = dura > 0 ? dura : (context.currentTime - start);
1624
+ if (props.amp.envelope) {
1625
+ const ADSR = props.amp.envelope.ADSR;
1552
1626
  const adsr = [ADSR[0] / 1000, ADSR[1] / 1000, ADSR[2], ADSR[3] / 1000];
1553
- const rate = adsr[0] === 0.0 ? 1.0 : Math.min(duration / adsr[0], 1.0);
1554
- stop = start + Math.max((adsr[0] + adsr[1]) * rate, duration) + adsr[3];
1627
+ const rate = adsr[0] === 0.0 ? 1.0 : Math.min(end / adsr[0], 1.0);
1628
+ stop = start + Math.max((adsr[0] + adsr[1]) * rate, end) + adsr[3];
1555
1629
  }
1556
1630
  else {
1557
- stop = start + duration;
1631
+ stop = start + end;
1558
1632
  }
1559
- if (this.oscillator.LFO) {
1633
+ if (nodes.oscillatorLFO) {
1560
1634
  nodes.oscillatorLFO.stop(stop);
1561
1635
  }
1562
- if (this.amp.LFO) {
1563
- nodes.ampLFO.stop(stop);
1564
- }
1565
- if (this.oscillator.envelope) {
1566
- const amount = frequency * (Math.pow(2.0, this.oscillator.envelope.amount / 12.0) - 1.0);
1567
- stopEnvelope(nodes.oscillator.frequency, frequency, amount, this.oscillator.envelope.ADSR);
1636
+ if (props.oscillator.envelope) {
1637
+ const amount = freq * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
1638
+ stopEnvelope(nodes.oscillator.frequency, freq, amount, props.oscillator.envelope.ADSR);
1568
1639
  }
1569
- if (this.filter.envelope) {
1570
- const amount = this.filter.cutoff * (Math.pow(2.0, this.filter.envelope.amount / 12.0) - 1.0);
1571
- stopEnvelope(nodes.filter.frequency, this.filter.cutoff, amount, this.filter.envelope.ADSR);
1572
- }
1573
- if (this.amp.envelope) {
1574
- stopEnvelope(nodes.amp.gain, 0.0, this.amp.envelope.amount, this.amp.envelope.ADSR);
1640
+ if (props.amp.envelope) {
1641
+ stopEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
1575
1642
  }
1576
1643
  nodes.oscillator.stop(stop);
1577
- }
1578
- function startEnvelope(param, base, amount, ADSR) {
1579
- const adsr = [ADSR[0] / 1000, ADSR[1] / 1000, ADSR[2], ADSR[3] / 1000];
1580
- param.value = base;
1581
- param.setValueAtTime(base, start);
1582
- param.linearRampToValueAtTime(base + amount, start + adsr[0]);
1583
- param.linearRampToValueAtTime(base + amount * adsr[2], start + (adsr[0] + adsr[1]));
1644
+ setTimeout(() => {
1645
+ var _a, _b, _c, _d, _e;
1646
+ nodes.oscillator.disconnect();
1647
+ nodes.amp.disconnect();
1648
+ nodes.target.disconnect();
1649
+ (_a = nodes.oscillatorLFO) === null || _a === void 0 ? void 0 : _a.disconnect();
1650
+ (_b = nodes.oscillatorLFODepth) === null || _b === void 0 ? void 0 : _b.disconnect();
1651
+ (_c = nodes.filter) === null || _c === void 0 ? void 0 : _c.disconnect();
1652
+ (_d = nodes.convolver) === null || _d === void 0 ? void 0 : _d.disconnect();
1653
+ (_e = nodes.convolverDepth) === null || _e === void 0 ? void 0 : _e.disconnect();
1654
+ }, 2000);
1584
1655
  }
1585
1656
  function stopEnvelope(param, base, amount, ADSR) {
1586
- const adsr = [ADSR[0] / 1000, ADSR[1] / 1000, ADSR[2], ADSR[3] / 1000];
1587
- const rate = adsr[0] === 0.0 ? 1.0 : Math.min(duration / adsr[0], 1.0);
1657
+ const rate = ADSR[0] === 0.0 ? 1.0 : Math.min(dura / (ADSR[0] / 1000), 1.0);
1588
1658
  if (rate < 1.0) {
1589
1659
  param.cancelScheduledValues(start);
1590
1660
  param.setValueAtTime(base, start);
1591
- param.linearRampToValueAtTime(base + amount * rate, start + adsr[0] * rate);
1592
- param.linearRampToValueAtTime(base + amount * rate * adsr[2], start + (adsr[0] + adsr[1]) * rate);
1661
+ param.linearRampToValueAtTime(base + amount * rate, start + ADSR[0] / 1000 * rate);
1662
+ param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000 * rate);
1593
1663
  }
1594
- param.linearRampToValueAtTime(base + amount * rate * adsr[2], start + Math.max((adsr[0] + adsr[1]) * rate, duration));
1595
- param.linearRampToValueAtTime(base, start + Math.max((adsr[0] + adsr[1]) * rate, duration) + adsr[3]);
1664
+ param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, dura));
1665
+ param.linearRampToValueAtTime(base, start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, dura) + ADSR[3] / 1000);
1666
+ }
1667
+ function startEnvelope(param, base, amount, ADSR) {
1668
+ param.value = base;
1669
+ param.setValueAtTime(base, start);
1670
+ param.linearRampToValueAtTime(base + amount, start + ADSR[0] / 1000);
1671
+ param.linearRampToValueAtTime(base + amount * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000);
1672
+ }
1673
+ function impulseResponse({ time, decay = 2.0 }) {
1674
+ const length = context.sampleRate * time / 1000;
1675
+ const impulse = context.createBuffer(2, length, context.sampleRate);
1676
+ const ch0 = impulse.getChannelData(0);
1677
+ const ch1 = impulse.getChannelData(1);
1678
+ for (let i = 0; i < length; i++) {
1679
+ ch0[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
1680
+ ch1[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
1681
+ }
1682
+ return impulse;
1596
1683
  }
1597
- return {
1598
- release: release.bind(this),
1599
- };
1600
1684
  }
1601
1685
  }
1602
- Synthesizer.keymap = {
1603
- 'A0': 27.500, 'A#0': 29.135, 'B0': 30.868,
1604
- '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,
1605
- '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,
1606
- '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,
1607
- '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,
1608
- '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,
1609
- '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,
1610
- '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,
1611
- 'C8': 4186.009,
1612
- };
1613
- Synthesizer.notemap = {
1614
- '1m': 4.000, '2n': 2.000, '4n': 1.000, '8n': 0.500, '16n': 0.250, '32n': 0.125,
1615
- };
1616
- Synthesizer.initialize();
1617
- function impulseResponse({ time, decay = 2.0 }) {
1618
- const length = Audio.context.sampleRate * time / 1000;
1619
- const impulse = Audio.context.createBuffer(2, length, Audio.context.sampleRate);
1620
- const ch0 = impulse.getChannelData(0);
1621
- const ch1 = impulse.getChannelData(1);
1622
- for (let i = 0; i < length; i++) {
1623
- ch0[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
1624
- ch1[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
1625
- }
1626
- return impulse;
1627
- }
1628
1686
 
1629
1687
  const basics = {
1630
1688
  Screen,
1631
- UserEvent,
1689
+ PointerEvent,
1632
1690
  ResizeEvent,
1691
+ KeyboardEvent,
1633
1692
  ModalFrame,
1634
1693
  ModalContent,
1635
1694
  AccordionFrame,
@@ -1642,16 +1701,18 @@ const basics = {
1642
1701
  InputFrame,
1643
1702
  DragFrame,
1644
1703
  DragTarget,
1645
- TouchStick,
1646
- TouchDPad,
1647
- TouchButton,
1704
+ AnalogStick,
1705
+ DirectionalPad,
1648
1706
  };
1649
1707
  const audio = {
1650
- synthesizer
1708
+ master,
1709
+ context,
1710
+ synthesizer,
1711
+ load
1651
1712
  };
1652
1713
  const xnew = Object.assign(xnew$1, {
1653
1714
  basics,
1654
- audio,
1715
+ audio
1655
1716
  });
1656
1717
 
1657
1718
  export { xnew as default };