@mulsense/xnew 0.3.7 → 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.js CHANGED
@@ -206,10 +206,323 @@
206
206
  }
207
207
  }
208
208
 
209
- //----------------------------------------------------------------------------------------------------
210
- // utils
211
- //----------------------------------------------------------------------------------------------------
212
209
  const SYSTEM_EVENTS = ['start', 'process', 'update', '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(xnew$1.scope((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, Object.assign({}, 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
+ if (isActive === true) {
456
+ props.listener({ event, type: props.type, scale: 1.0 });
457
+ }
458
+ isActive = false;
459
+ map.delete(event.pointerId);
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
  //----------------------------------------------------------------------------------------------------
@@ -303,6 +616,7 @@
303
616
  listeners: new MapMap(),
304
617
  defines: {},
305
618
  systems: { start: [], process: [], update: [], stop: [], finalize: [] },
619
+ eventManager: new EventManager(),
306
620
  });
307
621
  // nest html element
308
622
  if (typeof unit._.target === 'string') {
@@ -487,7 +801,7 @@
487
801
  this._.listeners.set(type, listener, { element: this.element, execute });
488
802
  Unit.type2units.add(type, this);
489
803
  if (/^[A-Za-z]/.test(type)) {
490
- this.element.addEventListener(type, execute, options);
804
+ this._.eventManager.add({ element: this.element, type, listener: execute, options });
491
805
  }
492
806
  }
493
807
  });
@@ -500,11 +814,11 @@
500
814
  }
501
815
  (listener ? [listener] : [...this._.listeners.keys(type)]).forEach((listener) => {
502
816
  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
- }
817
+ if (item === undefined)
818
+ return;
819
+ this._.listeners.delete(type, listener);
820
+ if (/^[A-Za-z]/.test(type)) {
821
+ this._.eventManager.remove({ type, listener: item.execute });
508
822
  }
509
823
  });
510
824
  if (this._.listeners.has(type) === false) {
@@ -923,209 +1237,12 @@
923
1237
  };
924
1238
  }
925
1239
 
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' } = {}) {
1240
+ function Screen(unit, { width = 640, height = 480, fit = 'contain' } = {}) {
1124
1241
  const size = { width, height };
1125
1242
  const wrapper = xnew$1.nest('<div style="position: relative; width: 100%; height: 100%; overflow: hidden;">');
1243
+ unit.on('resize', resize);
1126
1244
  const absolute = xnew$1.nest('<div style="position: absolute; margin: auto; container-type: size; overflow: hidden;">');
1127
1245
  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
1246
  resize();
1130
1247
  function resize() {
1131
1248
  const aspect = size.width / size.height;
@@ -1266,15 +1383,14 @@
1266
1383
  const absolute = xnew$1.nest(`<div style="position: absolute; top: ${y}px; left: ${x}px;">`);
1267
1384
  xnew$1.context('xnew.dragframe', { frame, absolute });
1268
1385
  }
1269
- function DragTarget(target, {} = {}) {
1386
+ function DragTarget(unit, {} = {}) {
1270
1387
  const { frame, absolute } = xnew$1.context('xnew.dragframe');
1271
- xnew$1.nest('<div>');
1272
- const direct = xnew$1(absolute.parentElement, DirectEvent);
1388
+ const target = xnew$1(absolute.parentElement);
1273
1389
  const current = { x: 0, y: 0 };
1274
1390
  const offset = { x: 0, y: 0 };
1275
1391
  let dragged = false;
1276
- direct.on('-dragstart', ({ event, position }) => {
1277
- if (target.element.contains(event.target) === false)
1392
+ target.on('dragstart', ({ event, position }) => {
1393
+ if (unit.element.contains(event.target) === false)
1278
1394
  return;
1279
1395
  dragged = true;
1280
1396
  offset.x = position.x - parseFloat(absolute.style.left || '0');
@@ -1282,7 +1398,7 @@
1282
1398
  current.x = position.x - offset.x;
1283
1399
  current.y = position.y - offset.y;
1284
1400
  });
1285
- direct.on('-dragmove', ({ event, delta }) => {
1401
+ target.on('dragmove', ({ event, delta }) => {
1286
1402
  if (dragged !== true)
1287
1403
  return;
1288
1404
  current.x += delta.x;
@@ -1290,9 +1406,10 @@
1290
1406
  absolute.style.left = `${current.x}px`;
1291
1407
  absolute.style.top = `${current.y}px`;
1292
1408
  });
1293
- direct.on('-dragcancel -dragend', ({ event }) => {
1409
+ target.on('dragend', ({ event }) => {
1294
1410
  dragged = false;
1295
1411
  });
1412
+ xnew$1.nest('<div>');
1296
1413
  }
1297
1414
 
1298
1415
  //----------------------------------------------------------------------------------------------------
@@ -1301,7 +1418,7 @@
1301
1418
  function SVGTemplate(self, { stroke = 'currentColor', strokeOpacity = 0.8, strokeWidth = 2, strokeLinejoin = 'round', fill = null, fillOpacity = 0.8 }) {
1302
1419
  xnew$1.nest(`<svg
1303
1420
  viewBox="0 0 100 100"
1304
- style="position: absolute; width: 100%; height: 100%; pointer-select: none;
1421
+ style="position: absolute; width: 100%; height: 100%; select: none;
1305
1422
  stroke: ${stroke}; stroke-opacity: ${strokeOpacity}; stroke-width: ${strokeWidth}; stroke-linejoin: ${strokeLinejoin};
1306
1423
  ${fill ? `fill: ${fill}; fill-opacity: ${fillOpacity};` : ''}
1307
1424
  ">`);
@@ -1311,7 +1428,7 @@
1311
1428
  const internal = xnew$1((unit) => {
1312
1429
  let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1313
1430
  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', () => {
1431
+ xnew$1(outer).on('resize', () => {
1315
1432
  newsize = Math.min(outer.clientWidth, outer.clientHeight);
1316
1433
  inner.style.width = `${newsize}px`;
1317
1434
  inner.style.height = `${newsize}px`;
@@ -1327,22 +1444,21 @@
1327
1444
  xnew$1.extend(SVGTemplate, { fill, fillOpacity, stroke, strokeOpacity, strokeWidth, strokeLinejoin });
1328
1445
  xnew$1('<circle cx="50" cy="50" r="23">');
1329
1446
  });
1330
- const direct = xnew$1(DirectEvent);
1331
- direct.on('-dragstart', ({ event, position }) => {
1447
+ unit.on('dragstart', ({ event, position }) => {
1332
1448
  const vector = getVector(position);
1333
1449
  target.element.style.filter = 'brightness(90%)';
1334
1450
  target.element.style.left = vector.x * newsize / 4 + 'px';
1335
1451
  target.element.style.top = vector.y * newsize / 4 + 'px';
1336
1452
  xnew$1.emit('-down', { vector });
1337
1453
  });
1338
- direct.on('-dragmove', ({ event, position }) => {
1454
+ unit.on('dragmove', ({ event, position }) => {
1339
1455
  const vector = getVector(position);
1340
1456
  target.element.style.filter = 'brightness(90%)';
1341
1457
  target.element.style.left = vector.x * newsize / 4 + 'px';
1342
1458
  target.element.style.top = vector.y * newsize / 4 + 'px';
1343
1459
  xnew$1.emit('-move', { vector });
1344
1460
  });
1345
- direct.on('-dragend', ({ event }) => {
1461
+ unit.on('dragend', ({ event }) => {
1346
1462
  const vector = { x: 0, y: 0 };
1347
1463
  target.element.style.filter = '';
1348
1464
  target.element.style.left = vector.x * newsize / 4 + 'px';
@@ -1366,7 +1482,7 @@
1366
1482
  const internal = xnew$1((unit) => {
1367
1483
  let newsize = Math.min(outer.clientWidth, outer.clientHeight);
1368
1484
  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', () => {
1485
+ xnew$1(outer).on('resize', () => {
1370
1486
  newsize = Math.min(outer.clientWidth, outer.clientHeight);
1371
1487
  inner.style.width = `${newsize}px`;
1372
1488
  inner.style.height = `${newsize}px`;
@@ -1394,8 +1510,7 @@
1394
1510
  xnew$1('<polygon points="11 50 20 42 20 58">');
1395
1511
  xnew$1('<polygon points="89 50 80 42 80 58">');
1396
1512
  });
1397
- const direct = xnew$1(DirectEvent);
1398
- direct.on('-dragstart', ({ event, position }) => {
1513
+ unit.on('dragstart', ({ event, position }) => {
1399
1514
  const vector = getVector(position);
1400
1515
  targets[0].element.style.filter = (vector.y < 0) ? 'brightness(90%)' : '';
1401
1516
  targets[1].element.style.filter = (vector.y > 0) ? 'brightness(90%)' : '';
@@ -1403,7 +1518,7 @@
1403
1518
  targets[3].element.style.filter = (vector.x > 0) ? 'brightness(90%)' : '';
1404
1519
  xnew$1.emit('-down', { vector });
1405
1520
  });
1406
- direct.on('-dragmove', ({ event, position }) => {
1521
+ unit.on('dragmove', ({ event, position }) => {
1407
1522
  const vector = getVector(position);
1408
1523
  targets[0].element.style.filter = (vector.y < 0) ? 'brightness(90%)' : '';
1409
1524
  targets[1].element.style.filter = (vector.y > 0) ? 'brightness(90%)' : '';
@@ -1411,7 +1526,7 @@
1411
1526
  targets[3].element.style.filter = (vector.x > 0) ? 'brightness(90%)' : '';
1412
1527
  xnew$1.emit('-move', { vector });
1413
1528
  });
1414
- direct.on('-dragend', ({ event }) => {
1529
+ unit.on('dragend', ({ event }) => {
1415
1530
  const vector = { x: 0, y: 0 };
1416
1531
  targets[0].element.style.filter = '';
1417
1532
  targets[1].element.style.filter = '';
@@ -1463,249 +1578,29 @@
1463
1578
  const index = Math.floor((new Date().getTime() - start) / speed);
1464
1579
  // Display characters up to the current index (fade in)
1465
1580
  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);
1581
+ if (i <= index) {
1582
+ chars[i].element.style.opacity = '1';
1689
1583
  }
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
1584
  }
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);
1585
+ if (state === 0 && index >= text.length) {
1586
+ action();
1698
1587
  }
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);
1588
+ });
1589
+ xnew$1.timeout(() => {
1590
+ xnew$1(document.body).on('click wheel', action);
1591
+ unit.on('keydown', action);
1592
+ }, 100);
1593
+ function action() {
1594
+ if (state === 0) {
1595
+ state = 1;
1596
+ for (let i = 0; i < chars.length; i++) {
1597
+ chars[i].element.style.opacity = '1';
1707
1598
  }
1708
- return impulse;
1599
+ xnew$1.emit('-complete');
1600
+ }
1601
+ else if (state === 1) {
1602
+ state = 2;
1603
+ xnew$1.emit('-next');
1709
1604
  }
1710
1605
  }
1711
1606
  }
@@ -3352,35 +3247,228 @@
3352
3247
  XMark(unit, props) { icon('XMark', props); },
3353
3248
  };
3354
3249
 
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);
3250
+ const context = window.AudioContext ? new window.AudioContext() : (null);
3251
+ const master = context ? context.createGain() : (null);
3252
+ if (context) {
3253
+ master.gain.value = 0.1;
3254
+ master.connect(context.destination);
3255
+ }
3256
+ class AudioFile {
3257
+ constructor(path) {
3258
+ this.promise = fetch(path)
3259
+ .then((response) => response.arrayBuffer())
3260
+ .then((response) => context.decodeAudioData(response))
3261
+ .then((response) => { this.buffer = response; })
3262
+ .catch(() => {
3263
+ console.warn(`"${path}" could not be loaded.`);
3264
+ });
3265
+ this.amp = context.createGain();
3266
+ this.amp.gain.value = 1.0;
3267
+ this.amp.connect(master);
3268
+ this.fade = context.createGain();
3269
+ this.fade.gain.value = 1.0;
3270
+ this.fade.connect(this.amp);
3271
+ this.source = null;
3272
+ this.played = null;
3273
+ }
3274
+ set volume(value) {
3275
+ this.amp.gain.value = value;
3276
+ }
3277
+ get volume() {
3278
+ return this.amp.gain.value;
3279
+ }
3280
+ play({ offset = 0, fade = 0, loop = false } = {}) {
3281
+ if (this.buffer !== undefined && this.played === null) {
3282
+ this.source = context.createBufferSource();
3283
+ this.source.buffer = this.buffer;
3284
+ this.source.loop = loop;
3285
+ this.source.connect(this.fade);
3286
+ this.played = context.currentTime;
3287
+ this.source.playbackRate.value = 1;
3288
+ this.source.start(context.currentTime, offset / 1000);
3289
+ // Apply fade-in effect if fade duration is specified
3290
+ if (fade > 0) {
3291
+ this.fade.gain.setValueAtTime(0, context.currentTime);
3292
+ this.fade.gain.linearRampToValueAtTime(1.0, context.currentTime + fade / 1000);
3370
3293
  }
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
- });
3294
+ this.source.onended = () => {
3295
+ var _a;
3296
+ this.played = null;
3297
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
3298
+ this.source = null;
3299
+ };
3300
+ }
3301
+ }
3302
+ pause({ fade = 0 } = {}) {
3303
+ var _a, _b;
3304
+ if (this.buffer !== undefined && this.played !== null) {
3305
+ const elapsed = (context.currentTime - this.played) % this.buffer.duration * 1000;
3306
+ // Apply fade-out effect if fade duration is specified
3307
+ if (fade > 0) {
3308
+ this.fade.gain.setValueAtTime(1.0, context.currentTime);
3309
+ this.fade.gain.linearRampToValueAtTime(0, context.currentTime + fade / 1000);
3310
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.stop(context.currentTime + fade / 1000);
3311
+ }
3312
+ else {
3313
+ (_b = this.source) === null || _b === void 0 ? void 0 : _b.stop(context.currentTime);
3314
+ }
3315
+ this.played = null;
3316
+ return elapsed;
3317
+ }
3318
+ }
3319
+ clear() {
3320
+ var _a;
3321
+ this.amp.disconnect();
3322
+ this.fade.disconnect();
3323
+ (_a = this.source) === null || _a === void 0 ? void 0 : _a.disconnect();
3324
+ }
3325
+ }
3326
+ const keymap = {
3327
+ 'A0': 27.500, 'A#0': 29.135, 'B0': 30.868,
3328
+ '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,
3329
+ '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,
3330
+ '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,
3331
+ '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,
3332
+ '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,
3333
+ '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,
3334
+ '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,
3335
+ 'C8': 4186.009,
3336
+ };
3337
+ const notemap = {
3338
+ '1m': 4.000, '2n': 2.000, '4n': 1.000, '8n': 0.500, '16n': 0.250, '32n': 0.125,
3339
+ };
3340
+ class Synthesizer {
3341
+ constructor(props) { this.props = props; }
3342
+ press(frequency, duration, wait) {
3343
+ var _a;
3344
+ const props = this.props;
3345
+ const fv = typeof frequency === 'string' ? keymap[frequency] : frequency;
3346
+ const dv = typeof duration === 'string' ? (notemap[duration] * 60 / ((_a = props.bpm) !== null && _a !== void 0 ? _a : 120)) : (typeof duration === 'number' ? (duration / 1000) : 0);
3347
+ const start = context.currentTime + (wait !== null && wait !== void 0 ? wait : 0) / 1000;
3348
+ const nodes = {};
3349
+ nodes.oscillator = context.createOscillator();
3350
+ nodes.oscillator.type = props.oscillator.type;
3351
+ nodes.oscillator.frequency.value = fv;
3352
+ if (props.oscillator.LFO) {
3353
+ nodes.oscillatorLFO = context.createOscillator();
3354
+ nodes.oscillatorLFODepth = context.createGain();
3355
+ nodes.oscillatorLFODepth.gain.value = fv * (Math.pow(2.0, props.oscillator.LFO.amount / 12.0) - 1.0);
3356
+ nodes.oscillatorLFO.type = props.oscillator.LFO.type;
3357
+ nodes.oscillatorLFO.frequency.value = props.oscillator.LFO.rate;
3358
+ nodes.oscillatorLFO.start(start);
3359
+ nodes.oscillatorLFO.connect(nodes.oscillatorLFODepth);
3360
+ nodes.oscillatorLFODepth.connect(nodes.oscillator.frequency);
3361
+ }
3362
+ nodes.amp = context.createGain();
3363
+ nodes.amp.gain.value = 0.0;
3364
+ nodes.target = context.createGain();
3365
+ nodes.target.gain.value = 1.0;
3366
+ nodes.amp.connect(nodes.target);
3367
+ nodes.target.connect(master);
3368
+ if (props.filter) {
3369
+ nodes.filter = context.createBiquadFilter();
3370
+ nodes.filter.type = props.filter.type;
3371
+ nodes.filter.frequency.value = props.filter.cutoff;
3372
+ nodes.oscillator.connect(nodes.filter);
3373
+ nodes.filter.connect(nodes.amp);
3374
+ }
3375
+ else {
3376
+ nodes.oscillator.connect(nodes.amp);
3377
+ }
3378
+ if (props.reverb) {
3379
+ nodes.convolver = context.createConvolver();
3380
+ nodes.convolver.buffer = impulseResponse({ time: props.reverb.time });
3381
+ nodes.convolverDepth = context.createGain();
3382
+ nodes.convolverDepth.gain.value = 1.0;
3383
+ nodes.convolverDepth.gain.value *= props.reverb.mix;
3384
+ nodes.target.gain.value *= (1.0 - props.reverb.mix);
3385
+ nodes.amp.connect(nodes.convolver);
3386
+ nodes.convolver.connect(nodes.convolverDepth);
3387
+ nodes.convolverDepth.connect(master);
3388
+ }
3389
+ if (props.oscillator.envelope) {
3390
+ const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
3391
+ startEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
3392
+ }
3393
+ if (props.amp.envelope) {
3394
+ startEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
3395
+ }
3396
+ nodes.oscillator.start(start);
3397
+ if (dv > 0) {
3398
+ release();
3399
+ }
3400
+ else {
3401
+ return { release };
3402
+ }
3403
+ function release() {
3404
+ let stop = null;
3405
+ const end = dv > 0 ? dv : (context.currentTime - start);
3406
+ if (props.amp.envelope) {
3407
+ const ADSR = props.amp.envelope.ADSR;
3408
+ const adsr = [ADSR[0] / 1000, ADSR[1] / 1000, ADSR[2], ADSR[3] / 1000];
3409
+ const rate = adsr[0] === 0.0 ? 1.0 : Math.min(end / (adsr[0] + 0.001), 1.0);
3410
+ stop = start + Math.max((adsr[0] + adsr[1]) * rate, end) + adsr[3];
3411
+ }
3412
+ else {
3413
+ stop = start + end;
3414
+ }
3415
+ if (nodes.oscillatorLFO) {
3416
+ nodes.oscillatorLFO.stop(stop);
3417
+ }
3418
+ if (props.oscillator.envelope) {
3419
+ const amount = fv * (Math.pow(2.0, props.oscillator.envelope.amount / 12.0) - 1.0);
3420
+ stopEnvelope(nodes.oscillator.frequency, fv, amount, props.oscillator.envelope.ADSR);
3421
+ }
3422
+ if (props.amp.envelope) {
3423
+ stopEnvelope(nodes.amp.gain, 0.0, props.amp.envelope.amount, props.amp.envelope.ADSR);
3424
+ }
3425
+ nodes.oscillator.stop(stop);
3426
+ setTimeout(() => {
3427
+ var _a, _b, _c, _d, _e;
3428
+ nodes.oscillator.disconnect();
3429
+ nodes.amp.disconnect();
3430
+ nodes.target.disconnect();
3431
+ (_a = nodes.oscillatorLFO) === null || _a === void 0 ? void 0 : _a.disconnect();
3432
+ (_b = nodes.oscillatorLFODepth) === null || _b === void 0 ? void 0 : _b.disconnect();
3433
+ (_c = nodes.filter) === null || _c === void 0 ? void 0 : _c.disconnect();
3434
+ (_d = nodes.convolver) === null || _d === void 0 ? void 0 : _d.disconnect();
3435
+ (_e = nodes.convolverDepth) === null || _e === void 0 ? void 0 : _e.disconnect();
3436
+ }, 2000);
3437
+ }
3438
+ function stopEnvelope(param, base, amount, ADSR) {
3439
+ const end = dv > 0 ? dv : (context.currentTime - start);
3440
+ const rate = ADSR[0] === 0.0 ? 1.0 : Math.min(end / (ADSR[0] / 1000), 1.0);
3441
+ if (rate < 1.0) {
3442
+ param.cancelScheduledValues(start);
3443
+ param.setValueAtTime(base, start);
3444
+ param.linearRampToValueAtTime(base + amount * rate, start + ADSR[0] / 1000 * rate);
3445
+ param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000 * rate);
3446
+ }
3447
+ param.linearRampToValueAtTime(base + amount * rate * ADSR[2], start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, dv));
3448
+ param.linearRampToValueAtTime(base, start + Math.max((ADSR[0] + ADSR[1]) / 1000 * rate, end) + ADSR[3] / 1000);
3449
+ }
3450
+ function startEnvelope(param, base, amount, ADSR) {
3451
+ param.value = base;
3452
+ param.setValueAtTime(base, start);
3453
+ param.linearRampToValueAtTime(base + amount, start + ADSR[0] / 1000);
3454
+ param.linearRampToValueAtTime(base + amount * ADSR[2], start + (ADSR[0] + ADSR[1]) / 1000);
3455
+ }
3456
+ function impulseResponse({ time, decay = 2.0 }) {
3457
+ const length = context.sampleRate * time / 1000;
3458
+ const impulse = context.createBuffer(2, length, context.sampleRate);
3459
+ const ch0 = impulse.getChannelData(0);
3460
+ const ch1 = impulse.getChannelData(1);
3461
+ for (let i = 0; i < length; i++) {
3462
+ ch0[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
3463
+ ch1[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, decay);
3464
+ }
3465
+ return impulse;
3466
+ }
3467
+ }
3378
3468
  }
3379
3469
 
3380
3470
  const basics = {
3381
3471
  Screen,
3382
- DirectEvent,
3383
- ResizeEvent,
3384
3472
  ModalFrame,
3385
3473
  ModalContent,
3386
3474
  AccordionFrame,
@@ -3395,7 +3483,6 @@
3395
3483
  DragTarget,
3396
3484
  AnalogStick,
3397
3485
  DirectionalPad,
3398
- VolumeController
3399
3486
  };
3400
3487
  const audio = {
3401
3488
  load(path) {