@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.js CHANGED
@@ -104,7 +104,7 @@
104
104
  //----------------------------------------------------------------------------------------------------
105
105
  // ticker
106
106
  //----------------------------------------------------------------------------------------------------
107
- class Ticker {
107
+ class AnimationTicker {
108
108
  constructor(callback, fps = 60) {
109
109
  const self = this;
110
110
  this.id = null;
@@ -135,7 +135,7 @@
135
135
  this.counter = 0;
136
136
  this.offset = 0.0;
137
137
  this.status = 0;
138
- this.ticker = new Ticker((time) => {
138
+ this.ticker = new AnimationTicker((time) => {
139
139
  var _a, _b;
140
140
  let p = Math.min(this.elapsed() / this.options.duration, 1.0);
141
141
  if (this.options.easing === 'ease-out') {
@@ -206,10 +206,323 @@
206
206
  }
207
207
  }
208
208
 
209
- //----------------------------------------------------------------------------------------------------
210
- // utils
211
- //----------------------------------------------------------------------------------------------------
212
- const SYSTEM_EVENTS = ['start', 'process', 'update', 'stop', 'finalize'];
209
+ const SYSTEM_EVENTS = ['start', 'update', 'render', 'stop', 'finalize'];
210
+
211
+ class EventManager {
212
+ constructor() {
213
+ this.map = new MapMap();
214
+ }
215
+ add(props) {
216
+ let finalize;
217
+ if (props.type === 'resize') {
218
+ finalize = this.resize(props);
219
+ }
220
+ else if (props.type === 'wheel') {
221
+ finalize = this.wheel(props);
222
+ }
223
+ else if (props.type === 'click') {
224
+ finalize = this.click(props);
225
+ }
226
+ else if (props.type === 'click.outside') {
227
+ finalize = this.click_outside(props);
228
+ }
229
+ else if (['pointerdown', 'pointermove', 'pointerup', 'pointerover', 'pointerout'].includes(props.type)) {
230
+ finalize = this.pointer(props);
231
+ }
232
+ else if (['pointerdown.outside', 'pointermove.outside', 'pointerup.outside'].includes(props.type)) {
233
+ finalize = this.pointer_outside(props);
234
+ }
235
+ else if (['mousedown', 'mousemove', 'mouseup', 'mouseover', 'mouseout'].includes(props.type)) {
236
+ finalize = this.mouse(props);
237
+ }
238
+ else if (['touchstart', 'touchmove', 'touchend', 'touchcancel'].includes(props.type)) {
239
+ finalize = this.touch(props);
240
+ }
241
+ else if (['dragstart', 'dragmove', 'dragend'].includes(props.type)) {
242
+ finalize = this.drag(props);
243
+ }
244
+ else if (['gesturestart', 'gesturemove', 'gestureend'].includes(props.type)) {
245
+ finalize = this.gesture(props);
246
+ }
247
+ else if (['keydown', 'keyup'].includes(props.type)) {
248
+ finalize = this.key(props);
249
+ }
250
+ else if (['keydown.arrow', 'keyup.arrow'].includes(props.type)) {
251
+ finalize = this.key_arrow(props);
252
+ }
253
+ else {
254
+ finalize = this.basic(props);
255
+ }
256
+ this.map.set(props.type, props.listener, finalize);
257
+ }
258
+ remove({ type, listener }) {
259
+ const finalize = this.map.get(type, listener);
260
+ if (finalize) {
261
+ finalize();
262
+ this.map.delete(type, listener);
263
+ }
264
+ }
265
+ basic(props) {
266
+ const execute = (event) => {
267
+ props.listener({ event, type: event.type });
268
+ };
269
+ props.element.addEventListener(props.type, execute, props.options);
270
+ return () => {
271
+ props.element.removeEventListener(props.type, execute);
272
+ };
273
+ }
274
+ resize(props) {
275
+ const observer = new ResizeObserver((entries) => {
276
+ for (const entry of entries) {
277
+ props.listener({ type: 'resize' });
278
+ break;
279
+ }
280
+ });
281
+ observer.observe(props.element);
282
+ return () => {
283
+ observer.unobserve(props.element);
284
+ };
285
+ }
286
+ click(props) {
287
+ const execute = (event) => {
288
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
289
+ };
290
+ props.element.addEventListener(props.type, execute, props.options);
291
+ return () => {
292
+ props.element.removeEventListener(props.type, execute);
293
+ };
294
+ }
295
+ click_outside(props) {
296
+ const execute = (event) => {
297
+ if (props.element.contains(event.target) === false) {
298
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
299
+ }
300
+ };
301
+ document.addEventListener(props.type.split('.')[0], execute, props.options);
302
+ return () => {
303
+ document.removeEventListener(props.type.split('.')[0], execute);
304
+ };
305
+ }
306
+ pointer(props) {
307
+ const execute = (event) => {
308
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
309
+ };
310
+ props.element.addEventListener(props.type, execute, props.options);
311
+ return () => {
312
+ props.element.removeEventListener(props.type, execute);
313
+ };
314
+ }
315
+ mouse(props) {
316
+ const execute = (event) => {
317
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
318
+ };
319
+ props.element.addEventListener(props.type, execute, props.options);
320
+ return () => {
321
+ props.element.removeEventListener(props.type, execute);
322
+ };
323
+ }
324
+ touch(props) {
325
+ const execute = (event) => {
326
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
327
+ };
328
+ props.element.addEventListener(props.type, execute, props.options);
329
+ return () => {
330
+ props.element.removeEventListener(props.type, execute);
331
+ };
332
+ }
333
+ pointer_outside(props) {
334
+ const execute = (event) => {
335
+ if (props.element.contains(event.target) === false) {
336
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
337
+ }
338
+ };
339
+ document.addEventListener(props.type.split('.')[0], execute, props.options);
340
+ return () => {
341
+ document.removeEventListener(props.type.split('.')[0], execute);
342
+ };
343
+ }
344
+ wheel(props) {
345
+ const execute = (event) => {
346
+ props.listener({ event, type: props.type, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } });
347
+ };
348
+ props.element.addEventListener(props.type, execute, props.options);
349
+ return () => {
350
+ props.element.removeEventListener(props.type, execute);
351
+ };
352
+ }
353
+ drag(props) {
354
+ let pointermove = null;
355
+ let pointerup = null;
356
+ let pointercancel = null;
357
+ const pointerdown = (event) => {
358
+ const id = event.pointerId;
359
+ const position = pointer(props.element, event).position;
360
+ let previous = position;
361
+ pointermove = (event) => {
362
+ if (event.pointerId === id) {
363
+ const position = pointer(props.element, event).position;
364
+ const delta = { x: position.x - previous.x, y: position.y - previous.y };
365
+ if (props.type === 'dragmove') {
366
+ props.listener({ event, type: props.type, position, delta });
367
+ }
368
+ previous = position;
369
+ }
370
+ };
371
+ pointerup = (event) => {
372
+ if (event.pointerId === id) {
373
+ const position = pointer(props.element, event).position;
374
+ if (props.type === 'dragend') {
375
+ props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
376
+ }
377
+ remove();
378
+ }
379
+ };
380
+ pointercancel = (event) => {
381
+ if (event.pointerId === id) {
382
+ const position = pointer(props.element, event).position;
383
+ if (props.type === 'dragend') {
384
+ props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
385
+ }
386
+ remove();
387
+ }
388
+ };
389
+ window.addEventListener('pointermove', pointermove);
390
+ window.addEventListener('pointerup', pointerup);
391
+ window.addEventListener('pointercancel', pointercancel);
392
+ if (props.type === 'dragstart') {
393
+ props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
394
+ }
395
+ };
396
+ function remove() {
397
+ if (pointermove)
398
+ window.removeEventListener('pointermove', pointermove);
399
+ if (pointerup)
400
+ window.removeEventListener('pointerup', pointerup);
401
+ if (pointercancel)
402
+ window.removeEventListener('pointercancel', pointercancel);
403
+ pointermove = null;
404
+ pointerup = null;
405
+ pointercancel = null;
406
+ }
407
+ props.element.addEventListener('pointerdown', pointerdown, props.options);
408
+ return () => {
409
+ props.element.removeEventListener('pointerdown', pointerdown);
410
+ remove();
411
+ };
412
+ }
413
+ gesture(props) {
414
+ let isActive = false;
415
+ const map = new Map();
416
+ const element = props.element;
417
+ const options = props.options;
418
+ const dragstart = ({ event, position }) => {
419
+ map.set(event.pointerId, position);
420
+ isActive = map.size === 2 ? true : false;
421
+ if (isActive === true && props.type === 'gesturestart') {
422
+ props.listener({ event, type: props.type });
423
+ }
424
+ };
425
+ const dragmove = ({ event, position, delta }) => {
426
+ if (map.size >= 2 && isActive === true) {
427
+ const a = map.get(event.pointerId);
428
+ const b = getOthers(event.pointerId)[0];
429
+ let scale = 0.0;
430
+ {
431
+ const v = { x: a.x - b.x, y: a.y - b.y };
432
+ const s = v.x * v.x + v.y * v.y;
433
+ scale = 1 + (s > 0.0 ? (v.x * delta.x + v.y * delta.y) / s : 0);
434
+ }
435
+ // let rotate = 0.0;
436
+ // {
437
+ // const c = { x: a.x + delta.x, y: a.y + delta.y };
438
+ // const v1 = { x: a.x - b.x, y: a.y - b.y };
439
+ // const v2 = { x: c.x - b.x, y: c.y - b.y };
440
+ // const l1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y);
441
+ // const l2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y);
442
+ // if (l1 > 0.0 && l2 > 0.0) {
443
+ // const angle = Math.acos((v1.x * v2.x + v1.y * v2.y) / (l1 * l2));
444
+ // const sign = v1.x * v2.y - v1.y * v2.x;
445
+ // rotate = sign > 0.0 ? +angle : -angle;
446
+ // }
447
+ // }
448
+ if (props.type === 'gesturemove') {
449
+ props.listener({ event, type: props.type, scale });
450
+ }
451
+ }
452
+ map.set(event.pointerId, position);
453
+ };
454
+ const dragend = ({ event }) => {
455
+ map.delete(event.pointerId);
456
+ if (isActive === true && props.type === 'gestureend') {
457
+ props.listener({ event, type: props.type, scale: 1.0 });
458
+ }
459
+ isActive = false;
460
+ };
461
+ this.add({ element, options, type: 'dragstart', listener: dragstart });
462
+ this.add({ element, options, type: 'dragmove', listener: dragmove });
463
+ this.add({ element, options, type: 'dragend', listener: dragend });
464
+ function getOthers(id) {
465
+ const backup = map.get(id);
466
+ map.delete(id);
467
+ const others = [...map.values()];
468
+ map.set(id, backup);
469
+ return others;
470
+ }
471
+ return () => {
472
+ this.remove({ type: 'dragstart', listener: dragstart });
473
+ this.remove({ type: 'dragmove', listener: dragmove });
474
+ this.remove({ type: 'dragend', listener: dragend });
475
+ };
476
+ }
477
+ key(props) {
478
+ const execute = (event) => {
479
+ if (props.type === 'keydown' && event.repeat)
480
+ return;
481
+ props.listener({ event, type: props.type, code: event.code });
482
+ };
483
+ window.addEventListener(props.type, execute, props.options);
484
+ return () => {
485
+ window.removeEventListener(props.type, execute);
486
+ };
487
+ }
488
+ key_arrow(props) {
489
+ const keymap = {};
490
+ const keydown = (event) => {
491
+ if (event.repeat)
492
+ return;
493
+ keymap[event.code] = 1;
494
+ if (props.type === 'keydown.arrow' && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
495
+ const vector = {
496
+ x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
497
+ y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
498
+ };
499
+ props.listener({ event, type: props.type, code: event.code, vector });
500
+ }
501
+ };
502
+ const keyup = (event) => {
503
+ keymap[event.code] = 0;
504
+ if (props.type === 'keyup.arrow' && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
505
+ const vector = {
506
+ x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
507
+ y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
508
+ };
509
+ props.listener({ event, type: props.type, code: event.code, vector });
510
+ }
511
+ };
512
+ window.addEventListener('keydown', keydown, props.options);
513
+ window.addEventListener('keyup', keyup, props.options);
514
+ return () => {
515
+ window.removeEventListener('keydown', keydown);
516
+ window.removeEventListener('keyup', keyup);
517
+ };
518
+ }
519
+ }
520
+ function pointer(element, event) {
521
+ const rect = element.getBoundingClientRect();
522
+ const position = { x: event.clientX - rect.left, y: event.clientY - rect.top };
523
+ return { position };
524
+ }
525
+
213
526
  //----------------------------------------------------------------------------------------------------
214
527
  // unit
215
528
  //----------------------------------------------------------------------------------------------------
@@ -285,8 +598,8 @@
285
598
  }
286
599
  static initialize(unit, anchor) {
287
600
  var _a, _b;
288
- const backup = Unit.current;
289
- Unit.current = unit;
601
+ const backup = Unit.currentUnit;
602
+ Unit.currentUnit = unit;
290
603
  unit._ = Object.assign(unit._, {
291
604
  currentElement: unit._.baseElement,
292
605
  currentContext: unit._.baseContext,
@@ -302,7 +615,8 @@
302
615
  components: [],
303
616
  listeners: new MapMap(),
304
617
  defines: {},
305
- systems: { start: [], process: [], update: [], stop: [], finalize: [] },
618
+ systems: { start: [], update: [], render: [], stop: [], finalize: [] },
619
+ eventManager: new EventManager(),
306
620
  });
307
621
  // nest html element
308
622
  if (typeof unit._.target === 'string') {
@@ -312,13 +626,13 @@
312
626
  Unit.extend(unit, unit._.baseComponent, unit._.props);
313
627
  // whether the unit promise was resolved
314
628
  Promise.all(unit._.promises.map(p => p.promise)).then(() => unit._.state = 'initialized');
315
- Unit.current = backup;
629
+ Unit.currentUnit = backup;
316
630
  }
317
631
  static finalize(unit) {
318
632
  if (unit._.state !== 'finalized' && unit._.state !== 'finalizing') {
319
633
  unit._.state = 'finalizing';
320
634
  unit._.children.forEach((child) => child.finalize());
321
- unit._.systems.finalize.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
635
+ unit._.systems.finalize.forEach(({ execute }) => execute());
322
636
  unit.off();
323
637
  unit._.components.forEach((component) => Unit.component2units.delete(component, unit));
324
638
  if (unit._.elements.length > 0) {
@@ -398,7 +712,7 @@
398
712
  if (unit._.state === 'initialized' || unit._.state === 'stopped') {
399
713
  unit._.state = 'started';
400
714
  unit._.children.forEach((child) => Unit.start(child));
401
- unit._.systems.start.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
715
+ unit._.systems.start.forEach(({ execute }) => execute());
402
716
  }
403
717
  else if (unit._.state === 'started') {
404
718
  unit._.children.forEach((child) => Unit.start(child));
@@ -408,31 +722,31 @@
408
722
  if (unit._.state === 'started') {
409
723
  unit._.state = 'stopped';
410
724
  unit._.children.forEach((child) => Unit.stop(child));
411
- unit._.systems.stop.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
725
+ unit._.systems.stop.forEach(({ execute }) => execute());
412
726
  }
413
727
  }
414
728
  static update(unit) {
415
- if (unit._.state === 'started' || unit._.state === 'stopped') {
729
+ if (unit._.state === 'started') {
416
730
  unit._.children.forEach((child) => Unit.update(child));
417
- unit._.systems.update.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
731
+ unit._.systems.update.forEach(({ execute }) => execute());
418
732
  }
419
733
  }
420
- static process(unit) {
421
- if (unit._.state === 'started') {
422
- unit._.children.forEach((child) => Unit.process(child));
423
- unit._.systems.process.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
734
+ static render(unit) {
735
+ if (unit._.state === 'started' || unit._.state === 'started' || unit._.state === 'stopped') {
736
+ unit._.children.forEach((child) => Unit.render(child));
737
+ unit._.systems.render.forEach(({ execute }) => execute());
424
738
  }
425
739
  }
426
740
  static reset() {
427
- var _a, _b;
428
- (_a = Unit.root) === null || _a === void 0 ? void 0 : _a.finalize();
429
- Unit.current = Unit.root = new Unit(null, null);
430
- (_b = Unit.ticker) === null || _b === void 0 ? void 0 : _b.clear();
431
- Unit.ticker = new Ticker(() => {
432
- Unit.start(Unit.root);
433
- Unit.process(Unit.root);
434
- Unit.update(Unit.root);
741
+ var _a;
742
+ (_a = Unit.rootUnit) === null || _a === void 0 ? void 0 : _a.finalize();
743
+ Unit.currentUnit = Unit.rootUnit = new Unit(null, null);
744
+ const ticker = new AnimationTicker(() => {
745
+ Unit.start(Unit.rootUnit);
746
+ Unit.update(Unit.rootUnit);
747
+ Unit.render(Unit.rootUnit);
435
748
  });
749
+ Unit.rootUnit.on('finalize', () => ticker.clear());
436
750
  }
437
751
  static wrap(unit, listener) {
438
752
  const snapshot = Unit.snapshot(unit);
@@ -442,25 +756,27 @@
442
756
  if (snapshot.unit._.state === 'finalized') {
443
757
  return;
444
758
  }
445
- const current = Unit.current;
759
+ const currentUnit = Unit.currentUnit;
446
760
  const backup = Unit.snapshot(snapshot.unit);
447
761
  try {
448
- Unit.current = snapshot.unit;
762
+ Unit.currentUnit = snapshot.unit;
449
763
  snapshot.unit._.currentContext = snapshot.context;
450
764
  snapshot.unit._.currentElement = snapshot.element;
765
+ snapshot.unit._.currentComponent = snapshot.component;
451
766
  return func(...args);
452
767
  }
453
768
  catch (error) {
454
769
  throw error;
455
770
  }
456
771
  finally {
457
- Unit.current = current;
772
+ Unit.currentUnit = currentUnit;
458
773
  snapshot.unit._.currentContext = backup.context;
459
774
  snapshot.unit._.currentElement = backup.element;
775
+ snapshot.unit._.currentComponent = backup.component;
460
776
  }
461
777
  }
462
778
  static snapshot(unit) {
463
- return { unit, context: unit._.currentContext, element: unit._.currentElement };
779
+ return { unit, context: unit._.currentContext, element: unit._.currentElement, component: unit._.currentComponent };
464
780
  }
465
781
  static context(unit, key, value) {
466
782
  if (value !== undefined) {
@@ -478,43 +794,47 @@
478
794
  return [...((_a = Unit.component2units.get(component)) !== null && _a !== void 0 ? _a : [])];
479
795
  }
480
796
  on(type, listener, options) {
481
- type.trim().split(/\s+/).forEach((type) => {
482
- if (SYSTEM_EVENTS.includes(type)) {
483
- this._.systems[type].push(listener);
484
- }
485
- if (this._.listeners.has(type, listener) === false) {
486
- const execute = Unit.wrap(Unit.current, listener);
487
- this._.listeners.set(type, listener, { element: this.element, execute });
488
- Unit.type2units.add(type, this);
489
- if (/^[A-Za-z]/.test(type)) {
490
- this.element.addEventListener(type, execute, options);
491
- }
492
- }
493
- });
797
+ const types = type.trim().split(/\s+/);
798
+ types.forEach((type) => Unit.on(this, type, listener, options));
494
799
  }
495
800
  off(type, listener) {
496
801
  const types = typeof type === 'string' ? type.trim().split(/\s+/) : [...this._.listeners.keys()];
497
- types.forEach((type) => {
498
- if (SYSTEM_EVENTS.includes(type)) {
499
- this._.systems[type] = this._.systems[type].filter((lis) => listener ? lis !== listener : false);
802
+ types.forEach((type) => Unit.off(this, type, listener));
803
+ }
804
+ static on(unit, type, listener, options) {
805
+ if (SYSTEM_EVENTS.includes(type)) {
806
+ const execute = Unit.wrap(Unit.currentUnit, listener);
807
+ unit._.systems[type].push({ listener, execute });
808
+ }
809
+ if (unit._.listeners.has(type, listener) === false) {
810
+ const execute = Unit.wrap(Unit.currentUnit, listener);
811
+ unit._.listeners.set(type, listener, { element: unit.element, component: unit._.currentComponent, execute });
812
+ Unit.type2units.add(type, unit);
813
+ if (/^[A-Za-z]/.test(type)) {
814
+ unit._.eventManager.add({ element: unit.element, type, listener: execute, options });
500
815
  }
501
- (listener ? [listener] : [...this._.listeners.keys(type)]).forEach((listener) => {
502
- const item = this._.listeners.get(type, listener);
503
- if (item !== undefined) {
504
- this._.listeners.delete(type, listener);
505
- if (/^[A-Za-z]/.test(type)) {
506
- item.element.removeEventListener(type, item.execute);
507
- }
508
- }
509
- });
510
- if (this._.listeners.has(type) === false) {
511
- Unit.type2units.delete(type, this);
816
+ }
817
+ }
818
+ static off(unit, type, listener) {
819
+ if (SYSTEM_EVENTS.includes(type)) {
820
+ unit._.systems[type] = unit._.systems[type].filter(({ listener: lis }) => listener ? lis !== listener : false);
821
+ }
822
+ (listener ? [listener] : [...unit._.listeners.keys(type)]).forEach((listener) => {
823
+ const item = unit._.listeners.get(type, listener);
824
+ if (item === undefined)
825
+ return;
826
+ unit._.listeners.delete(type, listener);
827
+ if (/^[A-Za-z]/.test(type)) {
828
+ unit._.eventManager.remove({ type, listener: item.execute });
512
829
  }
513
830
  });
831
+ if (unit._.listeners.has(type) === false) {
832
+ Unit.type2units.delete(type, unit);
833
+ }
514
834
  }
515
835
  static emit(type, ...args) {
516
836
  var _a, _b;
517
- const current = Unit.current;
837
+ const current = Unit.currentUnit;
518
838
  if (type[0] === '+') {
519
839
  (_a = Unit.type2units.get(type)) === null || _a === void 0 ? void 0 : _a.forEach((unit) => {
520
840
  var _a;
@@ -529,6 +849,7 @@
529
849
  }
530
850
  }
531
851
  }
852
+ Unit.currentComponent = () => { };
532
853
  Unit.component2units = new MapSet();
533
854
  //----------------------------------------------------------------------------------------------------
534
855
  // event
@@ -543,15 +864,15 @@
543
864
  this.component = component;
544
865
  }
545
866
  then(callback) {
546
- this.promise = this.promise.then(Unit.wrap(Unit.current, callback));
867
+ this.promise = this.promise.then(Unit.wrap(Unit.currentUnit, callback));
547
868
  return this;
548
869
  }
549
870
  catch(callback) {
550
- this.promise = this.promise.catch(Unit.wrap(Unit.current, callback));
871
+ this.promise = this.promise.catch(Unit.wrap(Unit.currentUnit, callback));
551
872
  return this;
552
873
  }
553
874
  finally(callback) {
554
- this.promise = this.promise.finally(Unit.wrap(Unit.current, callback));
875
+ this.promise = this.promise.finally(Unit.wrap(Unit.currentUnit, callback));
555
876
  return this;
556
877
  }
557
878
  }
@@ -561,7 +882,7 @@
561
882
  class UnitTimer {
562
883
  constructor(options) {
563
884
  this.stack = [];
564
- this.unit = new Unit(Unit.current, UnitTimer.Component, Object.assign({ snapshot: Unit.snapshot(Unit.current) }, options));
885
+ this.unit = new Unit(Unit.currentUnit, UnitTimer.Component, Object.assign({ snapshot: Unit.snapshot(Unit.currentUnit) }, options));
565
886
  }
566
887
  clear() {
567
888
  this.stack = [];
@@ -581,19 +902,19 @@
581
902
  }
582
903
  static execute(timer, options) {
583
904
  if (timer.unit._.state === 'finalized') {
584
- timer.unit = new Unit(Unit.current, UnitTimer.Component, Object.assign({ snapshot: Unit.snapshot(Unit.current) }, options));
905
+ timer.unit = new Unit(Unit.currentUnit, UnitTimer.Component, Object.assign({ snapshot: Unit.snapshot(Unit.currentUnit) }, options));
585
906
  }
586
907
  else if (timer.stack.length === 0) {
587
- timer.stack.push(Object.assign({ snapshot: Unit.snapshot(Unit.current) }, options));
908
+ timer.stack.push(Object.assign({ snapshot: Unit.snapshot(Unit.currentUnit) }, options));
588
909
  timer.unit.on('finalize', () => { UnitTimer.next(timer); });
589
910
  }
590
911
  else {
591
- timer.stack.push(Object.assign({ snapshot: Unit.snapshot(Unit.current) }, options));
912
+ timer.stack.push(Object.assign({ snapshot: Unit.snapshot(Unit.currentUnit) }, options));
592
913
  }
593
914
  }
594
915
  static next(timer) {
595
916
  if (timer.stack.length > 0) {
596
- timer.unit = new Unit(Unit.current, UnitTimer.Component, timer.stack.shift());
917
+ timer.unit = new Unit(Unit.currentUnit, UnitTimer.Component, timer.stack.shift());
597
918
  timer.unit.on('finalize', () => { UnitTimer.next(timer); });
598
919
  }
599
920
  }
@@ -620,10 +941,10 @@
620
941
  }
621
942
 
622
943
  const xnew$1 = Object.assign(function (...args) {
623
- if (Unit.root === undefined) {
944
+ if (Unit.rootUnit === undefined) {
624
945
  Unit.reset();
625
946
  }
626
- return new Unit(Unit.current, ...args);
947
+ return new Unit(Unit.currentUnit, ...args);
627
948
  }, {
628
949
  /**
629
950
  * Creates a nested HTML/SVG element within the current component
@@ -636,7 +957,7 @@
636
957
  */
637
958
  nest(tag) {
638
959
  try {
639
- return Unit.nest(Unit.current, tag);
960
+ return Unit.nest(Unit.currentUnit, tag);
640
961
  }
641
962
  catch (error) {
642
963
  console.error('xnew.nest(tag: string): ', error);
@@ -654,7 +975,7 @@
654
975
  */
655
976
  extend(component, props) {
656
977
  try {
657
- return Unit.extend(Unit.current, component, props);
978
+ return Unit.extend(Unit.currentUnit, component, props);
658
979
  }
659
980
  catch (error) {
660
981
  console.error('xnew.extend(component: Function, props?: Object): ', error);
@@ -675,7 +996,7 @@
675
996
  */
676
997
  context(key, value = undefined) {
677
998
  try {
678
- return Unit.context(Unit.current, key, value);
999
+ return Unit.context(Unit.currentUnit, key, value);
679
1000
  }
680
1001
  catch (error) {
681
1002
  console.error('xnew.context(key: string, value?: any): ', error);
@@ -691,9 +1012,9 @@
691
1012
  */
692
1013
  promise(promise) {
693
1014
  try {
694
- const component = Unit.current._.currentComponent;
695
- Unit.current._.promises.push(new UnitPromise(promise, component));
696
- return Unit.current._.promises[Unit.current._.promises.length - 1];
1015
+ const component = Unit.currentUnit._.currentComponent;
1016
+ Unit.currentUnit._.promises.push(new UnitPromise(promise, component));
1017
+ return Unit.currentUnit._.promises[Unit.currentUnit._.promises.length - 1];
697
1018
  }
698
1019
  catch (error) {
699
1020
  console.error('xnew.promise(promise: Promise<any>): ', error);
@@ -709,8 +1030,8 @@
709
1030
  */
710
1031
  then(callback) {
711
1032
  try {
712
- const component = Unit.current._.currentComponent;
713
- const promises = Unit.current._.promises;
1033
+ const component = Unit.currentUnit._.currentComponent;
1034
+ const promises = Unit.currentUnit._.promises;
714
1035
  return new UnitPromise(Promise.all(promises.map(p => p.promise)), null)
715
1036
  .then((results) => {
716
1037
  callback(results.filter((_result, index) => promises[index].component !== null && promises[index].component === component));
@@ -730,7 +1051,7 @@
730
1051
  */
731
1052
  catch(callback) {
732
1053
  try {
733
- const promises = Unit.current._.promises;
1054
+ const promises = Unit.currentUnit._.promises;
734
1055
  return new UnitPromise(Promise.all(promises.map(p => p.promise)), null)
735
1056
  .catch(callback);
736
1057
  }
@@ -748,7 +1069,7 @@
748
1069
  */
749
1070
  finally(callback) {
750
1071
  try {
751
- const promises = Unit.current._.promises;
1072
+ const promises = Unit.currentUnit._.promises;
752
1073
  return new UnitPromise(Promise.all(promises.map(p => p.promise)), null)
753
1074
  .finally(callback);
754
1075
  }
@@ -767,7 +1088,7 @@
767
1088
  * }), 1000)
768
1089
  */
769
1090
  scope(callback) {
770
- const snapshot = Unit.snapshot(Unit.current);
1091
+ const snapshot = Unit.snapshot(Unit.currentUnit);
771
1092
  return (...args) => Unit.scope(snapshot, callback, ...args);
772
1093
  },
773
1094
  /**
@@ -838,7 +1159,7 @@
838
1159
  return new UnitTimer({ transition, duration, easing, iterations: 1 });
839
1160
  },
840
1161
  protect() {
841
- Unit.current._.protected = true;
1162
+ Unit.currentUnit._.protected = true;
842
1163
  }
843
1164
  });
844
1165
 
@@ -873,12 +1194,12 @@
873
1194
  }
874
1195
  };
875
1196
  }
876
- function AccordionHeader(header, {} = {}) {
1197
+ function AccordionHeader(unit, {} = {}) {
877
1198
  const internal = xnew$1.context('xnew.accordionframe');
878
1199
  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;">');
879
- header.on('click', () => internal.frame.toggle());
1200
+ unit.on('click', () => internal.frame.toggle());
880
1201
  }
881
- function AccordionBullet(bullet, { type = 'arrow' } = {}) {
1202
+ function AccordionBullet(unit, { type = 'arrow' } = {}) {
882
1203
  const internal = xnew$1.context('xnew.accordionframe');
883
1204
  xnew$1.nest('<div style="display:inline-block; position: relative; width: 0.55em; margin: 0 0.3em;">');
884
1205
  if (type === 'arrow') {
@@ -899,12 +1220,12 @@
899
1220
  });
900
1221
  }
901
1222
  }
902
- function AccordionContent(content, {} = {}) {
1223
+ function AccordionContent(unit, {} = {}) {
903
1224
  const internal = xnew$1.context('xnew.accordionframe');
904
1225
  xnew$1.nest(`<div style="display: ${internal.open ? 'block' : 'none'};">`);
905
1226
  xnew$1.nest('<div style="padding: 0; display: flex; flex-direction: column; box-sizing: border-box;">');
906
1227
  internal.on('-transition', ({ rate }) => {
907
- content.transition({ element: content.element, rate });
1228
+ unit.transition({ element: unit.element, rate });
908
1229
  });
909
1230
  return {
910
1231
  transition({ element, rate }) {
@@ -923,209 +1244,12 @@
923
1244
  };
924
1245
  }
925
1246
 
926
- function ResizeEvent(resize) {
927
- const observer = new ResizeObserver(xnew$1.scope((entries) => {
928
- for (const entry of entries) {
929
- xnew$1.emit('-resize');
930
- break;
931
- }
932
- }));
933
- if (resize.element) {
934
- observer.observe(resize.element);
935
- }
936
- resize.on('finalize', () => {
937
- if (resize.element) {
938
- observer.unobserve(resize.element);
939
- }
940
- });
941
- }
942
- function DirectEvent(unit) {
943
- const state = {};
944
- const keydown = xnew$1.scope((event) => {
945
- if (event.repeat)
946
- return;
947
- state[event.code] = 1;
948
- xnew$1.emit('-keydown', { event, type: '-keydown', code: event.code });
949
- if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
950
- xnew$1.emit('-keydown.arrow', { event, type: '-keydown.arrow', code: event.code, vector: getVector() });
951
- }
952
- });
953
- const keyup = xnew$1.scope((event) => {
954
- state[event.code] = 0;
955
- xnew$1.emit('-keyup', { event, type: '-keyup', code: event.code });
956
- if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
957
- xnew$1.emit('-keyup.arrow', { event, type: '-keyup.arrow', code: event.code, vector: getVector() });
958
- }
959
- });
960
- window.addEventListener('keydown', keydown);
961
- window.addEventListener('keyup', keyup);
962
- unit.on('finalize', () => {
963
- window.removeEventListener('keydown', keydown);
964
- window.removeEventListener('keyup', keyup);
965
- });
966
- function getVector() {
967
- return {
968
- x: (state['ArrowLeft'] ? -1 : 0) + (state['ArrowRight'] ? +1 : 0),
969
- y: (state['ArrowUp'] ? -1 : 0) + (state['ArrowDown'] ? +1 : 0)
970
- };
971
- }
972
- const internal = xnew$1();
973
- internal.on('pointerdown', (event) => xnew$1.emit('-pointerdown', { event, position: getPosition(unit.element, event) }));
974
- internal.on('pointermove', (event) => xnew$1.emit('-pointermove', { event, position: getPosition(unit.element, event) }));
975
- internal.on('pointerup', (event) => xnew$1.emit('-pointerup', { event, position: getPosition(unit.element, event) }));
976
- internal.on('wheel', (event) => xnew$1.emit('-wheel', { event, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } }));
977
- internal.on('click', (event) => xnew$1.emit('-click', { event, position: getPosition(unit.element, event) }));
978
- internal.on('pointerover', (event) => xnew$1.emit('-pointerover', { event, position: getPosition(unit.element, event) }));
979
- internal.on('pointerout', (event) => xnew$1.emit('-pointerout', { event, position: getPosition(unit.element, event) }));
980
- const pointerdownoutside = xnew$1.scope((event) => {
981
- if (unit.element.contains(event.target) === false) {
982
- xnew$1.emit('-pointerdown.outside', { event, position: getPosition(unit.element, event) });
983
- }
984
- });
985
- const pointerupoutside = xnew$1.scope((event) => {
986
- if (unit.element.contains(event.target) === false) {
987
- xnew$1.emit('-pointerup.outside', { event, position: getPosition(unit.element, event) });
988
- }
989
- });
990
- const clickoutside = xnew$1.scope((event) => {
991
- if (unit.element.contains(event.target) === false) {
992
- xnew$1.emit('-click.outside', { event, position: getPosition(unit.element, event) });
993
- }
994
- });
995
- document.addEventListener('pointerdown', pointerdownoutside);
996
- document.addEventListener('pointerup', pointerupoutside);
997
- document.addEventListener('click', clickoutside);
998
- unit.on('finalize', () => {
999
- document.removeEventListener('pointerdown', pointerdownoutside);
1000
- document.removeEventListener('pointerup', pointerupoutside);
1001
- document.removeEventListener('click', clickoutside);
1002
- });
1003
- const drag = xnew$1(DragEvent);
1004
- drag.on('-dragstart', (...args) => xnew$1.emit('-dragstart', ...args));
1005
- drag.on('-dragmove', (...args) => xnew$1.emit('-dragmove', ...args));
1006
- drag.on('-dragend', (...args) => xnew$1.emit('-dragend', ...args));
1007
- drag.on('-dragcancel', (...args) => xnew$1.emit('-dragcancel', ...args));
1008
- const gesture = xnew$1(GestureEvent);
1009
- gesture.on('-gesturestart', (...args) => xnew$1.emit('-gesturestart', ...args));
1010
- gesture.on('-gesturemove', (...args) => xnew$1.emit('-gesturemove', ...args));
1011
- gesture.on('-gestureend', (...args) => xnew$1.emit('-gestureend', ...args));
1012
- gesture.on('-gesturecancel', (...args) => xnew$1.emit('-gesturecancel', ...args));
1013
- }
1014
- function DragEvent(unit) {
1015
- const pointerdown = xnew$1.scope((event) => {
1016
- const id = event.pointerId;
1017
- const position = getPosition(unit.element, event);
1018
- let previous = position;
1019
- let connect = true;
1020
- const pointermove = xnew$1.scope((event) => {
1021
- if (event.pointerId === id) {
1022
- const position = getPosition(unit.element, event);
1023
- const delta = { x: position.x - previous.x, y: position.y - previous.y };
1024
- xnew$1.emit('-dragmove', { event, position, delta });
1025
- previous = position;
1026
- }
1027
- });
1028
- const pointerup = xnew$1.scope((event) => {
1029
- if (event.pointerId === id) {
1030
- const position = getPosition(unit.element, event);
1031
- xnew$1.emit('-dragend', { event, position, });
1032
- remove();
1033
- }
1034
- });
1035
- const pointercancel = xnew$1.scope((event) => {
1036
- if (event.pointerId === id) {
1037
- const position = getPosition(unit.element, event);
1038
- xnew$1.emit('-dragcancel', { event, position, });
1039
- remove();
1040
- }
1041
- });
1042
- window.addEventListener('pointermove', pointermove);
1043
- window.addEventListener('pointerup', pointerup);
1044
- window.addEventListener('pointercancel', pointercancel);
1045
- function remove() {
1046
- if (connect === true) {
1047
- window.removeEventListener('pointermove', pointermove);
1048
- window.removeEventListener('pointerup', pointerup);
1049
- window.removeEventListener('pointercancel', pointercancel);
1050
- connect = false;
1051
- }
1052
- }
1053
- xnew$1((unit) => unit.on('-finalize', remove));
1054
- xnew$1.emit('-dragstart', { event, position });
1055
- });
1056
- unit.on('pointerdown', pointerdown);
1057
- }
1058
- function GestureEvent(unit) {
1059
- const drag = xnew$1(DragEvent);
1060
- let isActive = false;
1061
- const map = new Map();
1062
- drag.on('-dragstart', ({ event, position }) => {
1063
- map.set(event.pointerId, Object.assign({}, position));
1064
- isActive = map.size === 2 ? true : false;
1065
- if (isActive === true) {
1066
- xnew$1.emit('-gesturestart', {});
1067
- }
1068
- });
1069
- drag.on('-dragmove', ({ event, position, delta }) => {
1070
- if (map.size >= 2 && isActive === true) {
1071
- const a = map.get(event.pointerId);
1072
- const b = getOthers(event.pointerId)[0];
1073
- let scale = 0.0;
1074
- {
1075
- const v = { x: a.x - b.x, y: a.y - b.y };
1076
- const s = v.x * v.x + v.y * v.y;
1077
- scale = 1 + (s > 0.0 ? (v.x * delta.x + v.y * delta.y) / s : 0);
1078
- }
1079
- // let rotate = 0.0;
1080
- // {
1081
- // const c = { x: a.x + delta.x, y: a.y + delta.y };
1082
- // const v1 = { x: a.x - b.x, y: a.y - b.y };
1083
- // const v2 = { x: c.x - b.x, y: c.y - b.y };
1084
- // const l1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y);
1085
- // const l2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y);
1086
- // if (l1 > 0.0 && l2 > 0.0) {
1087
- // const angle = Math.acos((v1.x * v2.x + v1.y * v2.y) / (l1 * l2));
1088
- // const sign = v1.x * v2.y - v1.y * v2.x;
1089
- // rotate = sign > 0.0 ? +angle : -angle;
1090
- // }
1091
- // }
1092
- xnew$1.emit('-gesturemove', { event, position, delta, scale });
1093
- }
1094
- map.set(event.pointerId, position);
1095
- });
1096
- drag.on('-dragend', ({ event }) => {
1097
- if (isActive === true) {
1098
- xnew$1.emit('-gestureend', {});
1099
- }
1100
- isActive = false;
1101
- map.delete(event.pointerId);
1102
- });
1103
- drag.on('-dragcancel', ({ event }) => {
1104
- if (isActive === true) {
1105
- xnew$1.emit('-gesturecancel', { event });
1106
- }
1107
- isActive = false;
1108
- map.delete(event.pointerId);
1109
- });
1110
- function getOthers(id) {
1111
- const backup = map.get(id);
1112
- map.delete(id);
1113
- const others = [...map.values()];
1114
- map.set(id, backup);
1115
- return others;
1116
- }
1117
- }
1118
- function getPosition(element, event) {
1119
- const rect = element.getBoundingClientRect();
1120
- return { x: event.clientX - rect.left, y: event.clientY - rect.top };
1121
- }
1122
-
1123
- function Screen(screen, { width = 640, height = 480, fit = 'contain' } = {}) {
1247
+ function Screen(unit, { width = 640, height = 480, fit = 'contain' } = {}) {
1124
1248
  const size = { width, height };
1125
1249
  const wrapper = xnew$1.nest('<div style="position: relative; width: 100%; height: 100%; overflow: hidden;">');
1250
+ unit.on('resize', resize);
1126
1251
  const absolute = xnew$1.nest('<div style="position: absolute; margin: auto; container-type: size; overflow: hidden;">');
1127
1252
  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;">`);
1128
- xnew$1(wrapper, ResizeEvent).on('-resize', resize);
1129
1253
  resize();
1130
1254
  function resize() {
1131
1255
  const aspect = size.width / size.height;
@@ -1266,15 +1390,14 @@
1266
1390
  const absolute = xnew$1.nest(`<div style="position: absolute; top: ${y}px; left: ${x}px;">`);
1267
1391
  xnew$1.context('xnew.dragframe', { frame, absolute });
1268
1392
  }
1269
- function DragTarget(target, {} = {}) {
1393
+ function DragTarget(unit, {} = {}) {
1270
1394
  const { frame, absolute } = xnew$1.context('xnew.dragframe');
1271
- xnew$1.nest('<div>');
1272
- const direct = xnew$1(absolute.parentElement, DirectEvent);
1395
+ const target = xnew$1(absolute.parentElement);
1273
1396
  const current = { x: 0, y: 0 };
1274
1397
  const offset = { x: 0, y: 0 };
1275
1398
  let dragged = false;
1276
- direct.on('-dragstart', ({ event, position }) => {
1277
- if (target.element.contains(event.target) === false)
1399
+ target.on('dragstart', ({ event, position }) => {
1400
+ if (unit.element.contains(event.target) === false)
1278
1401
  return;
1279
1402
  dragged = true;
1280
1403
  offset.x = position.x - parseFloat(absolute.style.left || '0');
@@ -1282,7 +1405,7 @@
1282
1405
  current.x = position.x - offset.x;
1283
1406
  current.y = position.y - offset.y;
1284
1407
  });
1285
- direct.on('-dragmove', ({ event, delta }) => {
1408
+ target.on('dragmove', ({ event, delta }) => {
1286
1409
  if (dragged !== true)
1287
1410
  return;
1288
1411
  current.x += delta.x;
@@ -1290,9 +1413,10 @@
1290
1413
  absolute.style.left = `${current.x}px`;
1291
1414
  absolute.style.top = `${current.y}px`;
1292
1415
  });
1293
- direct.on('-dragcancel -dragend', ({ event }) => {
1416
+ target.on('dragend', ({ event }) => {
1294
1417
  dragged = false;
1295
1418
  });
1419
+ xnew$1.nest('<div>');
1296
1420
  }
1297
1421
 
1298
1422
  //----------------------------------------------------------------------------------------------------
@@ -1301,7 +1425,7 @@
1301
1425
  function SVGTemplate(self, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 2, strokeLinejoin = 'round', fill = null, fillOpacity = 0.8 }) {
1302
1426
  xnew$1.nest(`<svg
1303
1427
  viewBox="0 0 100 100"
1304
- style="position: absolute; width: 100%; height: 100%; pointer-select: none;
1428
+ style="position: absolute; width: 100%; height: 100%; select: none;
1305
1429
  stroke: ${stroke}; stroke-opacity: ${strokeOpacity}; stroke-width: ${strokeWidth}; stroke-linejoin: ${strokeLinejoin};
1306
1430
  ${fill ? `fill: ${fill}; fill-opacity: ${fillOpacity};` : ''}
1307
1431
  ">`);
@@ -1311,7 +1435,7 @@
1311
1435
  const internal = xnew$1((unit) => {
1312
1436
  let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1313
1437
  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;">`);
1314
- xnew$1(outer, ResizeEvent).on('-resize', () => {
1438
+ xnew$1(outer).on('resize', () => {
1315
1439
  newsize = Math.min(outer.clientWidth, outer.clientHeight);
1316
1440
  inner.style.width = `${newsize}px`;
1317
1441
  inner.style.height = `${newsize}px`;
@@ -1327,22 +1451,21 @@
1327
1451
  xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1328
1452
  xnew$1('<circle cx="50" cy="50" r="23">');
1329
1453
  });
1330
- const direct = xnew$1(DirectEvent);
1331
- direct.on('-dragstart', ({ event, position }) => {
1454
+ unit.on('dragstart', ({ event, position }) => {
1332
1455
  const vector = getVector(position);
1333
1456
  target.element.style.filter = 'brightness(90%)';
1334
1457
  target.element.style.left = vector.x * newsize / 4 + 'px';
1335
1458
  target.element.style.top = vector.y * newsize / 4 + 'px';
1336
1459
  xnew$1.emit('-down', { vector });
1337
1460
  });
1338
- direct.on('-dragmove', ({ event, position }) => {
1461
+ unit.on('dragmove', ({ event, position }) => {
1339
1462
  const vector = getVector(position);
1340
1463
  target.element.style.filter = 'brightness(90%)';
1341
1464
  target.element.style.left = vector.x * newsize / 4 + 'px';
1342
1465
  target.element.style.top = vector.y * newsize / 4 + 'px';
1343
1466
  xnew$1.emit('-move', { vector });
1344
1467
  });
1345
- direct.on('-dragend', ({ event }) => {
1468
+ unit.on('dragend', ({ event }) => {
1346
1469
  const vector = { x: 0, y: 0 };
1347
1470
  target.element.style.filter = '';
1348
1471
  target.element.style.left = vector.x * newsize / 4 + 'px';
@@ -1366,7 +1489,7 @@
1366
1489
  const internal = xnew$1((unit) => {
1367
1490
  let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1368
1491
  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;">`);
1369
- xnew$1(outer, ResizeEvent).on('-resize', () => {
1492
+ xnew$1(outer).on('resize', () => {
1370
1493
  newsize = Math.min(outer.clientWidth, outer.clientHeight);
1371
1494
  inner.style.width = `${newsize}px`;
1372
1495
  inner.style.height = `${newsize}px`;
@@ -1394,8 +1517,7 @@
1394
1517
  xnew$1('<polygon points="11 50 20 42 20 58">');
1395
1518
  xnew$1('<polygon points="89 50 80 42 80 58">');
1396
1519
  });
1397
- const direct = xnew$1(DirectEvent);
1398
- direct.on('-dragstart', ({ event, position }) => {
1520
+ unit.on('dragstart', ({ event, position }) => {
1399
1521
  const vector = getVector(position);
1400
1522
  targets[0].element.style.filter = (vector.y < 0) ? 'brightness(90%)' : '';
1401
1523
  targets[1].element.style.filter = (vector.y > 0) ? 'brightness(90%)' : '';
@@ -1403,7 +1525,7 @@
1403
1525
  targets[3].element.style.filter = (vector.x > 0) ? 'brightness(90%)' : '';
1404
1526
  xnew$1.emit('-down', { vector });
1405
1527
  });
1406
- direct.on('-dragmove', ({ event, position }) => {
1528
+ unit.on('dragmove', ({ event, position }) => {
1407
1529
  const vector = getVector(position);
1408
1530
  targets[0].element.style.filter = (vector.y < 0) ? 'brightness(90%)' : '';
1409
1531
  targets[1].element.style.filter = (vector.y > 0) ? 'brightness(90%)' : '';
@@ -1411,7 +1533,7 @@
1411
1533
  targets[3].element.style.filter = (vector.x > 0) ? 'brightness(90%)' : '';
1412
1534
  xnew$1.emit('-move', { vector });
1413
1535
  });
1414
- direct.on('-dragend', ({ event }) => {
1536
+ unit.on('dragend', ({ event }) => {
1415
1537
  const vector = { x: 0, y: 0 };
1416
1538
  targets[0].element.style.filter = '';
1417
1539
  targets[1].element.style.filter = '';
@@ -1463,249 +1585,29 @@
1463
1585
  const index = Math.floor((new Date().getTime() - start) / speed);
1464
1586
  // Display characters up to the current index (fade in)
1465
1587
  for (let i = 0; i < chars.length; i++) {
1466
- if (i <= index) {
1467
- chars[i].element.style.opacity = '1';
1468
- }
1469
- }
1470
- if (state === 0 && index >= text.length) {
1471
- action();
1472
- }
1473
- });
1474
- xnew$1.timeout(() => {
1475
- xnew$1(document.body).on('click wheel', action);
1476
- xnew$1(DirectEvent).on('-keydown', action);
1477
- }, 100);
1478
- function action() {
1479
- if (state === 0) {
1480
- state = 1;
1481
- for (let i = 0; i < chars.length; i++) {
1482
- chars[i].element.style.opacity = '1';
1483
- }
1484
- xnew$1.emit('-complete');
1485
- }
1486
- else if (state === 1) {
1487
- state = 2;
1488
- xnew$1.emit('-next');
1489
- }
1490
- }
1491
- }
1492
-
1493
- const context = window.AudioContext ? new window.AudioContext() : (null);
1494
- const master = context ? context.createGain() : (null);
1495
- if (context) {
1496
- master.gain.value = 0.1;
1497
- master.connect(context.destination);
1498
- }
1499
- class AudioFile {
1500
- constructor(path) {
1501
- this.promise = fetch(path)
1502
- .then((response) => response.arrayBuffer())
1503
- .then((response) => context.decodeAudioData(response))
1504
- .then((response) => { this.buffer = response; })
1505
- .catch(() => {
1506
- console.warn(`"${path}" could not be loaded.`);
1507
- });
1508
- this.amp = context.createGain();
1509
- this.amp.gain.value = 1.0;
1510
- this.amp.connect(master);
1511
- this.fade = context.createGain();
1512
- this.fade.gain.value = 1.0;
1513
- this.fade.connect(this.amp);
1514
- this.source = null;
1515
- this.played = null;
1516
- }
1517
- set volume(value) {
1518
- this.amp.gain.value = value;
1519
- }
1520
- get volume() {
1521
- return this.amp.gain.value;
1522
- }
1523
- play({ offset = 0, fade = 0, loop = false } = {}) {
1524
- if (this.buffer !== undefined && this.played === null) {
1525
- this.source = context.createBufferSource();
1526
- this.source.buffer = this.buffer;
1527
- this.source.loop = loop;
1528
- this.source.connect(this.fade);
1529
- this.played = context.currentTime;
1530
- this.source.playbackRate.value = 1;
1531
- this.source.start(context.currentTime, offset / 1000);
1532
- // Apply fade-in effect if fade duration is specified
1533
- if (fade > 0) {
1534
- this.fade.gain.setValueAtTime(0, context.currentTime);
1535
- this.fade.gain.linearRampToValueAtTime(1.0, context.currentTime + fade / 1000);
1536
- }
1537
- this.source.onended = () => {
1538
- var _a;
1539
- this.played = null;
1540
- (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
1541
- this.source = null;
1542
- };
1543
- }
1544
- }
1545
- pause({ fade = 0 } = {}) {
1546
- var _a, _b;
1547
- if (this.buffer !== undefined && this.played !== null) {
1548
- const elapsed = (context.currentTime - this.played) % this.buffer.duration * 1000;
1549
- // Apply fade-out effect if fade duration is specified
1550
- if (fade > 0) {
1551
- this.fade.gain.setValueAtTime(1.0, context.currentTime);
1552
- this.fade.gain.linearRampToValueAtTime(0, context.currentTime + fade / 1000);
1553
- (_a = this.source) === null || _a === void 0 ? void 0 : _a.stop(context.currentTime + fade / 1000);
1554
- }
1555
- else {
1556
- (_b = this.source) === null || _b === void 0 ? void 0 : _b.stop(context.currentTime);
1557
- }
1558
- this.played = null;
1559
- return elapsed;
1560
- }
1561
- }
1562
- clear() {
1563
- var _a;
1564
- this.amp.disconnect();
1565
- this.fade.disconnect();
1566
- (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
1567
- }
1568
- }
1569
- const keymap = {
1570
- 'A0': 27.500, 'A#0': 29.135, 'B0': 30.868,
1571
- '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,
1572
- '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,
1573
- '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,
1574
- '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,
1575
- '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,
1576
- '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,
1577
- '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,
1578
- 'C8': 4186.009,
1579
- };
1580
- const notemap = {
1581
- '1m': 4.000, '2n': 2.000, '4n': 1.000, '8n': 0.500, '16n': 0.250, '32n': 0.125,
1582
- };
1583
- class Synthesizer {
1584
- constructor(props) { this.props = props; }
1585
- press(frequency, duration, wait) {
1586
- var _a;
1587
- const props = this.props;
1588
- const fv = typeof frequency === 'string' ? keymap[frequency] : frequency;
1589
- const dv = typeof duration === 'string' ? (notemap[duration] * 60 / ((_a = props.bpm) !== null && _a !== void 0 ? _a : 120)) : (typeof duration === 'number' ? (duration / 1000) : 0);
1590
- const start = context.currentTime + (wait !== null && wait !== void 0 ? wait : 0) / 1000;
1591
- const nodes = {};
1592
- nodes.oscillator = context.createOscillator();
1593
- nodes.oscillator.type = props.oscillator.type;
1594
- nodes.oscillator.frequency.value = fv;
1595
- if (props.oscillator.LFO) {
1596
- nodes.oscillatorLFO = context.createOscillator();
1597
- nodes.oscillatorLFODepth = context.createGain();
1598
- nodes.oscillatorLFODepth.gain.value = fv * (Math.pow(2.0, props.oscillator.LFO.amount / 12.0) - 1.0);
1599
- nodes.oscillatorLFO.type = props.oscillator.LFO.type;
1600
- nodes.oscillatorLFO.frequency.value = props.oscillator.LFO.rate;
1601
- nodes.oscillatorLFO.start(start);
1602
- nodes.oscillatorLFO.connect(nodes.oscillatorLFODepth);
1603
- nodes.oscillatorLFODepth.connect(nodes.oscillator.frequency);
1604
- }
1605
- nodes.amp = context.createGain();
1606
- nodes.amp.gain.value = 0.0;
1607
- nodes.target = context.createGain();
1608
- nodes.target.gain.value = 1.0;
1609
- nodes.amp.connect(nodes.target);
1610
- nodes.target.connect(master);
1611
- if (props.filter) {
1612
- nodes.filter = context.createBiquadFilter();
1613
- nodes.filter.type = props.filter.type;
1614
- nodes.filter.frequency.value = props.filter.cutoff;
1615
- nodes.oscillator.connect(nodes.filter);
1616
- nodes.filter.connect(nodes.amp);
1617
- }
1618
- else {
1619
- nodes.oscillator.connect(nodes.amp);
1620
- }
1621
- if (props.reverb) {
1622
- nodes.convolver = context.createConvolver();
1623
- nodes.convolver.buffer = impulseResponse({ time: props.reverb.time });
1624
- nodes.convolverDepth = context.createGain();
1625
- nodes.convolverDepth.gain.value = 1.0;
1626
- nodes.convolverDepth.gain.value *= props.reverb.mix;
1627
- nodes.target.gain.value *= (1.0 - props.reverb.mix);
1628
- nodes.amp.connect(nodes.convolver);
1629
- nodes.convolver.connect(nodes.convolverDepth);
1630
- nodes.convolverDepth.connect(master);
1631
- }
1632
- if (props.oscillator.envelope) {
1633
- const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
1634
- startEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
1635
- }
1636
- if (props.amp.envelope) {
1637
- startEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
1638
- }
1639
- nodes.oscillator.start(start);
1640
- if (dv > 0) {
1641
- release();
1642
- }
1643
- else {
1644
- return { release };
1645
- }
1646
- function release() {
1647
- let stop = null;
1648
- const end = dv > 0 ? dv : (context.currentTime - start);
1649
- if (props.amp.envelope) {
1650
- const ADSR = props.amp.envelope.ADSR;
1651
- const adsr = [ADSR[0] / 1000, ADSR[1] / 1000, ADSR[2], ADSR[3] / 1000];
1652
- const rate = adsr[0] === 0.0 ? 1.0 : Math.min(end / (adsr[0] + 0.001), 1.0);
1653
- stop = start + Math.max((adsr[0] + adsr[1]) * rate, end) + adsr[3];
1654
- }
1655
- else {
1656
- stop = start + end;
1657
- }
1658
- if (nodes.oscillatorLFO) {
1659
- nodes.oscillatorLFO.stop(stop);
1660
- }
1661
- if (props.oscillator.envelope) {
1662
- const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
1663
- stopEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
1664
- }
1665
- if (props.amp.envelope) {
1666
- stopEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
1667
- }
1668
- nodes.oscillator.stop(stop);
1669
- setTimeout(() => {
1670
- var _a, _b, _c, _d, _e;
1671
- nodes.oscillator.disconnect();
1672
- nodes.amp.disconnect();
1673
- nodes.target.disconnect();
1674
- (_a = nodes.oscillatorLFO) === null || _a === void 0 ? void 0 : _a.disconnect();
1675
- (_b = nodes.oscillatorLFODepth) === null || _b === void 0 ? void 0 : _b.disconnect();
1676
- (_c = nodes.filter) === null || _c === void 0 ? void 0 : _c.disconnect();
1677
- (_d = nodes.convolver) === null || _d === void 0 ? void 0 : _d.disconnect();
1678
- (_e = nodes.convolverDepth) === null || _e === void 0 ? void 0 : _e.disconnect();
1679
- }, 2000);
1680
- }
1681
- function stopEnvelope(param, base, amount, ADSR) {
1682
- const end = dv > 0 ? dv : (context.currentTime - start);
1683
- const rate = ADSR[0] === 0.0 ? 1.0 : Math.min(end / (ADSR[0] / 1000), 1.0);
1684
- if (rate < 1.0) {
1685
- param.cancelScheduledValues(start);
1686
- param.setValueAtTime(base, start);
1687
- param.linearRampToValueAtTime(base + amount * rate, start + ADSR[0] / 1000 * rate);
1688
- param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000 * rate);
1588
+ if (i <= index) {
1589
+ chars[i].element.style.opacity = '1';
1689
1590
  }
1690
- param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, dv));
1691
- param.linearRampToValueAtTime(base, start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, end) + ADSR[3] / 1000);
1692
1591
  }
1693
- function startEnvelope(param, base, amount, ADSR) {
1694
- param.value = base;
1695
- param.setValueAtTime(base, start);
1696
- param.linearRampToValueAtTime(base + amount, start + ADSR[0] / 1000);
1697
- param.linearRampToValueAtTime(base + amount * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000);
1592
+ if (state === 0 && index >= text.length) {
1593
+ action();
1698
1594
  }
1699
- function impulseResponse({ time, decay = 2.0 }) {
1700
- const length = context.sampleRate * time / 1000;
1701
- const impulse = context.createBuffer(2, length, context.sampleRate);
1702
- const ch0 = impulse.getChannelData(0);
1703
- const ch1 = impulse.getChannelData(1);
1704
- for (let i = 0; i < length; i++) {
1705
- ch0[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
1706
- ch1[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
1595
+ });
1596
+ xnew$1.timeout(() => {
1597
+ xnew$1(document.body).on('click wheel', action);
1598
+ unit.on('keydown', action);
1599
+ }, 100);
1600
+ function action() {
1601
+ if (state === 0) {
1602
+ state = 1;
1603
+ for (let i = 0; i < chars.length; i++) {
1604
+ chars[i].element.style.opacity = '1';
1707
1605
  }
1708
- return impulse;
1606
+ xnew$1.emit('-complete');
1607
+ }
1608
+ else if (state === 1) {
1609
+ state = 2;
1610
+ xnew$1.emit('-next');
1709
1611
  }
1710
1612
  }
1711
1613
  }
@@ -3352,35 +3254,228 @@
3352
3254
  XMark(unit, props) { icon('XMark', props); },
3353
3255
  };
3354
3256
 
3355
- function VolumeController(unit, {} = {}) {
3356
- 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;">`);
3357
- xnew$1.extend(DirectEvent);
3358
- unit.on('-pointerdown', ({ event }) => event.stopPropagation());
3359
- const slider = xnew$1(`<input type="range" min="0" max="100" value="${master.gain.value * 100}"
3360
- style="display: none; width: calc(96cqw - 100cqh); margin: 0 2cqw; cursor: pointer; pointer-events: auto;"
3361
- >`);
3362
- unit.on('-click:outside', () => slider.element.style.display = 'none');
3363
- const button = xnew$1((button) => {
3364
- xnew$1.nest('<div style="position: relative; width: 100cqh; height: 100cqh; cursor: pointer; pointer-events: auto;">');
3365
- let icon = xnew$1(master.gain.value > 0 ? icons.SpeakerWave : icons.SpeakerXMark);
3366
- return {
3367
- update() {
3368
- icon === null || icon === void 0 ? void 0 : icon.finalize();
3369
- icon = xnew$1(master.gain.value > 0 ? icons.SpeakerWave : icons.SpeakerXMark);
3257
+ const context = window.AudioContext ? new window.AudioContext() : (null);
3258
+ const master = context ? context.createGain() : (null);
3259
+ if (context) {
3260
+ master.gain.value = 0.1;
3261
+ master.connect(context.destination);
3262
+ }
3263
+ class AudioFile {
3264
+ constructor(path) {
3265
+ this.promise = fetch(path)
3266
+ .then((response) => response.arrayBuffer())
3267
+ .then((response) => context.decodeAudioData(response))
3268
+ .then((response) => { this.buffer = response; })
3269
+ .catch(() => {
3270
+ console.warn(`"${path}" could not be loaded.`);
3271
+ });
3272
+ this.amp = context.createGain();
3273
+ this.amp.gain.value = 1.0;
3274
+ this.amp.connect(master);
3275
+ this.fade = context.createGain();
3276
+ this.fade.gain.value = 1.0;
3277
+ this.fade.connect(this.amp);
3278
+ this.source = null;
3279
+ this.played = null;
3280
+ }
3281
+ set volume(value) {
3282
+ this.amp.gain.value = value;
3283
+ }
3284
+ get volume() {
3285
+ return this.amp.gain.value;
3286
+ }
3287
+ play({ offset = 0, fade = 0, loop = false } = {}) {
3288
+ if (this.buffer !== undefined && this.played === null) {
3289
+ this.source = context.createBufferSource();
3290
+ this.source.buffer = this.buffer;
3291
+ this.source.loop = loop;
3292
+ this.source.connect(this.fade);
3293
+ this.played = context.currentTime;
3294
+ this.source.playbackRate.value = 1;
3295
+ this.source.start(context.currentTime, offset / 1000);
3296
+ // Apply fade-in effect if fade duration is specified
3297
+ if (fade > 0) {
3298
+ this.fade.gain.setValueAtTime(0, context.currentTime);
3299
+ this.fade.gain.linearRampToValueAtTime(1.0, context.currentTime + fade / 1000);
3370
3300
  }
3371
- };
3372
- });
3373
- button.on('click', () => slider.element.style.display = slider.element.style.display !== 'none' ? 'none' : 'flex');
3374
- slider.on('input', (event) => {
3375
- master.gain.value = parseFloat(event.target.value) / 100;
3376
- button.update();
3377
- });
3301
+ this.source.onended = () => {
3302
+ var _a;
3303
+ this.played = null;
3304
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
3305
+ this.source = null;
3306
+ };
3307
+ }
3308
+ }
3309
+ pause({ fade = 0 } = {}) {
3310
+ var _a, _b;
3311
+ if (this.buffer !== undefined && this.played !== null) {
3312
+ const elapsed = (context.currentTime - this.played) % this.buffer.duration * 1000;
3313
+ // Apply fade-out effect if fade duration is specified
3314
+ if (fade > 0) {
3315
+ this.fade.gain.setValueAtTime(1.0, context.currentTime);
3316
+ this.fade.gain.linearRampToValueAtTime(0, context.currentTime + fade / 1000);
3317
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.stop(context.currentTime + fade / 1000);
3318
+ }
3319
+ else {
3320
+ (_b = this.source) === null || _b === void 0 ? void 0 : _b.stop(context.currentTime);
3321
+ }
3322
+ this.played = null;
3323
+ return elapsed;
3324
+ }
3325
+ }
3326
+ clear() {
3327
+ var _a;
3328
+ this.amp.disconnect();
3329
+ this.fade.disconnect();
3330
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
3331
+ }
3332
+ }
3333
+ const keymap = {
3334
+ 'A0': 27.500, 'A#0': 29.135, 'B0': 30.868,
3335
+ '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,
3336
+ '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,
3337
+ '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,
3338
+ '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,
3339
+ '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,
3340
+ '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,
3341
+ '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,
3342
+ 'C8': 4186.009,
3343
+ };
3344
+ const notemap = {
3345
+ '1m': 4.000, '2n': 2.000, '4n': 1.000, '8n': 0.500, '16n': 0.250, '32n': 0.125,
3346
+ };
3347
+ class Synthesizer {
3348
+ constructor(props) { this.props = props; }
3349
+ press(frequency, duration, wait) {
3350
+ var _a;
3351
+ const props = this.props;
3352
+ const fv = typeof frequency === 'string' ? keymap[frequency] : frequency;
3353
+ const dv = typeof duration === 'string' ? (notemap[duration] * 60 / ((_a = props.bpm) !== null && _a !== void 0 ? _a : 120)) : (typeof duration === 'number' ? (duration / 1000) : 0);
3354
+ const start = context.currentTime + (wait !== null && wait !== void 0 ? wait : 0) / 1000;
3355
+ const nodes = {};
3356
+ nodes.oscillator = context.createOscillator();
3357
+ nodes.oscillator.type = props.oscillator.type;
3358
+ nodes.oscillator.frequency.value = fv;
3359
+ if (props.oscillator.LFO) {
3360
+ nodes.oscillatorLFO = context.createOscillator();
3361
+ nodes.oscillatorLFODepth = context.createGain();
3362
+ nodes.oscillatorLFODepth.gain.value = fv * (Math.pow(2.0, props.oscillator.LFO.amount / 12.0) - 1.0);
3363
+ nodes.oscillatorLFO.type = props.oscillator.LFO.type;
3364
+ nodes.oscillatorLFO.frequency.value = props.oscillator.LFO.rate;
3365
+ nodes.oscillatorLFO.start(start);
3366
+ nodes.oscillatorLFO.connect(nodes.oscillatorLFODepth);
3367
+ nodes.oscillatorLFODepth.connect(nodes.oscillator.frequency);
3368
+ }
3369
+ nodes.amp = context.createGain();
3370
+ nodes.amp.gain.value = 0.0;
3371
+ nodes.target = context.createGain();
3372
+ nodes.target.gain.value = 1.0;
3373
+ nodes.amp.connect(nodes.target);
3374
+ nodes.target.connect(master);
3375
+ if (props.filter) {
3376
+ nodes.filter = context.createBiquadFilter();
3377
+ nodes.filter.type = props.filter.type;
3378
+ nodes.filter.frequency.value = props.filter.cutoff;
3379
+ nodes.oscillator.connect(nodes.filter);
3380
+ nodes.filter.connect(nodes.amp);
3381
+ }
3382
+ else {
3383
+ nodes.oscillator.connect(nodes.amp);
3384
+ }
3385
+ if (props.reverb) {
3386
+ nodes.convolver = context.createConvolver();
3387
+ nodes.convolver.buffer = impulseResponse({ time: props.reverb.time });
3388
+ nodes.convolverDepth = context.createGain();
3389
+ nodes.convolverDepth.gain.value = 1.0;
3390
+ nodes.convolverDepth.gain.value *= props.reverb.mix;
3391
+ nodes.target.gain.value *= (1.0 - props.reverb.mix);
3392
+ nodes.amp.connect(nodes.convolver);
3393
+ nodes.convolver.connect(nodes.convolverDepth);
3394
+ nodes.convolverDepth.connect(master);
3395
+ }
3396
+ if (props.oscillator.envelope) {
3397
+ const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
3398
+ startEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
3399
+ }
3400
+ if (props.amp.envelope) {
3401
+ startEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
3402
+ }
3403
+ nodes.oscillator.start(start);
3404
+ if (dv > 0) {
3405
+ release();
3406
+ }
3407
+ else {
3408
+ return { release };
3409
+ }
3410
+ function release() {
3411
+ let stop = null;
3412
+ const end = dv > 0 ? dv : (context.currentTime - start);
3413
+ if (props.amp.envelope) {
3414
+ const ADSR = props.amp.envelope.ADSR;
3415
+ const adsr = [ADSR[0] / 1000, ADSR[1] / 1000, ADSR[2], ADSR[3] / 1000];
3416
+ const rate = adsr[0] === 0.0 ? 1.0 : Math.min(end / (adsr[0] + 0.001), 1.0);
3417
+ stop = start + Math.max((adsr[0] + adsr[1]) * rate, end) + adsr[3];
3418
+ }
3419
+ else {
3420
+ stop = start + end;
3421
+ }
3422
+ if (nodes.oscillatorLFO) {
3423
+ nodes.oscillatorLFO.stop(stop);
3424
+ }
3425
+ if (props.oscillator.envelope) {
3426
+ const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
3427
+ stopEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
3428
+ }
3429
+ if (props.amp.envelope) {
3430
+ stopEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
3431
+ }
3432
+ nodes.oscillator.stop(stop);
3433
+ setTimeout(() => {
3434
+ var _a, _b, _c, _d, _e;
3435
+ nodes.oscillator.disconnect();
3436
+ nodes.amp.disconnect();
3437
+ nodes.target.disconnect();
3438
+ (_a = nodes.oscillatorLFO) === null || _a === void 0 ? void 0 : _a.disconnect();
3439
+ (_b = nodes.oscillatorLFODepth) === null || _b === void 0 ? void 0 : _b.disconnect();
3440
+ (_c = nodes.filter) === null || _c === void 0 ? void 0 : _c.disconnect();
3441
+ (_d = nodes.convolver) === null || _d === void 0 ? void 0 : _d.disconnect();
3442
+ (_e = nodes.convolverDepth) === null || _e === void 0 ? void 0 : _e.disconnect();
3443
+ }, 2000);
3444
+ }
3445
+ function stopEnvelope(param, base, amount, ADSR) {
3446
+ const end = dv > 0 ? dv : (context.currentTime - start);
3447
+ const rate = ADSR[0] === 0.0 ? 1.0 : Math.min(end / (ADSR[0] / 1000), 1.0);
3448
+ if (rate < 1.0) {
3449
+ param.cancelScheduledValues(start);
3450
+ param.setValueAtTime(base, start);
3451
+ param.linearRampToValueAtTime(base + amount * rate, start + ADSR[0] / 1000 * rate);
3452
+ param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000 * rate);
3453
+ }
3454
+ param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, dv));
3455
+ param.linearRampToValueAtTime(base, start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, end) + ADSR[3] / 1000);
3456
+ }
3457
+ function startEnvelope(param, base, amount, ADSR) {
3458
+ param.value = base;
3459
+ param.setValueAtTime(base, start);
3460
+ param.linearRampToValueAtTime(base + amount, start + ADSR[0] / 1000);
3461
+ param.linearRampToValueAtTime(base + amount * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000);
3462
+ }
3463
+ function impulseResponse({ time, decay = 2.0 }) {
3464
+ const length = context.sampleRate * time / 1000;
3465
+ const impulse = context.createBuffer(2, length, context.sampleRate);
3466
+ const ch0 = impulse.getChannelData(0);
3467
+ const ch1 = impulse.getChannelData(1);
3468
+ for (let i = 0; i < length; i++) {
3469
+ ch0[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
3470
+ ch1[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
3471
+ }
3472
+ return impulse;
3473
+ }
3474
+ }
3378
3475
  }
3379
3476
 
3380
3477
  const basics = {
3381
3478
  Screen,
3382
- DirectEvent,
3383
- ResizeEvent,
3384
3479
  ModalFrame,
3385
3480
  ModalContent,
3386
3481
  AccordionFrame,
@@ -3395,7 +3490,6 @@
3395
3490
  DragTarget,
3396
3491
  AnalogStick,
3397
3492
  DirectionalPad,
3398
- VolumeController
3399
3493
  };
3400
3494
  const audio = {
3401
3495
  load(path) {