@mulsense/xnew 0.3.6 → 0.4.0

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
@@ -200,10 +200,323 @@ class Timer {
200
200
  }
201
201
  }
202
202
 
203
- //----------------------------------------------------------------------------------------------------
204
- // utils
205
- //----------------------------------------------------------------------------------------------------
206
- const SYSTEM_EVENTS = ['start', 'logicupdate', 'update', 'stop', 'finalize'];
203
+ const SYSTEM_EVENTS = ['start', 'process', 'update', 'stop', 'finalize'];
204
+
205
+ class EventManager {
206
+ constructor() {
207
+ this.map = new MapMap();
208
+ }
209
+ add(props) {
210
+ let finalize;
211
+ if (props.type === 'resize') {
212
+ finalize = this.resize(props);
213
+ }
214
+ else if (props.type === 'wheel') {
215
+ finalize = this.wheel(props);
216
+ }
217
+ else if (props.type === 'click') {
218
+ finalize = this.click(props);
219
+ }
220
+ else if (props.type === 'click.outside') {
221
+ finalize = this.click_outside(props);
222
+ }
223
+ else if (['pointerdown', 'pointermove', 'pointerup', 'pointerover', 'pointerout'].includes(props.type)) {
224
+ finalize = this.pointer(props);
225
+ }
226
+ else if (['pointerdown.outside', 'pointermove.outside', 'pointerup.outside'].includes(props.type)) {
227
+ finalize = this.pointer_outside(props);
228
+ }
229
+ else if (['mousedown', 'mousemove', 'mouseup', 'mouseover', 'mouseout'].includes(props.type)) {
230
+ finalize = this.mouse(props);
231
+ }
232
+ else if (['touchstart', 'touchmove', 'touchend', 'touchcancel'].includes(props.type)) {
233
+ finalize = this.touch(props);
234
+ }
235
+ else if (['dragstart', 'dragmove', 'dragend'].includes(props.type)) {
236
+ finalize = this.drag(props);
237
+ }
238
+ else if (['gesturestart', 'gesturemove', 'gestureend'].includes(props.type)) {
239
+ finalize = this.gesture(props);
240
+ }
241
+ else if (['keydown', 'keyup'].includes(props.type)) {
242
+ finalize = this.key(props);
243
+ }
244
+ else if (['keydown.arrow', 'keyup.arrow'].includes(props.type)) {
245
+ finalize = this.key_arrow(props);
246
+ }
247
+ else {
248
+ finalize = this.basic(props);
249
+ }
250
+ this.map.set(props.type, props.listener, finalize);
251
+ }
252
+ remove({ type, listener }) {
253
+ const finalize = this.map.get(type, listener);
254
+ if (finalize) {
255
+ finalize();
256
+ this.map.delete(type, listener);
257
+ }
258
+ }
259
+ basic(props) {
260
+ const execute = (event) => {
261
+ props.listener({ event, type: event.type });
262
+ };
263
+ props.element.addEventListener(props.type, execute, props.options);
264
+ return () => {
265
+ props.element.removeEventListener(props.type, execute);
266
+ };
267
+ }
268
+ resize(props) {
269
+ const observer = new ResizeObserver(xnew$1.scope((entries) => {
270
+ for (const entry of entries) {
271
+ props.listener({ type: 'resize' });
272
+ break;
273
+ }
274
+ }));
275
+ observer.observe(props.element);
276
+ return () => {
277
+ observer.unobserve(props.element);
278
+ };
279
+ }
280
+ click(props) {
281
+ const execute = (event) => {
282
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
283
+ };
284
+ props.element.addEventListener(props.type, execute, props.options);
285
+ return () => {
286
+ props.element.removeEventListener(props.type, execute);
287
+ };
288
+ }
289
+ click_outside(props) {
290
+ const execute = (event) => {
291
+ if (props.element.contains(event.target) === false) {
292
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
293
+ }
294
+ };
295
+ document.addEventListener(props.type.split('.')[0], execute, props.options);
296
+ return () => {
297
+ document.removeEventListener(props.type.split('.')[0], execute);
298
+ };
299
+ }
300
+ pointer(props) {
301
+ const execute = (event) => {
302
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
303
+ };
304
+ props.element.addEventListener(props.type, execute, props.options);
305
+ return () => {
306
+ props.element.removeEventListener(props.type, execute);
307
+ };
308
+ }
309
+ mouse(props) {
310
+ const execute = (event) => {
311
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
312
+ };
313
+ props.element.addEventListener(props.type, execute, props.options);
314
+ return () => {
315
+ props.element.removeEventListener(props.type, execute);
316
+ };
317
+ }
318
+ touch(props) {
319
+ const execute = (event) => {
320
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
321
+ };
322
+ props.element.addEventListener(props.type, execute, props.options);
323
+ return () => {
324
+ props.element.removeEventListener(props.type, execute);
325
+ };
326
+ }
327
+ pointer_outside(props) {
328
+ const execute = (event) => {
329
+ if (props.element.contains(event.target) === false) {
330
+ props.listener({ event, type: props.type, position: pointer(props.element, event).position });
331
+ }
332
+ };
333
+ document.addEventListener(props.type.split('.')[0], execute, props.options);
334
+ return () => {
335
+ document.removeEventListener(props.type.split('.')[0], execute);
336
+ };
337
+ }
338
+ wheel(props) {
339
+ const execute = (event) => {
340
+ props.listener({ event, type: props.type, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } });
341
+ };
342
+ props.element.addEventListener(props.type, execute, props.options);
343
+ return () => {
344
+ props.element.removeEventListener(props.type, execute);
345
+ };
346
+ }
347
+ drag(props) {
348
+ let pointermove = null;
349
+ let pointerup = null;
350
+ let pointercancel = null;
351
+ const pointerdown = (event) => {
352
+ const id = event.pointerId;
353
+ const position = pointer(props.element, event).position;
354
+ let previous = position;
355
+ pointermove = (event) => {
356
+ if (event.pointerId === id) {
357
+ const position = pointer(props.element, event).position;
358
+ const delta = { x: position.x - previous.x, y: position.y - previous.y };
359
+ if (props.type === 'dragmove') {
360
+ props.listener({ event, type: props.type, position, delta });
361
+ }
362
+ previous = position;
363
+ }
364
+ };
365
+ pointerup = (event) => {
366
+ if (event.pointerId === id) {
367
+ const position = pointer(props.element, event).position;
368
+ if (props.type === 'dragend') {
369
+ props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
370
+ }
371
+ remove();
372
+ }
373
+ };
374
+ pointercancel = (event) => {
375
+ if (event.pointerId === id) {
376
+ const position = pointer(props.element, event).position;
377
+ if (props.type === 'dragend') {
378
+ props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
379
+ }
380
+ remove();
381
+ }
382
+ };
383
+ window.addEventListener('pointermove', pointermove);
384
+ window.addEventListener('pointerup', pointerup);
385
+ window.addEventListener('pointercancel', pointercancel);
386
+ if (props.type === 'dragstart') {
387
+ props.listener({ event, type: props.type, position, delta: { x: 0, y: 0 } });
388
+ }
389
+ };
390
+ function remove() {
391
+ if (pointermove)
392
+ window.removeEventListener('pointermove', pointermove);
393
+ if (pointerup)
394
+ window.removeEventListener('pointerup', pointerup);
395
+ if (pointercancel)
396
+ window.removeEventListener('pointercancel', pointercancel);
397
+ pointermove = null;
398
+ pointerup = null;
399
+ pointercancel = null;
400
+ }
401
+ props.element.addEventListener('pointerdown', pointerdown, props.options);
402
+ return () => {
403
+ props.element.removeEventListener('pointerdown', pointerdown);
404
+ remove();
405
+ };
406
+ }
407
+ gesture(props) {
408
+ let isActive = false;
409
+ const map = new Map();
410
+ const element = props.element;
411
+ const options = props.options;
412
+ const dragstart = ({ event, position }) => {
413
+ map.set(event.pointerId, Object.assign({}, position));
414
+ isActive = map.size === 2 ? true : false;
415
+ if (isActive === true && props.type === 'gesturestart') {
416
+ props.listener({ event, type: props.type });
417
+ }
418
+ };
419
+ const dragmove = ({ event, position, delta }) => {
420
+ if (map.size >= 2 && isActive === true) {
421
+ const a = map.get(event.pointerId);
422
+ const b = getOthers(event.pointerId)[0];
423
+ let scale = 0.0;
424
+ {
425
+ const v = { x: a.x - b.x, y: a.y - b.y };
426
+ const s = v.x * v.x + v.y * v.y;
427
+ scale = 1 + (s > 0.0 ? (v.x * delta.x + v.y * delta.y) / s : 0);
428
+ }
429
+ // let rotate = 0.0;
430
+ // {
431
+ // const c = { x: a.x + delta.x, y: a.y + delta.y };
432
+ // const v1 = { x: a.x - b.x, y: a.y - b.y };
433
+ // const v2 = { x: c.x - b.x, y: c.y - b.y };
434
+ // const l1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y);
435
+ // const l2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y);
436
+ // if (l1 > 0.0 && l2 > 0.0) {
437
+ // const angle = Math.acos((v1.x * v2.x + v1.y * v2.y) / (l1 * l2));
438
+ // const sign = v1.x * v2.y - v1.y * v2.x;
439
+ // rotate = sign > 0.0 ? +angle : -angle;
440
+ // }
441
+ // }
442
+ if (props.type === 'gesturemove') {
443
+ props.listener({ event, type: props.type, scale });
444
+ }
445
+ }
446
+ map.set(event.pointerId, position);
447
+ };
448
+ const dragend = ({ event }) => {
449
+ if (isActive === true) {
450
+ props.listener({ event, type: props.type, scale: 1.0 });
451
+ }
452
+ isActive = false;
453
+ map.delete(event.pointerId);
454
+ };
455
+ this.add({ element, options, type: 'dragstart', listener: dragstart });
456
+ this.add({ element, options, type: 'dragmove', listener: dragmove });
457
+ this.add({ element, options, type: 'dragend', listener: dragend });
458
+ function getOthers(id) {
459
+ const backup = map.get(id);
460
+ map.delete(id);
461
+ const others = [...map.values()];
462
+ map.set(id, backup);
463
+ return others;
464
+ }
465
+ return () => {
466
+ this.remove({ type: 'dragstart', listener: dragstart });
467
+ this.remove({ type: 'dragmove', listener: dragmove });
468
+ this.remove({ type: 'dragend', listener: dragend });
469
+ };
470
+ }
471
+ key(props) {
472
+ const execute = (event) => {
473
+ if (props.type === 'keydown' && event.repeat)
474
+ return;
475
+ props.listener({ event, type: props.type, code: event.code });
476
+ };
477
+ window.addEventListener(props.type, execute, props.options);
478
+ return () => {
479
+ window.removeEventListener(props.type, execute);
480
+ };
481
+ }
482
+ key_arrow(props) {
483
+ const keymap = {};
484
+ const keydown = (event) => {
485
+ if (event.repeat)
486
+ return;
487
+ keymap[event.code] = 1;
488
+ if (props.type === 'keydown.arrow' && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
489
+ const vector = {
490
+ x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
491
+ y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
492
+ };
493
+ props.listener({ event, type: props.type, code: event.code, vector });
494
+ }
495
+ };
496
+ const keyup = (event) => {
497
+ keymap[event.code] = 0;
498
+ if (props.type === 'keyup.arrow' && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
499
+ const vector = {
500
+ x: (keymap['ArrowLeft'] ? -1 : 0) + (keymap['ArrowRight'] ? +1 : 0),
501
+ y: (keymap['ArrowUp'] ? -1 : 0) + (keymap['ArrowDown'] ? +1 : 0)
502
+ };
503
+ props.listener({ event, type: props.type, code: event.code, vector });
504
+ }
505
+ };
506
+ window.addEventListener('keydown', keydown, props.options);
507
+ window.addEventListener('keyup', keyup, props.options);
508
+ return () => {
509
+ window.removeEventListener('keydown', keydown);
510
+ window.removeEventListener('keyup', keyup);
511
+ };
512
+ }
513
+ }
514
+ function pointer(element, event) {
515
+ const rect = element.getBoundingClientRect();
516
+ const position = { x: event.clientX - rect.left, y: event.clientY - rect.top };
517
+ return { position };
518
+ }
519
+
207
520
  //----------------------------------------------------------------------------------------------------
208
521
  // unit
209
522
  //----------------------------------------------------------------------------------------------------
@@ -296,7 +609,8 @@ class Unit {
296
609
  components: [],
297
610
  listeners: new MapMap(),
298
611
  defines: {},
299
- systems: { start: [], logicupdate: [], update: [], stop: [], finalize: [] },
612
+ systems: { start: [], process: [], update: [], stop: [], finalize: [] },
613
+ eventManager: new EventManager(),
300
614
  });
301
615
  // nest html element
302
616
  if (typeof unit._.target === 'string') {
@@ -406,15 +720,15 @@ class Unit {
406
720
  }
407
721
  }
408
722
  static update(unit) {
409
- if (unit._.state === 'started') {
723
+ if (unit._.state === 'started' || unit._.state === 'stopped') {
410
724
  unit._.children.forEach((child) => Unit.update(child));
411
725
  unit._.systems.update.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
412
726
  }
413
727
  }
414
- static logicupdate(unit) {
728
+ static process(unit) {
415
729
  if (unit._.state === 'started') {
416
- unit._.children.forEach((child) => Unit.logicupdate(child));
417
- unit._.systems.logicupdate.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
730
+ unit._.children.forEach((child) => Unit.process(child));
731
+ unit._.systems.process.forEach((listener) => Unit.scope(Unit.snapshot(unit), listener));
418
732
  }
419
733
  }
420
734
  static reset() {
@@ -424,7 +738,7 @@ class Unit {
424
738
  (_b = Unit.ticker) === null || _b === void 0 ? void 0 : _b.clear();
425
739
  Unit.ticker = new Ticker(() => {
426
740
  Unit.start(Unit.root);
427
- Unit.logicupdate(Unit.root);
741
+ Unit.process(Unit.root);
428
742
  Unit.update(Unit.root);
429
743
  });
430
744
  }
@@ -481,7 +795,7 @@ class Unit {
481
795
  this._.listeners.set(type, listener, { element: this.element, execute });
482
796
  Unit.type2units.add(type, this);
483
797
  if (/^[A-Za-z]/.test(type)) {
484
- this.element.addEventListener(type, execute, options);
798
+ this._.eventManager.add({ element: this.element, type, listener: execute, options });
485
799
  }
486
800
  }
487
801
  });
@@ -494,11 +808,11 @@ class Unit {
494
808
  }
495
809
  (listener ? [listener] : [...this._.listeners.keys(type)]).forEach((listener) => {
496
810
  const item = this._.listeners.get(type, listener);
497
- if (item !== undefined) {
498
- this._.listeners.delete(type, listener);
499
- if (/^[A-Za-z]/.test(type)) {
500
- item.element.removeEventListener(type, item.execute);
501
- }
811
+ if (item === undefined)
812
+ return;
813
+ this._.listeners.delete(type, listener);
814
+ if (/^[A-Za-z]/.test(type)) {
815
+ this._.eventManager.remove({ type, listener: item.execute });
502
816
  }
503
817
  });
504
818
  if (this._.listeners.has(type) === false) {
@@ -917,279 +1231,12 @@ function AccordionContent(content, {} = {}) {
917
1231
  };
918
1232
  }
919
1233
 
920
- function ResizeEvent(resize) {
921
- const observer = new ResizeObserver(xnew$1.scope((entries) => {
922
- for (const entry of entries) {
923
- xnew$1.emit('-resize');
924
- break;
925
- }
926
- }));
927
- if (resize.element) {
928
- observer.observe(resize.element);
929
- }
930
- resize.on('finalize', () => {
931
- if (resize.element) {
932
- observer.unobserve(resize.element);
933
- }
934
- });
935
- }
936
- function DirectEvent(unit) {
937
- const state = {};
938
- const keydown = xnew$1.scope((event) => {
939
- state[event.code] = 1;
940
- xnew$1.emit('-keydown', { event, type: '-keydown', code: event.code });
941
- if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
942
- xnew$1.emit('-keydown.arrow', { event, type: '-keydown.arrow', code: event.code, vector: getVector() });
943
- }
944
- });
945
- const keyup = xnew$1.scope((event) => {
946
- state[event.code] = 0;
947
- xnew$1.emit('-keyup', { event, type: '-keyup', code: event.code });
948
- if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
949
- xnew$1.emit('-keyup.arrow', { event, type: '-keyup.arrow', code: event.code, vector: getVector() });
950
- }
951
- });
952
- window.addEventListener('keydown', keydown);
953
- window.addEventListener('keyup', keyup);
954
- unit.on('finalize', () => {
955
- window.removeEventListener('keydown', keydown);
956
- window.removeEventListener('keyup', keyup);
957
- });
958
- function getVector() {
959
- return {
960
- x: (state['ArrowLeft'] ? -1 : 0) + (state['ArrowRight'] ? +1 : 0),
961
- y: (state['ArrowUp'] ? -1 : 0) + (state['ArrowDown'] ? +1 : 0)
962
- };
963
- }
964
- const internal = xnew$1();
965
- internal.on('pointerdown', (event) => xnew$1.emit('-pointerdown', { event, position: getPosition(unit.element, event) }));
966
- internal.on('pointermove', (event) => xnew$1.emit('-pointermove', { event, position: getPosition(unit.element, event) }));
967
- internal.on('pointerup', (event) => xnew$1.emit('-pointerup', { event, position: getPosition(unit.element, event) }));
968
- internal.on('wheel', (event) => xnew$1.emit('-wheel', { event, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } }));
969
- internal.on('click', (event) => xnew$1.emit('-click', { event, position: getPosition(unit.element, event) }));
970
- internal.on('pointerover', (event) => xnew$1.emit('-pointerover', { event, position: getPosition(unit.element, event) }));
971
- internal.on('pointerout', (event) => xnew$1.emit('-pointerout', { event, position: getPosition(unit.element, event) }));
972
- const pointerdownoutside = xnew$1.scope((event) => {
973
- if (unit.element.contains(event.target) === false) {
974
- xnew$1.emit('-pointerdown.outside', { event, position: getPosition(unit.element, event) });
975
- }
976
- });
977
- const pointerupoutside = xnew$1.scope((event) => {
978
- if (unit.element.contains(event.target) === false) {
979
- xnew$1.emit('-pointerup.outside', { event, position: getPosition(unit.element, event) });
980
- }
981
- });
982
- const clickoutside = xnew$1.scope((event) => {
983
- if (unit.element.contains(event.target) === false) {
984
- xnew$1.emit('-click.outside', { event, position: getPosition(unit.element, event) });
985
- }
986
- });
987
- document.addEventListener('pointerdown', pointerdownoutside);
988
- document.addEventListener('pointerup', pointerupoutside);
989
- document.addEventListener('click', clickoutside);
990
- unit.on('finalize', () => {
991
- document.removeEventListener('pointerdown', pointerdownoutside);
992
- document.removeEventListener('pointerup', pointerupoutside);
993
- document.removeEventListener('click', clickoutside);
994
- });
995
- const drag = xnew$1(DragEvent);
996
- drag.on('-dragstart', (...args) => xnew$1.emit('-dragstart', ...args));
997
- drag.on('-dragmove', (...args) => xnew$1.emit('-dragmove', ...args));
998
- drag.on('-dragend', (...args) => xnew$1.emit('-dragend', ...args));
999
- drag.on('-dragcancel', (...args) => xnew$1.emit('-dragcancel', ...args));
1000
- const gesture = xnew$1(GestureEvent);
1001
- gesture.on('-gesturestart', (...args) => xnew$1.emit('-gesturestart', ...args));
1002
- gesture.on('-gesturemove', (...args) => xnew$1.emit('-gesturemove', ...args));
1003
- gesture.on('-gestureend', (...args) => xnew$1.emit('-gestureend', ...args));
1004
- gesture.on('-gesturecancel', (...args) => xnew$1.emit('-gesturecancel', ...args));
1005
- }
1006
- function KeyboardEvent(keyboard) {
1007
- const state = {};
1008
- const keydown = xnew$1.scope((event) => {
1009
- state[event.code] = 1;
1010
- xnew$1.emit('-keydown', { event, type: '-keydown', code: event.code });
1011
- if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
1012
- xnew$1.emit('-keydown.arrow', { event, type: '-keydown.arrow', code: event.code, vector: getVector() });
1013
- }
1014
- });
1015
- const keyup = xnew$1.scope((event) => {
1016
- state[event.code] = 0;
1017
- xnew$1.emit('-keyup', { event, type: '-keyup', code: event.code });
1018
- if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.code)) {
1019
- xnew$1.emit('-keyup.arrow', { event, type: '-keyup.arrow', code: event.code, vector: getVector() });
1020
- }
1021
- });
1022
- window.addEventListener('keydown', keydown);
1023
- window.addEventListener('keyup', keyup);
1024
- keyboard.on('finalize', () => {
1025
- window.removeEventListener('keydown', keydown);
1026
- window.removeEventListener('keyup', keyup);
1027
- });
1028
- function getVector() {
1029
- return {
1030
- x: (state['ArrowLeft'] ? -1 : 0) + (state['ArrowRight'] ? +1 : 0),
1031
- y: (state['ArrowUp'] ? -1 : 0) + (state['ArrowDown'] ? +1 : 0)
1032
- };
1033
- }
1034
- }
1035
- function PointerEvent(unit) {
1036
- const internal = xnew$1();
1037
- internal.on('pointerdown', (event) => xnew$1.emit('-pointerdown', { event, position: getPosition(unit.element, event) }));
1038
- internal.on('pointermove', (event) => xnew$1.emit('-pointermove', { event, position: getPosition(unit.element, event) }));
1039
- internal.on('pointerup', (event) => xnew$1.emit('-pointerup', { event, position: getPosition(unit.element, event) }));
1040
- internal.on('wheel', (event) => xnew$1.emit('-wheel', { event, delta: { x: event.wheelDeltaX, y: event.wheelDeltaY } }));
1041
- internal.on('click', (event) => xnew$1.emit('-click', { event, position: getPosition(unit.element, event) }));
1042
- internal.on('pointerover', (event) => xnew$1.emit('-pointerover', { event, position: getPosition(unit.element, event) }));
1043
- internal.on('pointerout', (event) => xnew$1.emit('-pointerout', { event, position: getPosition(unit.element, event) }));
1044
- const pointerdownoutside = xnew$1.scope((event) => {
1045
- if (unit.element.contains(event.target) === false) {
1046
- xnew$1.emit('-pointerdown.outside', { event, position: getPosition(unit.element, event) });
1047
- }
1048
- });
1049
- const pointerupoutside = xnew$1.scope((event) => {
1050
- if (unit.element.contains(event.target) === false) {
1051
- xnew$1.emit('-pointerup.outside', { event, position: getPosition(unit.element, event) });
1052
- }
1053
- });
1054
- const clickoutside = xnew$1.scope((event) => {
1055
- if (unit.element.contains(event.target) === false) {
1056
- xnew$1.emit('-click.outside', { event, position: getPosition(unit.element, event) });
1057
- }
1058
- });
1059
- document.addEventListener('pointerdown', pointerdownoutside);
1060
- document.addEventListener('pointerup', pointerupoutside);
1061
- document.addEventListener('click', clickoutside);
1062
- unit.on('finalize', () => {
1063
- document.removeEventListener('pointerdown', pointerdownoutside);
1064
- document.removeEventListener('pointerup', pointerupoutside);
1065
- document.removeEventListener('click', clickoutside);
1066
- });
1067
- const drag = xnew$1(DragEvent);
1068
- drag.on('-dragstart', (...args) => xnew$1.emit('-dragstart', ...args));
1069
- drag.on('-dragmove', (...args) => xnew$1.emit('-dragmove', ...args));
1070
- drag.on('-dragend', (...args) => xnew$1.emit('-dragend', ...args));
1071
- drag.on('-dragcancel', (...args) => xnew$1.emit('-dragcancel', ...args));
1072
- const gesture = xnew$1(GestureEvent);
1073
- gesture.on('-gesturestart', (...args) => xnew$1.emit('-gesturestart', ...args));
1074
- gesture.on('-gesturemove', (...args) => xnew$1.emit('-gesturemove', ...args));
1075
- gesture.on('-gestureend', (...args) => xnew$1.emit('-gestureend', ...args));
1076
- gesture.on('-gesturecancel', (...args) => xnew$1.emit('-gesturecancel', ...args));
1077
- }
1078
- function DragEvent(unit) {
1079
- const pointerdown = xnew$1.scope((event) => {
1080
- const id = event.pointerId;
1081
- const position = getPosition(unit.element, event);
1082
- let previous = position;
1083
- let connect = true;
1084
- const pointermove = xnew$1.scope((event) => {
1085
- if (event.pointerId === id) {
1086
- const position = getPosition(unit.element, event);
1087
- const delta = { x: position.x - previous.x, y: position.y - previous.y };
1088
- xnew$1.emit('-dragmove', { event, position, delta });
1089
- previous = position;
1090
- }
1091
- });
1092
- const pointerup = xnew$1.scope((event) => {
1093
- if (event.pointerId === id) {
1094
- const position = getPosition(unit.element, event);
1095
- xnew$1.emit('-dragend', { event, position, });
1096
- remove();
1097
- }
1098
- });
1099
- const pointercancel = xnew$1.scope((event) => {
1100
- if (event.pointerId === id) {
1101
- const position = getPosition(unit.element, event);
1102
- xnew$1.emit('-dragcancel', { event, position, });
1103
- remove();
1104
- }
1105
- });
1106
- window.addEventListener('pointermove', pointermove);
1107
- window.addEventListener('pointerup', pointerup);
1108
- window.addEventListener('pointercancel', pointercancel);
1109
- function remove() {
1110
- if (connect === true) {
1111
- window.removeEventListener('pointermove', pointermove);
1112
- window.removeEventListener('pointerup', pointerup);
1113
- window.removeEventListener('pointercancel', pointercancel);
1114
- connect = false;
1115
- }
1116
- }
1117
- xnew$1((unit) => unit.on('-finalize', remove));
1118
- xnew$1.emit('-dragstart', { event, position });
1119
- });
1120
- unit.on('pointerdown', pointerdown);
1121
- }
1122
- function GestureEvent(unit) {
1123
- const drag = xnew$1(DragEvent);
1124
- let isActive = false;
1125
- const map = new Map();
1126
- drag.on('-dragstart', ({ event, position }) => {
1127
- map.set(event.pointerId, Object.assign({}, position));
1128
- isActive = map.size === 2 ? true : false;
1129
- if (isActive === true) {
1130
- xnew$1.emit('-gesturestart', {});
1131
- }
1132
- });
1133
- drag.on('-dragmove', ({ event, position, delta }) => {
1134
- if (map.size >= 2 && isActive === true) {
1135
- const a = map.get(event.pointerId);
1136
- const b = getOthers(event.pointerId)[0];
1137
- let scale = 0.0;
1138
- {
1139
- const v = { x: a.x - b.x, y: a.y - b.y };
1140
- const s = v.x * v.x + v.y * v.y;
1141
- scale = 1 + (s > 0.0 ? (v.x * delta.x + v.y * delta.y) / s : 0);
1142
- }
1143
- // let rotate = 0.0;
1144
- // {
1145
- // const c = { x: a.x + delta.x, y: a.y + delta.y };
1146
- // const v1 = { x: a.x - b.x, y: a.y - b.y };
1147
- // const v2 = { x: c.x - b.x, y: c.y - b.y };
1148
- // const l1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y);
1149
- // const l2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y);
1150
- // if (l1 > 0.0 && l2 > 0.0) {
1151
- // const angle = Math.acos((v1.x * v2.x + v1.y * v2.y) / (l1 * l2));
1152
- // const sign = v1.x * v2.y - v1.y * v2.x;
1153
- // rotate = sign > 0.0 ? +angle : -angle;
1154
- // }
1155
- // }
1156
- xnew$1.emit('-gesturemove', { event, position, delta, scale });
1157
- }
1158
- map.set(event.pointerId, position);
1159
- });
1160
- drag.on('-dragend', ({ event }) => {
1161
- if (isActive === true) {
1162
- xnew$1.emit('-gestureend', {});
1163
- }
1164
- isActive = false;
1165
- map.delete(event.pointerId);
1166
- });
1167
- drag.on('-dragcancel', ({ event }) => {
1168
- if (isActive === true) {
1169
- xnew$1.emit('-gesturecancel', { event });
1170
- }
1171
- isActive = false;
1172
- map.delete(event.pointerId);
1173
- });
1174
- function getOthers(id) {
1175
- const backup = map.get(id);
1176
- map.delete(id);
1177
- const others = [...map.values()];
1178
- map.set(id, backup);
1179
- return others;
1180
- }
1181
- }
1182
- function getPosition(element, event) {
1183
- const rect = element.getBoundingClientRect();
1184
- return { x: event.clientX - rect.left, y: event.clientY - rect.top };
1185
- }
1186
-
1187
- function Screen(screen, { width = 640, height = 480, fit = 'contain' } = {}) {
1234
+ function Screen(unit, { width = 640, height = 480, fit = 'contain' } = {}) {
1188
1235
  const size = { width, height };
1189
1236
  const wrapper = xnew$1.nest('<div style="position: relative; width: 100%; height: 100%; overflow: hidden;">');
1237
+ unit.on('resize', resize);
1190
1238
  const absolute = xnew$1.nest('<div style="position: absolute; margin: auto; container-type: size; overflow: hidden;">');
1191
1239
  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;">`);
1192
- xnew$1(wrapper, ResizeEvent).on('-resize', resize);
1193
1240
  resize();
1194
1241
  function resize() {
1195
1242
  const aspect = size.width / size.height;
@@ -1330,15 +1377,14 @@ function DragFrame(frame, { x = 0, y = 0 } = {}) {
1330
1377
  const absolute = xnew$1.nest(`<div style="position: absolute; top: ${y}px; left: ${x}px;">`);
1331
1378
  xnew$1.context('xnew.dragframe', { frame, absolute });
1332
1379
  }
1333
- function DragTarget(target, {} = {}) {
1380
+ function DragTarget(unit, {} = {}) {
1334
1381
  const { frame, absolute } = xnew$1.context('xnew.dragframe');
1335
- xnew$1.nest('<div>');
1336
- const pointer = xnew$1(absolute.parentElement, PointerEvent);
1382
+ const target = xnew$1(absolute.parentElement);
1337
1383
  const current = { x: 0, y: 0 };
1338
1384
  const offset = { x: 0, y: 0 };
1339
1385
  let dragged = false;
1340
- pointer.on('-dragstart', ({ event, position }) => {
1341
- if (target.element.contains(event.target) === false)
1386
+ target.on('dragstart', ({ event, position }) => {
1387
+ if (unit.element.contains(event.target) === false)
1342
1388
  return;
1343
1389
  dragged = true;
1344
1390
  offset.x = position.x - parseFloat(absolute.style.left || '0');
@@ -1346,7 +1392,7 @@ function DragTarget(target, {} = {}) {
1346
1392
  current.x = position.x - offset.x;
1347
1393
  current.y = position.y - offset.y;
1348
1394
  });
1349
- pointer.on('-dragmove', ({ event, delta }) => {
1395
+ target.on('dragmove', ({ event, delta }) => {
1350
1396
  if (dragged !== true)
1351
1397
  return;
1352
1398
  current.x += delta.x;
@@ -1354,9 +1400,10 @@ function DragTarget(target, {} = {}) {
1354
1400
  absolute.style.left = `${current.x}px`;
1355
1401
  absolute.style.top = `${current.y}px`;
1356
1402
  });
1357
- pointer.on('-dragcancel -dragend', ({ event }) => {
1403
+ target.on('dragend', ({ event }) => {
1358
1404
  dragged = false;
1359
1405
  });
1406
+ xnew$1.nest('<div>');
1360
1407
  }
1361
1408
 
1362
1409
  //----------------------------------------------------------------------------------------------------
@@ -1365,7 +1412,7 @@ function DragTarget(target, {} = {}) {
1365
1412
  function SVGTemplate(self, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 2, strokeLinejoin = 'round', fill = null, fillOpacity = 0.8 }) {
1366
1413
  xnew$1.nest(`<svg
1367
1414
  viewBox="0 0 100 100"
1368
- style="position: absolute; width: 100%; height: 100%; pointer-select: none;
1415
+ style="position: absolute; width: 100%; height: 100%; select: none;
1369
1416
  stroke: ${stroke}; stroke-opacity: ${strokeOpacity}; stroke-width: ${strokeWidth}; stroke-linejoin: ${strokeLinejoin};
1370
1417
  ${fill ? `fill: ${fill}; fill-opacity: ${fillOpacity};` : ''}
1371
1418
  ">`);
@@ -1375,7 +1422,7 @@ function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strok
1375
1422
  const internal = xnew$1((unit) => {
1376
1423
  let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1377
1424
  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;">`);
1378
- xnew$1(outer, ResizeEvent).on('-resize', () => {
1425
+ xnew$1(outer).on('resize', () => {
1379
1426
  newsize = Math.min(outer.clientWidth, outer.clientHeight);
1380
1427
  inner.style.width = `${newsize}px`;
1381
1428
  inner.style.height = `${newsize}px`;
@@ -1391,22 +1438,21 @@ function AnalogStick(unit, { stroke = 'currentColor', strokeOpacity = 0.8, strok
1391
1438
  xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1392
1439
  xnew$1('<circle cx="50" cy="50" r="23">');
1393
1440
  });
1394
- const pointer = xnew$1(PointerEvent);
1395
- pointer.on('-dragstart', ({ event, position }) => {
1441
+ unit.on('dragstart', ({ event, position }) => {
1396
1442
  const vector = getVector(position);
1397
1443
  target.element.style.filter = 'brightness(90%)';
1398
1444
  target.element.style.left = vector.x * newsize / 4 + 'px';
1399
1445
  target.element.style.top = vector.y * newsize / 4 + 'px';
1400
1446
  xnew$1.emit('-down', { vector });
1401
1447
  });
1402
- pointer.on('-dragmove', ({ event, position }) => {
1448
+ unit.on('dragmove', ({ event, position }) => {
1403
1449
  const vector = getVector(position);
1404
1450
  target.element.style.filter = 'brightness(90%)';
1405
1451
  target.element.style.left = vector.x * newsize / 4 + 'px';
1406
1452
  target.element.style.top = vector.y * newsize / 4 + 'px';
1407
1453
  xnew$1.emit('-move', { vector });
1408
1454
  });
1409
- pointer.on('-dragend', ({ event }) => {
1455
+ unit.on('dragend', ({ event }) => {
1410
1456
  const vector = { x: 0, y: 0 };
1411
1457
  target.element.style.filter = '';
1412
1458
  target.element.style.left = vector.x * newsize / 4 + 'px';
@@ -1430,7 +1476,7 @@ function DirectionalPad(unit, { diagonal = true, stroke = 'currentColor', stroke
1430
1476
  const internal = xnew$1((unit) => {
1431
1477
  let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1432
1478
  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;">`);
1433
- xnew$1(outer, ResizeEvent).on('-resize', () => {
1479
+ xnew$1(outer).on('resize', () => {
1434
1480
  newsize = Math.min(outer.clientWidth, outer.clientHeight);
1435
1481
  inner.style.width = `${newsize}px`;
1436
1482
  inner.style.height = `${newsize}px`;
@@ -1458,8 +1504,7 @@ function DirectionalPad(unit, { diagonal = true, stroke = 'currentColor', stroke
1458
1504
  xnew$1('<polygon points="11 50 20 42 20 58">');
1459
1505
  xnew$1('<polygon points="89 50 80 42 80 58">');
1460
1506
  });
1461
- const pointer = xnew$1(PointerEvent);
1462
- pointer.on('-dragstart', ({ event, position }) => {
1507
+ unit.on('dragstart', ({ event, position }) => {
1463
1508
  const vector = getVector(position);
1464
1509
  targets[0].element.style.filter = (vector.y < 0) ? 'brightness(90%)' : '';
1465
1510
  targets[1].element.style.filter = (vector.y > 0) ? 'brightness(90%)' : '';
@@ -1467,7 +1512,7 @@ function DirectionalPad(unit, { diagonal = true, stroke = 'currentColor', stroke
1467
1512
  targets[3].element.style.filter = (vector.x > 0) ? 'brightness(90%)' : '';
1468
1513
  xnew$1.emit('-down', { vector });
1469
1514
  });
1470
- pointer.on('-dragmove', ({ event, position }) => {
1515
+ unit.on('dragmove', ({ event, position }) => {
1471
1516
  const vector = getVector(position);
1472
1517
  targets[0].element.style.filter = (vector.y < 0) ? 'brightness(90%)' : '';
1473
1518
  targets[1].element.style.filter = (vector.y > 0) ? 'brightness(90%)' : '';
@@ -1475,7 +1520,7 @@ function DirectionalPad(unit, { diagonal = true, stroke = 'currentColor', stroke
1475
1520
  targets[3].element.style.filter = (vector.x > 0) ? 'brightness(90%)' : '';
1476
1521
  xnew$1.emit('-move', { vector });
1477
1522
  });
1478
- pointer.on('-dragend', ({ event }) => {
1523
+ unit.on('dragend', ({ event }) => {
1479
1524
  const vector = { x: 0, y: 0 };
1480
1525
  targets[0].element.style.filter = '';
1481
1526
  targets[1].element.style.filter = '';
@@ -1527,249 +1572,29 @@ function TextStream(unit, { text = '', speed = 50, fade = 300 } = {}) {
1527
1572
  const index = Math.floor((new Date().getTime() - start) / speed);
1528
1573
  // Display characters up to the current index (fade in)
1529
1574
  for (let i = 0; i < chars.length; i++) {
1530
- if (i <= index) {
1531
- chars[i].element.style.opacity = '1';
1532
- }
1533
- }
1534
- if (state === 0 && index >= text.length) {
1535
- action();
1536
- }
1537
- });
1538
- xnew$1.timeout(() => {
1539
- xnew$1(document.body).on('click wheel', action);
1540
- xnew$1(KeyboardEvent).on('-keydown', action);
1541
- }, 100);
1542
- function action() {
1543
- if (state === 0) {
1544
- state = 1;
1545
- for (let i = 0; i < chars.length; i++) {
1546
- chars[i].element.style.opacity = '1';
1547
- }
1548
- xnew$1.emit('-complete');
1549
- }
1550
- else if (state === 1) {
1551
- state = 2;
1552
- xnew$1.emit('-next');
1553
- }
1554
- }
1555
- }
1556
-
1557
- const context = window.AudioContext ? new window.AudioContext() : (null);
1558
- const master = context ? context.createGain() : (null);
1559
- if (context) {
1560
- master.gain.value = 0.1;
1561
- master.connect(context.destination);
1562
- }
1563
- class AudioFile {
1564
- constructor(path) {
1565
- this.promise = fetch(path)
1566
- .then((response) => response.arrayBuffer())
1567
- .then((response) => context.decodeAudioData(response))
1568
- .then((response) => { this.buffer = response; })
1569
- .catch(() => {
1570
- console.warn(`"${path}" could not be loaded.`);
1571
- });
1572
- this.amp = context.createGain();
1573
- this.amp.gain.value = 1.0;
1574
- this.amp.connect(master);
1575
- this.fade = context.createGain();
1576
- this.fade.gain.value = 1.0;
1577
- this.fade.connect(this.amp);
1578
- this.source = null;
1579
- this.played = null;
1580
- }
1581
- set volume(value) {
1582
- this.amp.gain.value = value;
1583
- }
1584
- get volume() {
1585
- return this.amp.gain.value;
1586
- }
1587
- play({ offset = 0, fade = 0, loop = false } = {}) {
1588
- if (this.buffer !== undefined && this.played === null) {
1589
- this.source = context.createBufferSource();
1590
- this.source.buffer = this.buffer;
1591
- this.source.loop = loop;
1592
- this.source.connect(this.fade);
1593
- this.played = context.currentTime;
1594
- this.source.playbackRate.value = 1;
1595
- this.source.start(context.currentTime, offset / 1000);
1596
- // Apply fade-in effect if fade duration is specified
1597
- if (fade > 0) {
1598
- this.fade.gain.setValueAtTime(0, context.currentTime);
1599
- this.fade.gain.linearRampToValueAtTime(1.0, context.currentTime + fade / 1000);
1600
- }
1601
- this.source.onended = () => {
1602
- var _a;
1603
- this.played = null;
1604
- (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
1605
- this.source = null;
1606
- };
1607
- }
1608
- }
1609
- pause({ fade = 0 } = {}) {
1610
- var _a, _b;
1611
- if (this.buffer !== undefined && this.played !== null) {
1612
- const elapsed = (context.currentTime - this.played) % this.buffer.duration * 1000;
1613
- // Apply fade-out effect if fade duration is specified
1614
- if (fade > 0) {
1615
- this.fade.gain.setValueAtTime(1.0, context.currentTime);
1616
- this.fade.gain.linearRampToValueAtTime(0, context.currentTime + fade / 1000);
1617
- (_a = this.source) === null || _a === void 0 ? void 0 : _a.stop(context.currentTime + fade / 1000);
1618
- }
1619
- else {
1620
- (_b = this.source) === null || _b === void 0 ? void 0 : _b.stop(context.currentTime);
1621
- }
1622
- this.played = null;
1623
- return elapsed;
1624
- }
1625
- }
1626
- clear() {
1627
- var _a;
1628
- this.amp.disconnect();
1629
- this.fade.disconnect();
1630
- (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
1631
- }
1632
- }
1633
- const keymap = {
1634
- 'A0': 27.500, 'A#0': 29.135, 'B0': 30.868,
1635
- '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,
1636
- '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,
1637
- '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,
1638
- '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,
1639
- '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,
1640
- '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,
1641
- '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,
1642
- 'C8': 4186.009,
1643
- };
1644
- const notemap = {
1645
- '1m': 4.000, '2n': 2.000, '4n': 1.000, '8n': 0.500, '16n': 0.250, '32n': 0.125,
1646
- };
1647
- class Synthesizer {
1648
- constructor(props) { this.props = props; }
1649
- press(frequency, duration, wait) {
1650
- var _a;
1651
- const props = this.props;
1652
- const fv = typeof frequency === 'string' ? keymap[frequency] : frequency;
1653
- const dv = typeof duration === 'string' ? (notemap[duration] * 60 / ((_a = props.bpm) !== null && _a !== void 0 ? _a : 120)) : (typeof duration === 'number' ? (duration / 1000) : 0);
1654
- const start = context.currentTime + (wait !== null && wait !== void 0 ? wait : 0) / 1000;
1655
- const nodes = {};
1656
- nodes.oscillator = context.createOscillator();
1657
- nodes.oscillator.type = props.oscillator.type;
1658
- nodes.oscillator.frequency.value = fv;
1659
- if (props.oscillator.LFO) {
1660
- nodes.oscillatorLFO = context.createOscillator();
1661
- nodes.oscillatorLFODepth = context.createGain();
1662
- nodes.oscillatorLFODepth.gain.value = fv * (Math.pow(2.0, props.oscillator.LFO.amount / 12.0) - 1.0);
1663
- nodes.oscillatorLFO.type = props.oscillator.LFO.type;
1664
- nodes.oscillatorLFO.frequency.value = props.oscillator.LFO.rate;
1665
- nodes.oscillatorLFO.start(start);
1666
- nodes.oscillatorLFO.connect(nodes.oscillatorLFODepth);
1667
- nodes.oscillatorLFODepth.connect(nodes.oscillator.frequency);
1668
- }
1669
- nodes.amp = context.createGain();
1670
- nodes.amp.gain.value = 0.0;
1671
- nodes.target = context.createGain();
1672
- nodes.target.gain.value = 1.0;
1673
- nodes.amp.connect(nodes.target);
1674
- nodes.target.connect(master);
1675
- if (props.filter) {
1676
- nodes.filter = context.createBiquadFilter();
1677
- nodes.filter.type = props.filter.type;
1678
- nodes.filter.frequency.value = props.filter.cutoff;
1679
- nodes.oscillator.connect(nodes.filter);
1680
- nodes.filter.connect(nodes.amp);
1681
- }
1682
- else {
1683
- nodes.oscillator.connect(nodes.amp);
1684
- }
1685
- if (props.reverb) {
1686
- nodes.convolver = context.createConvolver();
1687
- nodes.convolver.buffer = impulseResponse({ time: props.reverb.time });
1688
- nodes.convolverDepth = context.createGain();
1689
- nodes.convolverDepth.gain.value = 1.0;
1690
- nodes.convolverDepth.gain.value *= props.reverb.mix;
1691
- nodes.target.gain.value *= (1.0 - props.reverb.mix);
1692
- nodes.amp.connect(nodes.convolver);
1693
- nodes.convolver.connect(nodes.convolverDepth);
1694
- nodes.convolverDepth.connect(master);
1695
- }
1696
- if (props.oscillator.envelope) {
1697
- const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
1698
- startEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
1699
- }
1700
- if (props.amp.envelope) {
1701
- startEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
1702
- }
1703
- nodes.oscillator.start(start);
1704
- if (dv > 0) {
1705
- release();
1706
- }
1707
- else {
1708
- return { release };
1709
- }
1710
- function release() {
1711
- let stop = null;
1712
- const end = dv > 0 ? dv : (context.currentTime - start);
1713
- if (props.amp.envelope) {
1714
- const ADSR = props.amp.envelope.ADSR;
1715
- const adsr = [ADSR[0] / 1000, ADSR[1] / 1000, ADSR[2], ADSR[3] / 1000];
1716
- const rate = adsr[0] === 0.0 ? 1.0 : Math.min(end / (adsr[0] + 0.001), 1.0);
1717
- stop = start + Math.max((adsr[0] + adsr[1]) * rate, end) + adsr[3];
1718
- }
1719
- else {
1720
- stop = start + end;
1721
- }
1722
- if (nodes.oscillatorLFO) {
1723
- nodes.oscillatorLFO.stop(stop);
1724
- }
1725
- if (props.oscillator.envelope) {
1726
- const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
1727
- stopEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
1728
- }
1729
- if (props.amp.envelope) {
1730
- stopEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
1731
- }
1732
- nodes.oscillator.stop(stop);
1733
- setTimeout(() => {
1734
- var _a, _b, _c, _d, _e;
1735
- nodes.oscillator.disconnect();
1736
- nodes.amp.disconnect();
1737
- nodes.target.disconnect();
1738
- (_a = nodes.oscillatorLFO) === null || _a === void 0 ? void 0 : _a.disconnect();
1739
- (_b = nodes.oscillatorLFODepth) === null || _b === void 0 ? void 0 : _b.disconnect();
1740
- (_c = nodes.filter) === null || _c === void 0 ? void 0 : _c.disconnect();
1741
- (_d = nodes.convolver) === null || _d === void 0 ? void 0 : _d.disconnect();
1742
- (_e = nodes.convolverDepth) === null || _e === void 0 ? void 0 : _e.disconnect();
1743
- }, 2000);
1744
- }
1745
- function stopEnvelope(param, base, amount, ADSR) {
1746
- const end = dv > 0 ? dv : (context.currentTime - start);
1747
- const rate = ADSR[0] === 0.0 ? 1.0 : Math.min(end / (ADSR[0] / 1000), 1.0);
1748
- if (rate < 1.0) {
1749
- param.cancelScheduledValues(start);
1750
- param.setValueAtTime(base, start);
1751
- param.linearRampToValueAtTime(base + amount * rate, start + ADSR[0] / 1000 * rate);
1752
- param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000 * rate);
1575
+ if (i <= index) {
1576
+ chars[i].element.style.opacity = '1';
1753
1577
  }
1754
- param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, dv));
1755
- param.linearRampToValueAtTime(base, start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, end) + ADSR[3] / 1000);
1756
1578
  }
1757
- function startEnvelope(param, base, amount, ADSR) {
1758
- param.value = base;
1759
- param.setValueAtTime(base, start);
1760
- param.linearRampToValueAtTime(base + amount, start + ADSR[0] / 1000);
1761
- param.linearRampToValueAtTime(base + amount * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000);
1579
+ if (state === 0 && index >= text.length) {
1580
+ action();
1762
1581
  }
1763
- function impulseResponse({ time, decay = 2.0 }) {
1764
- const length = context.sampleRate * time / 1000;
1765
- const impulse = context.createBuffer(2, length, context.sampleRate);
1766
- const ch0 = impulse.getChannelData(0);
1767
- const ch1 = impulse.getChannelData(1);
1768
- for (let i = 0; i < length; i++) {
1769
- ch0[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
1770
- ch1[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
1582
+ });
1583
+ xnew$1.timeout(() => {
1584
+ xnew$1(document.body).on('click wheel', action);
1585
+ unit.on('keydown', action);
1586
+ }, 100);
1587
+ function action() {
1588
+ if (state === 0) {
1589
+ state = 1;
1590
+ for (let i = 0; i < chars.length; i++) {
1591
+ chars[i].element.style.opacity = '1';
1771
1592
  }
1772
- return impulse;
1593
+ xnew$1.emit('-complete');
1594
+ }
1595
+ else if (state === 1) {
1596
+ state = 2;
1597
+ xnew$1.emit('-next');
1773
1598
  }
1774
1599
  }
1775
1600
  }
@@ -3416,37 +3241,228 @@ const icons = {
3416
3241
  XMark(unit, props) { icon('XMark', props); },
3417
3242
  };
3418
3243
 
3419
- function VolumeController(unit, {} = {}) {
3420
- 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;">`);
3421
- xnew$1.extend(PointerEvent);
3422
- unit.on('-pointerdown', ({ event }) => event.stopPropagation());
3423
- const slider = xnew$1(`<input type="range" min="0" max="100" value="${master.gain.value * 100}"
3424
- style="display: none; width: calc(96cqw - 100cqh); margin: 0 2cqw; cursor: pointer; pointer-events: auto;"
3425
- >`);
3426
- unit.on('-click:outside', () => slider.element.style.display = 'none');
3427
- const button = xnew$1((button) => {
3428
- xnew$1.nest('<div style="position: relative; width: 100cqh; height: 100cqh; cursor: pointer; pointer-events: auto;">');
3429
- let icon = xnew$1(master.gain.value > 0 ? icons.SpeakerWave : icons.SpeakerXMark);
3430
- return {
3431
- update() {
3432
- icon === null || icon === void 0 ? void 0 : icon.finalize();
3433
- icon = xnew$1(master.gain.value > 0 ? icons.SpeakerWave : icons.SpeakerXMark);
3244
+ const context = window.AudioContext ? new window.AudioContext() : (null);
3245
+ const master = context ? context.createGain() : (null);
3246
+ if (context) {
3247
+ master.gain.value = 0.1;
3248
+ master.connect(context.destination);
3249
+ }
3250
+ class AudioFile {
3251
+ constructor(path) {
3252
+ this.promise = fetch(path)
3253
+ .then((response) => response.arrayBuffer())
3254
+ .then((response) => context.decodeAudioData(response))
3255
+ .then((response) => { this.buffer = response; })
3256
+ .catch(() => {
3257
+ console.warn(`"${path}" could not be loaded.`);
3258
+ });
3259
+ this.amp = context.createGain();
3260
+ this.amp.gain.value = 1.0;
3261
+ this.amp.connect(master);
3262
+ this.fade = context.createGain();
3263
+ this.fade.gain.value = 1.0;
3264
+ this.fade.connect(this.amp);
3265
+ this.source = null;
3266
+ this.played = null;
3267
+ }
3268
+ set volume(value) {
3269
+ this.amp.gain.value = value;
3270
+ }
3271
+ get volume() {
3272
+ return this.amp.gain.value;
3273
+ }
3274
+ play({ offset = 0, fade = 0, loop = false } = {}) {
3275
+ if (this.buffer !== undefined && this.played === null) {
3276
+ this.source = context.createBufferSource();
3277
+ this.source.buffer = this.buffer;
3278
+ this.source.loop = loop;
3279
+ this.source.connect(this.fade);
3280
+ this.played = context.currentTime;
3281
+ this.source.playbackRate.value = 1;
3282
+ this.source.start(context.currentTime, offset / 1000);
3283
+ // Apply fade-in effect if fade duration is specified
3284
+ if (fade > 0) {
3285
+ this.fade.gain.setValueAtTime(0, context.currentTime);
3286
+ this.fade.gain.linearRampToValueAtTime(1.0, context.currentTime + fade / 1000);
3434
3287
  }
3435
- };
3436
- });
3437
- button.on('click', () => slider.element.style.display = slider.element.style.display !== 'none' ? 'none' : 'flex');
3438
- slider.on('input', (event) => {
3439
- master.gain.value = parseFloat(event.target.value) / 100;
3440
- button.update();
3441
- });
3288
+ this.source.onended = () => {
3289
+ var _a;
3290
+ this.played = null;
3291
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
3292
+ this.source = null;
3293
+ };
3294
+ }
3295
+ }
3296
+ pause({ fade = 0 } = {}) {
3297
+ var _a, _b;
3298
+ if (this.buffer !== undefined && this.played !== null) {
3299
+ const elapsed = (context.currentTime - this.played) % this.buffer.duration * 1000;
3300
+ // Apply fade-out effect if fade duration is specified
3301
+ if (fade > 0) {
3302
+ this.fade.gain.setValueAtTime(1.0, context.currentTime);
3303
+ this.fade.gain.linearRampToValueAtTime(0, context.currentTime + fade / 1000);
3304
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.stop(context.currentTime + fade / 1000);
3305
+ }
3306
+ else {
3307
+ (_b = this.source) === null || _b === void 0 ? void 0 : _b.stop(context.currentTime);
3308
+ }
3309
+ this.played = null;
3310
+ return elapsed;
3311
+ }
3312
+ }
3313
+ clear() {
3314
+ var _a;
3315
+ this.amp.disconnect();
3316
+ this.fade.disconnect();
3317
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
3318
+ }
3319
+ }
3320
+ const keymap = {
3321
+ 'A0': 27.500, 'A#0': 29.135, 'B0': 30.868,
3322
+ '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,
3323
+ '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,
3324
+ '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,
3325
+ '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,
3326
+ '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,
3327
+ '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,
3328
+ '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,
3329
+ 'C8': 4186.009,
3330
+ };
3331
+ const notemap = {
3332
+ '1m': 4.000, '2n': 2.000, '4n': 1.000, '8n': 0.500, '16n': 0.250, '32n': 0.125,
3333
+ };
3334
+ class Synthesizer {
3335
+ constructor(props) { this.props = props; }
3336
+ press(frequency, duration, wait) {
3337
+ var _a;
3338
+ const props = this.props;
3339
+ const fv = typeof frequency === 'string' ? keymap[frequency] : frequency;
3340
+ const dv = typeof duration === 'string' ? (notemap[duration] * 60 / ((_a = props.bpm) !== null && _a !== void 0 ? _a : 120)) : (typeof duration === 'number' ? (duration / 1000) : 0);
3341
+ const start = context.currentTime + (wait !== null && wait !== void 0 ? wait : 0) / 1000;
3342
+ const nodes = {};
3343
+ nodes.oscillator = context.createOscillator();
3344
+ nodes.oscillator.type = props.oscillator.type;
3345
+ nodes.oscillator.frequency.value = fv;
3346
+ if (props.oscillator.LFO) {
3347
+ nodes.oscillatorLFO = context.createOscillator();
3348
+ nodes.oscillatorLFODepth = context.createGain();
3349
+ nodes.oscillatorLFODepth.gain.value = fv * (Math.pow(2.0, props.oscillator.LFO.amount / 12.0) - 1.0);
3350
+ nodes.oscillatorLFO.type = props.oscillator.LFO.type;
3351
+ nodes.oscillatorLFO.frequency.value = props.oscillator.LFO.rate;
3352
+ nodes.oscillatorLFO.start(start);
3353
+ nodes.oscillatorLFO.connect(nodes.oscillatorLFODepth);
3354
+ nodes.oscillatorLFODepth.connect(nodes.oscillator.frequency);
3355
+ }
3356
+ nodes.amp = context.createGain();
3357
+ nodes.amp.gain.value = 0.0;
3358
+ nodes.target = context.createGain();
3359
+ nodes.target.gain.value = 1.0;
3360
+ nodes.amp.connect(nodes.target);
3361
+ nodes.target.connect(master);
3362
+ if (props.filter) {
3363
+ nodes.filter = context.createBiquadFilter();
3364
+ nodes.filter.type = props.filter.type;
3365
+ nodes.filter.frequency.value = props.filter.cutoff;
3366
+ nodes.oscillator.connect(nodes.filter);
3367
+ nodes.filter.connect(nodes.amp);
3368
+ }
3369
+ else {
3370
+ nodes.oscillator.connect(nodes.amp);
3371
+ }
3372
+ if (props.reverb) {
3373
+ nodes.convolver = context.createConvolver();
3374
+ nodes.convolver.buffer = impulseResponse({ time: props.reverb.time });
3375
+ nodes.convolverDepth = context.createGain();
3376
+ nodes.convolverDepth.gain.value = 1.0;
3377
+ nodes.convolverDepth.gain.value *= props.reverb.mix;
3378
+ nodes.target.gain.value *= (1.0 - props.reverb.mix);
3379
+ nodes.amp.connect(nodes.convolver);
3380
+ nodes.convolver.connect(nodes.convolverDepth);
3381
+ nodes.convolverDepth.connect(master);
3382
+ }
3383
+ if (props.oscillator.envelope) {
3384
+ const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
3385
+ startEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
3386
+ }
3387
+ if (props.amp.envelope) {
3388
+ startEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
3389
+ }
3390
+ nodes.oscillator.start(start);
3391
+ if (dv > 0) {
3392
+ release();
3393
+ }
3394
+ else {
3395
+ return { release };
3396
+ }
3397
+ function release() {
3398
+ let stop = null;
3399
+ const end = dv > 0 ? dv : (context.currentTime - start);
3400
+ if (props.amp.envelope) {
3401
+ const ADSR = props.amp.envelope.ADSR;
3402
+ const adsr = [ADSR[0] / 1000, ADSR[1] / 1000, ADSR[2], ADSR[3] / 1000];
3403
+ const rate = adsr[0] === 0.0 ? 1.0 : Math.min(end / (adsr[0] + 0.001), 1.0);
3404
+ stop = start + Math.max((adsr[0] + adsr[1]) * rate, end) + adsr[3];
3405
+ }
3406
+ else {
3407
+ stop = start + end;
3408
+ }
3409
+ if (nodes.oscillatorLFO) {
3410
+ nodes.oscillatorLFO.stop(stop);
3411
+ }
3412
+ if (props.oscillator.envelope) {
3413
+ const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
3414
+ stopEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
3415
+ }
3416
+ if (props.amp.envelope) {
3417
+ stopEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
3418
+ }
3419
+ nodes.oscillator.stop(stop);
3420
+ setTimeout(() => {
3421
+ var _a, _b, _c, _d, _e;
3422
+ nodes.oscillator.disconnect();
3423
+ nodes.amp.disconnect();
3424
+ nodes.target.disconnect();
3425
+ (_a = nodes.oscillatorLFO) === null || _a === void 0 ? void 0 : _a.disconnect();
3426
+ (_b = nodes.oscillatorLFODepth) === null || _b === void 0 ? void 0 : _b.disconnect();
3427
+ (_c = nodes.filter) === null || _c === void 0 ? void 0 : _c.disconnect();
3428
+ (_d = nodes.convolver) === null || _d === void 0 ? void 0 : _d.disconnect();
3429
+ (_e = nodes.convolverDepth) === null || _e === void 0 ? void 0 : _e.disconnect();
3430
+ }, 2000);
3431
+ }
3432
+ function stopEnvelope(param, base, amount, ADSR) {
3433
+ const end = dv > 0 ? dv : (context.currentTime - start);
3434
+ const rate = ADSR[0] === 0.0 ? 1.0 : Math.min(end / (ADSR[0] / 1000), 1.0);
3435
+ if (rate < 1.0) {
3436
+ param.cancelScheduledValues(start);
3437
+ param.setValueAtTime(base, start);
3438
+ param.linearRampToValueAtTime(base + amount * rate, start + ADSR[0] / 1000 * rate);
3439
+ param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000 * rate);
3440
+ }
3441
+ param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, dv));
3442
+ param.linearRampToValueAtTime(base, start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, end) + ADSR[3] / 1000);
3443
+ }
3444
+ function startEnvelope(param, base, amount, ADSR) {
3445
+ param.value = base;
3446
+ param.setValueAtTime(base, start);
3447
+ param.linearRampToValueAtTime(base + amount, start + ADSR[0] / 1000);
3448
+ param.linearRampToValueAtTime(base + amount * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000);
3449
+ }
3450
+ function impulseResponse({ time, decay = 2.0 }) {
3451
+ const length = context.sampleRate * time / 1000;
3452
+ const impulse = context.createBuffer(2, length, context.sampleRate);
3453
+ const ch0 = impulse.getChannelData(0);
3454
+ const ch1 = impulse.getChannelData(1);
3455
+ for (let i = 0; i < length; i++) {
3456
+ ch0[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
3457
+ ch1[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
3458
+ }
3459
+ return impulse;
3460
+ }
3461
+ }
3442
3462
  }
3443
3463
 
3444
3464
  const basics = {
3445
3465
  Screen,
3446
- PointerEvent,
3447
- DirectEvent,
3448
- ResizeEvent,
3449
- KeyboardEvent,
3450
3466
  ModalFrame,
3451
3467
  ModalContent,
3452
3468
  AccordionFrame,
@@ -3461,7 +3477,6 @@ const basics = {
3461
3477
  DragTarget,
3462
3478
  AnalogStick,
3463
3479
  DirectionalPad,
3464
- VolumeController
3465
3480
  };
3466
3481
  const audio = {
3467
3482
  load(path) {