@aegisjsproject/callback-registry 1.0.1 → 1.0.2

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/callbacks.cjs CHANGED
@@ -1,5 +1,506 @@
1
1
  'use strict';
2
2
 
3
+ const PREFIX = 'data-aegis-event-';
4
+ const EVENT_PREFIX = PREFIX + 'on-';
5
+ const EVENT_PREFIX_LENGTH = EVENT_PREFIX.length;
6
+ const DATA_PREFIX = 'aegisEventOn';
7
+ const DATA_PREFIX_LENGTH = DATA_PREFIX.length;
8
+ const signalSymbol = Symbol('aegis:signal');
9
+ const signalRegistry = new Map();
10
+ const controllerRegistry = new Map();
11
+
12
+ const once = PREFIX + 'once';
13
+ const passive = PREFIX + 'passive';
14
+ const capture = PREFIX + 'capture';
15
+ const signal = PREFIX + 'signal';
16
+ const onAbort = EVENT_PREFIX + 'abort';
17
+ const onBlur = EVENT_PREFIX + 'blur';
18
+ const onFocus = EVENT_PREFIX + 'focus';
19
+ const onCancel = EVENT_PREFIX + 'cancel';
20
+ const onAuxclick = EVENT_PREFIX + 'auxclick';
21
+ const onBeforeinput = EVENT_PREFIX + 'beforeinput';
22
+ const onBeforetoggle = EVENT_PREFIX + 'beforetoggle';
23
+ const onCanplay = EVENT_PREFIX + 'canplay';
24
+ const onCanplaythrough = EVENT_PREFIX + 'canplaythrough';
25
+ const onChange = EVENT_PREFIX + 'change';
26
+ const onClick = EVENT_PREFIX + 'click';
27
+ const onClose = EVENT_PREFIX + 'close';
28
+ const onContextmenu = EVENT_PREFIX + 'contextmenu';
29
+ const onCopy = EVENT_PREFIX + 'copy';
30
+ const onCuechange = EVENT_PREFIX + 'cuechange';
31
+ const onCut = EVENT_PREFIX + 'cut';
32
+ const onDblclick = EVENT_PREFIX + 'dblclick';
33
+ const onDrag = EVENT_PREFIX + 'drag';
34
+ const onDragend = EVENT_PREFIX + 'dragend';
35
+ const onDragenter = EVENT_PREFIX + 'dragenter';
36
+ const onDragexit = EVENT_PREFIX + 'dragexit';
37
+ const onDragleave = EVENT_PREFIX + 'dragleave';
38
+ const onDragover = EVENT_PREFIX + 'dragover';
39
+ const onDragstart = EVENT_PREFIX + 'dragstart';
40
+ const onDrop = EVENT_PREFIX + 'drop';
41
+ const onDurationchange = EVENT_PREFIX + 'durationchange';
42
+ const onEmptied = EVENT_PREFIX + 'emptied';
43
+ const onEnded = EVENT_PREFIX + 'ended';
44
+ const onFormdata = EVENT_PREFIX + 'formdata';
45
+ const onInput = EVENT_PREFIX + 'input';
46
+ const onInvalid = EVENT_PREFIX + 'invalid';
47
+ const onKeydown = EVENT_PREFIX + 'keydown';
48
+ const onKeypress = EVENT_PREFIX + 'keypress';
49
+ const onKeyup = EVENT_PREFIX + 'keyup';
50
+ const onLoad = EVENT_PREFIX + 'load';
51
+ const onLoadeddata = EVENT_PREFIX + 'loadeddata';
52
+ const onLoadedmetadata = EVENT_PREFIX + 'loadedmetadata';
53
+ const onLoadstart = EVENT_PREFIX + 'loadstart';
54
+ const onMousedown = EVENT_PREFIX + 'mousedown';
55
+ const onMouseenter = EVENT_PREFIX + 'mouseenter';
56
+ const onMouseleave = EVENT_PREFIX + 'mouseleave';
57
+ const onMousemove = EVENT_PREFIX + 'mousemove';
58
+ const onMouseout = EVENT_PREFIX + 'mouseout';
59
+ const onMouseover = EVENT_PREFIX + 'mouseover';
60
+ const onMouseup = EVENT_PREFIX + 'mouseup';
61
+ const onWheel = EVENT_PREFIX + 'wheel';
62
+ const onPaste = EVENT_PREFIX + 'paste';
63
+ const onPause = EVENT_PREFIX + 'pause';
64
+ const onPlay = EVENT_PREFIX + 'play';
65
+ const onPlaying = EVENT_PREFIX + 'playing';
66
+ const onProgress = EVENT_PREFIX + 'progress';
67
+ const onRatechange = EVENT_PREFIX + 'ratechange';
68
+ const onReset = EVENT_PREFIX + 'reset';
69
+ const onResize = EVENT_PREFIX + 'resize';
70
+ const onScroll = EVENT_PREFIX + 'scroll';
71
+ const onScrollend = EVENT_PREFIX + 'scrollend';
72
+ const onSecuritypolicyviolation = EVENT_PREFIX + 'securitypolicyviolation';
73
+ const onSeeked = EVENT_PREFIX + 'seeked';
74
+ const onSeeking = EVENT_PREFIX + 'seeking';
75
+ const onSelect = EVENT_PREFIX + 'select';
76
+ const onSlotchange = EVENT_PREFIX + 'slotchange';
77
+ const onStalled = EVENT_PREFIX + 'stalled';
78
+ const onSubmit = EVENT_PREFIX + 'submit';
79
+ const onSuspend = EVENT_PREFIX + 'suspend';
80
+ const onTimeupdate = EVENT_PREFIX + 'timeupdate';
81
+ const onVolumechange = EVENT_PREFIX + 'volumechange';
82
+ const onWaiting = EVENT_PREFIX + 'waiting';
83
+ const onSelectstart = EVENT_PREFIX + 'selectstart';
84
+ const onSelectionchange = EVENT_PREFIX + 'selectionchange';
85
+ const onToggle = EVENT_PREFIX + 'toggle';
86
+ const onPointercancel = EVENT_PREFIX + 'pointercancel';
87
+ const onPointerdown = EVENT_PREFIX + 'pointerdown';
88
+ const onPointerup = EVENT_PREFIX + 'pointerup';
89
+ const onPointermove = EVENT_PREFIX + 'pointermove';
90
+ const onPointerout = EVENT_PREFIX + 'pointerout';
91
+ const onPointerover = EVENT_PREFIX + 'pointerover';
92
+ const onPointerenter = EVENT_PREFIX + 'pointerenter';
93
+ const onPointerleave = EVENT_PREFIX + 'pointerleave';
94
+ const onGotpointercapture = EVENT_PREFIX + 'gotpointercapture';
95
+ const onLostpointercapture = EVENT_PREFIX + 'lostpointercapture';
96
+ const onMozfullscreenchange = EVENT_PREFIX + 'mozfullscreenchange';
97
+ const onMozfullscreenerror = EVENT_PREFIX + 'mozfullscreenerror';
98
+ const onAnimationcancel = EVENT_PREFIX + 'animationcancel';
99
+ const onAnimationend = EVENT_PREFIX + 'animationend';
100
+ const onAnimationiteration = EVENT_PREFIX + 'animationiteration';
101
+ const onAnimationstart = EVENT_PREFIX + 'animationstart';
102
+ const onTransitioncancel = EVENT_PREFIX + 'transitioncancel';
103
+ const onTransitionend = EVENT_PREFIX + 'transitionend';
104
+ const onTransitionrun = EVENT_PREFIX + 'transitionrun';
105
+ const onTransitionstart = EVENT_PREFIX + 'transitionstart';
106
+ const onWebkitanimationend = EVENT_PREFIX + 'webkitanimationend';
107
+ const onWebkitanimationiteration = EVENT_PREFIX + 'webkitanimationiteration';
108
+ const onWebkitanimationstart = EVENT_PREFIX + 'webkitanimationstart';
109
+ const onWebkittransitionend = EVENT_PREFIX + 'webkittransitionend';
110
+ const onError = EVENT_PREFIX + 'error';
111
+
112
+ const eventAttrs = [
113
+ onAbort,
114
+ onBlur,
115
+ onFocus,
116
+ onCancel,
117
+ onAuxclick,
118
+ onBeforeinput,
119
+ onBeforetoggle,
120
+ onCanplay,
121
+ onCanplaythrough,
122
+ onChange,
123
+ onClick,
124
+ onClose,
125
+ onContextmenu,
126
+ onCopy,
127
+ onCuechange,
128
+ onCut,
129
+ onDblclick,
130
+ onDrag,
131
+ onDragend,
132
+ onDragenter,
133
+ onDragexit,
134
+ onDragleave,
135
+ onDragover,
136
+ onDragstart,
137
+ onDrop,
138
+ onDurationchange,
139
+ onEmptied,
140
+ onEnded,
141
+ onFormdata,
142
+ onInput,
143
+ onInvalid,
144
+ onKeydown,
145
+ onKeypress,
146
+ onKeyup,
147
+ onLoad,
148
+ onLoadeddata,
149
+ onLoadedmetadata,
150
+ onLoadstart,
151
+ onMousedown,
152
+ onMouseenter,
153
+ onMouseleave,
154
+ onMousemove,
155
+ onMouseout,
156
+ onMouseover,
157
+ onMouseup,
158
+ onWheel,
159
+ onPaste,
160
+ onPause,
161
+ onPlay,
162
+ onPlaying,
163
+ onProgress,
164
+ onRatechange,
165
+ onReset,
166
+ onResize,
167
+ onScroll,
168
+ onScrollend,
169
+ onSecuritypolicyviolation,
170
+ onSeeked,
171
+ onSeeking,
172
+ onSelect,
173
+ onSlotchange,
174
+ onStalled,
175
+ onSubmit,
176
+ onSuspend,
177
+ onTimeupdate,
178
+ onVolumechange,
179
+ onWaiting,
180
+ onSelectstart,
181
+ onSelectionchange,
182
+ onToggle,
183
+ onPointercancel,
184
+ onPointerdown,
185
+ onPointerup,
186
+ onPointermove,
187
+ onPointerout,
188
+ onPointerover,
189
+ onPointerenter,
190
+ onPointerleave,
191
+ onGotpointercapture,
192
+ onLostpointercapture,
193
+ onMozfullscreenchange,
194
+ onMozfullscreenerror,
195
+ onAnimationcancel,
196
+ onAnimationend,
197
+ onAnimationiteration,
198
+ onAnimationstart,
199
+ onTransitioncancel,
200
+ onTransitionend,
201
+ onTransitionrun,
202
+ onTransitionstart,
203
+ onWebkitanimationend,
204
+ onWebkitanimationiteration,
205
+ onWebkitanimationstart,
206
+ onWebkittransitionend,
207
+ onError,
208
+ ];
209
+
210
+ let selector = eventAttrs.map(attr => `[${CSS.escape(attr)}]`).join(', ');
211
+
212
+ const attrToProp = attr => `on${attr[EVENT_PREFIX_LENGTH].toUpperCase()}${attr.substring(EVENT_PREFIX_LENGTH + 1)}`;
213
+
214
+ const eventToProp = event => EVENT_PREFIX + event;
215
+
216
+ const hasEventAttribute = event => eventAttrs.includes(EVENT_PREFIX + event);
217
+
218
+ const isEventDataAttr = ([name]) => name.startsWith(DATA_PREFIX);
219
+
220
+ function _addListeners(el, { signal, attrFilter = EVENTS } = {}) {
221
+ const dataset = el.dataset;
222
+
223
+ for (const [attr, val] of Object.entries(dataset).filter(isEventDataAttr)) {
224
+ try {
225
+ const event = 'on' + attr.substring(DATA_PREFIX_LENGTH);
226
+
227
+ if (attrFilter.hasOwnProperty(event) && hasCallback(val)) {
228
+ el.addEventListener(event.substring(2).toLowerCase(), getCallback(val), {
229
+ passive: dataset.hasOwnProperty('aegisEventPassive'),
230
+ capture: dataset.hasOwnProperty('aegisEventCapture'),
231
+ once: dataset.hasOwnProperty('aegisEventOnce'),
232
+ signal: dataset.hasOwnProperty('aegisEventSignal') ? getSignal(dataset.aegisEventSignal) : signal,
233
+ });
234
+ }
235
+ } catch(err) {
236
+ reportError(err);
237
+ }
238
+ }
239
+ }
240
+
241
+ new MutationObserver(records => {
242
+ records.forEach(record => {
243
+ switch(record.type) {
244
+ case 'childList':
245
+ [...record.addedNodes]
246
+ .filter(node => node.nodeType === Node.ELEMENT_NODE)
247
+ .forEach(node => attachListeners(node));
248
+ break;
249
+
250
+ case 'attributes':
251
+ if (typeof record.oldValue === 'string' && hasCallback(record.oldValue)) {
252
+ record.target.removeEventListener(
253
+ record.attributeName.substring(EVENT_PREFIX_LENGTH),
254
+ getCallback(record.oldValue), {
255
+ once: record.target.hasAttribute(once),
256
+ capture: record.target.hasAttribute(capture),
257
+ passive: record.target.hasAttribute(passive),
258
+ }
259
+ );
260
+ }
261
+
262
+ if (
263
+ record.target.hasAttribute(record.attributeName)
264
+ && hasCallback(record.target.getAttribute(record.attributeName))
265
+ ) {
266
+ record.target.addEventListener(
267
+ record.attributeName.substring(EVENT_PREFIX_LENGTH),
268
+ getCallback(record.target.getAttribute(record.attributeName)), {
269
+ once: record.target.hasAttribute(once),
270
+ capture: record.target.hasAttribute(capture),
271
+ passive: record.target.hasAttribute(passive),
272
+ signal: record.target.hasAttribute(signal) ? getSignal(record.target.getAttribute(signal)) : undefined,
273
+ }
274
+ );
275
+ }
276
+ break;
277
+ }
278
+ });
279
+ });
280
+
281
+ const EVENTS = {
282
+ onAbort,
283
+ onBlur,
284
+ onFocus,
285
+ onCancel,
286
+ onAuxclick,
287
+ onBeforeinput,
288
+ onBeforetoggle,
289
+ onCanplay,
290
+ onCanplaythrough,
291
+ onChange,
292
+ onClick,
293
+ onClose,
294
+ onContextmenu,
295
+ onCopy,
296
+ onCuechange,
297
+ onCut,
298
+ onDblclick,
299
+ onDrag,
300
+ onDragend,
301
+ onDragenter,
302
+ onDragexit,
303
+ onDragleave,
304
+ onDragover,
305
+ onDragstart,
306
+ onDrop,
307
+ onDurationchange,
308
+ onEmptied,
309
+ onEnded,
310
+ onFormdata,
311
+ onInput,
312
+ onInvalid,
313
+ onKeydown,
314
+ onKeypress,
315
+ onKeyup,
316
+ onLoad,
317
+ onLoadeddata,
318
+ onLoadedmetadata,
319
+ onLoadstart,
320
+ onMousedown,
321
+ onMouseenter,
322
+ onMouseleave,
323
+ onMousemove,
324
+ onMouseout,
325
+ onMouseover,
326
+ onMouseup,
327
+ onWheel,
328
+ onPaste,
329
+ onPause,
330
+ onPlay,
331
+ onPlaying,
332
+ onProgress,
333
+ onRatechange,
334
+ onReset,
335
+ onResize,
336
+ onScroll,
337
+ onScrollend,
338
+ onSecuritypolicyviolation,
339
+ onSeeked,
340
+ onSeeking,
341
+ onSelect,
342
+ onSlotchange,
343
+ onStalled,
344
+ onSubmit,
345
+ onSuspend,
346
+ onTimeupdate,
347
+ onVolumechange,
348
+ onWaiting,
349
+ onSelectstart,
350
+ onSelectionchange,
351
+ onToggle,
352
+ onPointercancel,
353
+ onPointerdown,
354
+ onPointerup,
355
+ onPointermove,
356
+ onPointerout,
357
+ onPointerover,
358
+ onPointerenter,
359
+ onPointerleave,
360
+ onGotpointercapture,
361
+ onLostpointercapture,
362
+ onMozfullscreenchange,
363
+ onMozfullscreenerror,
364
+ onAnimationcancel,
365
+ onAnimationend,
366
+ onAnimationiteration,
367
+ onAnimationstart,
368
+ onTransitioncancel,
369
+ onTransitionend,
370
+ onTransitionrun,
371
+ onTransitionstart,
372
+ onWebkitanimationend,
373
+ onWebkitanimationiteration,
374
+ onWebkitanimationstart,
375
+ onWebkittransitionend,
376
+ onError,
377
+ once,
378
+ passive,
379
+ capture,
380
+ };
381
+
382
+ /**
383
+ * Register an attribute to observe for adding/removing event listeners
384
+ *
385
+ * @param {string} attr Name of the attribute to observe
386
+ * @param {object} options
387
+ * @param {boolean} [options.addListeners=false] Whether or not to automatically add listeners
388
+ * @param {Document|Element} [options.base=document.body] Root node to observe
389
+ * @param {AbortSignal} [options.signal] An abort signal to remove any listeners when aborted
390
+ * @returns {string} The resulting `data-*` attribute name
391
+ */
392
+ function registerEventAttribute(attr, {
393
+ addListeners = false,
394
+ base = document.body,
395
+ signal,
396
+ } = {}) {
397
+ const fullAttr = EVENT_PREFIX + attr.toLowerCase();
398
+
399
+ if (! eventAttrs.includes(fullAttr)) {
400
+ const sel = `[${CSS.escape(fullAttr)}]`;
401
+ const prop = attrToProp(fullAttr);
402
+ eventAttrs.push(fullAttr);
403
+ EVENTS[prop] = fullAttr;
404
+ selector += `, ${sel}`;
405
+
406
+ if (addListeners) {
407
+ requestAnimationFrame(() => {
408
+ const config = { attrFilter: { [prop]: sel }, signal };
409
+ [base, ...base.querySelectorAll(sel)].forEach(el => _addListeners(el, config));
410
+ });
411
+ }
412
+ }
413
+
414
+ return fullAttr;
415
+ }
416
+
417
+ /**
418
+ * Get a registetd controller from the registry
419
+ *
420
+ * @param {string} key Generated key with which the controller was registered
421
+ * @returns {AbortController|void} Any registered controller, if any
422
+ */
423
+ const getController = key => controllerRegistry.get(key);
424
+
425
+ function abortController(key, reason) {
426
+ const controller = getController(key);
427
+
428
+ if (! (controller instanceof AbortController)) {
429
+ return false;
430
+ } else if (typeof reason === 'string') {
431
+ controller.abort(new Error(reason));
432
+ return true;
433
+ } else {
434
+ controller.abort(reason);
435
+ return true;
436
+ }
437
+ }
438
+
439
+ /**
440
+ * Register an `AbortSignal` to be used in declarative HTML as a value for `data-aegis-event-signal`
441
+ *
442
+ * @param {AbortSignal} signal The signal to register
443
+ * @returns {string} The registered key
444
+ * @throws {TypeError} Thrown if not an `AbortSignal`
445
+ */
446
+ function registerSignal(signal) {
447
+ if (! (signal instanceof AbortSignal)) {
448
+ throw new TypeError('Signal must be an `AbortSignal`.');
449
+ } else if (typeof signal[signalSymbol] === 'string') {
450
+ return signal[signalSymbol];
451
+ } else {
452
+ const key = 'aegis:event:signal:' + crypto.randomUUID();
453
+ Object.defineProperty(signal, signalSymbol, { value: key, writable: false, enumerable: false });
454
+ signalRegistry.set(key, signal);
455
+ signal.addEventListener('abort', ({ target }) => unregisterSignal(target[signalSymbol]), { once: true });
456
+
457
+ return key;
458
+ }
459
+ }
460
+
461
+ /**
462
+ * Gets and `AbortSignal` from the registry
463
+ *
464
+ * @param {string} key The registered key for the signal
465
+ * @returns {AbortSignal|void} The corresponding `AbortSignal`, if any
466
+ */
467
+ const getSignal = key => signalRegistry.get(key);
468
+
469
+ /**
470
+ * Removes an `AbortSignal` from the registry
471
+ *
472
+ * @param {AbortSignal|string} signal An `AbortSignal` or the registered key for one
473
+ * @returns {boolean} Whether or not the signal was sucessfully unregistered
474
+ * @throws {TypeError} Throws if `signal` is not an `AbortSignal` or the key for a registered signal
475
+ */
476
+ function unregisterSignal(signal) {
477
+ if (signal instanceof AbortSignal) {
478
+ return signalRegistry.delete(signal[signalSymbol]);
479
+ } else if (typeof signal === 'string') {
480
+ return signalRegistry.delete(signal);
481
+ } else {
482
+ throw new TypeError('Signal must be an `AbortSignal` or registered key/attribute.');
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Add listeners to an element and its children, matching a generated query based on registered attributes
488
+ *
489
+ * @param {Element|Document} target Root node to add listeners from
490
+ * @param {object} options
491
+ * @param {AbortSignal} [options.signal] Optional signal to remove event listeners
492
+ * @returns {Element|Document} Returns the passed target node
493
+ */
494
+ function attachListeners(target, { signal } = {}) {
495
+ const nodes = target instanceof Element && target.matches(selector)
496
+ ? [target, ...target.querySelectorAll(selector)]
497
+ : target.querySelectorAll(selector);
498
+
499
+ nodes.forEach(el => _addListeners(el, { signal }));
500
+
501
+ return target;
502
+ }
503
+
3
504
  let _isRegistrationOpen = true;
4
505
 
5
506
  const $$ = (selector, base = document) => base.querySelectorAll(selector);
@@ -17,6 +518,7 @@ const FUNCS = {
17
518
  back: 'aegis:navigate:back',
18
519
  forward: 'aegis:navigate:forward',
19
520
  reload: 'aegis:navigate:reload',
521
+ close: 'aegis:navigate:close',
20
522
  link: 'aegis:navigate:go',
21
523
  popup: 'aegis:navigate:popup',
22
524
  },
@@ -34,6 +536,9 @@ const FUNCS = {
34
536
  disable: 'aegis:ui:disable',
35
537
  scrollTo: 'aegis:ui:scrollTo',
36
538
  prevent: 'aegis:ui:prevent',
539
+ revokeObjectURL: 'aegis:ui:revokeObjectURL',
540
+ cancelAnimationFrame: 'aegis:ui:cancelAnimationFrame',
541
+ abortController: 'aegis:ui:controller:abort',
37
542
  },
38
543
  };
39
544
 
@@ -45,6 +550,7 @@ const registry = new Map([
45
550
  [FUNCS.navigate.back, () => history.back()],
46
551
  [FUNCS.navigate.forward, () => history.forward()],
47
552
  [FUNCS.navigate.reload, () => history.go(0)],
553
+ [FUNCS.navigate.close, () => globalThis.close()],
48
554
  [FUNCS.navigate.link, event => {
49
555
  if (event.isTrusted) {
50
556
  event.preventDefault();
@@ -83,6 +589,11 @@ const registry = new Map([
83
589
  });
84
590
  }
85
591
  }],
592
+ [FUNCS.ui.revokeObjectURL, ({ currentTarget }) => URL.revokeObjectURL(currentTarget.src)],
593
+ [FUNCS.ui.cancelAnimationFrame, ({ currentTarget }) => cancelAnimationFrame(parseInt(currentTarget.dataset.animationFrame))],
594
+ [FUNCS.ui.clearInterval, ({ currentTarget }) => clearInterval(parseInt(currentTarget.dataset.clearInterval))],
595
+ [FUNCS.ui.clearTimeout, ({ currentTarget }) => clearTimeout(parseInt(currentTarget.dataset.timeout))],
596
+ [FUNCS.ui.abortController, ({ currentTarget }) => abortController(currentTarget.dataset.aegisEventController, currentTarget.dataset.aegisControllerReason)],
86
597
  [FUNCS.ui.showModal, ({ currentTarget }) => {
87
598
  const target = $(currentTarget.dataset.showModalSelector);
88
599
 
@@ -240,6 +751,42 @@ function getHost(target) {
240
751
  }
241
752
  }
242
753
 
754
+ function on(event, callback, { capture: capture$1 = false, passive: passive$1 = false, once: once$1 = false, signal: signal$1 } = {}) {
755
+ if (callback instanceof Function) {
756
+ return on(event, createCallback(callback), { capture: capture$1, passive: passive$1, once: once$1, signal: signal$1 });
757
+ } else if (typeof callback !== 'string' || callback.length === 0) {
758
+ throw new TypeError('Callback must be a function or a registered callback string.');
759
+ } else if (typeof event !== 'string' || event.length === 0) {
760
+ throw new TypeError('Event must be a non-empty string.');
761
+ } else {
762
+ if (! hasEventAttribute(event)) {
763
+ registerEventAttribute(event);
764
+ }
765
+
766
+ const parts = [[eventToProp(event), callback]];
767
+
768
+ if (capture$1) {
769
+ parts.push([capture, '']);
770
+ }
771
+
772
+ if (passive$1) {
773
+ parts.push([passive, '']);
774
+ }
775
+
776
+ if (once$1) {
777
+ parts.push([once, '']);
778
+ }
779
+
780
+ if (signal$1 instanceof AbortSignal) {
781
+ parts.push([signal, registerSignal(signal$1)]);
782
+ } else if (typeof signal$1 === 'string') {
783
+ parts.push([signal, signal$1]);
784
+ }
785
+
786
+ return parts.map(([prop, val]) => `${prop}="${val}"`).join(' ');
787
+ }
788
+ }
789
+
243
790
  exports.FUNCS = FUNCS;
244
791
  exports.callCallback = callCallback;
245
792
  exports.clearRegistry = clearRegistry;
@@ -250,5 +797,6 @@ exports.getHost = getHost;
250
797
  exports.hasCallback = hasCallback;
251
798
  exports.isRegistrationOpen = isRegistrationOpen;
252
799
  exports.listCallbacks = listCallbacks;
800
+ exports.on = on;
253
801
  exports.registerCallback = registerCallback;
254
802
  exports.unregisterCallback = unregisterCallback;
package/callbacks.js CHANGED
@@ -1,3 +1,8 @@
1
+ import {
2
+ eventToProp, capture as captureAttr, once as onceAttr, passive as passiveAttr, signal as signalAttr,
3
+ registerEventAttribute, hasEventAttribute, registerSignal, abortController,
4
+ } from './events.js';
5
+
1
6
  let _isRegistrationOpen = true;
2
7
 
3
8
  const $$ = (selector, base = document) => base.querySelectorAll(selector);
@@ -15,6 +20,7 @@ export const FUNCS = {
15
20
  back: 'aegis:navigate:back',
16
21
  forward: 'aegis:navigate:forward',
17
22
  reload: 'aegis:navigate:reload',
23
+ close: 'aegis:navigate:close',
18
24
  link: 'aegis:navigate:go',
19
25
  popup: 'aegis:navigate:popup',
20
26
  },
@@ -32,6 +38,9 @@ export const FUNCS = {
32
38
  disable: 'aegis:ui:disable',
33
39
  scrollTo: 'aegis:ui:scrollTo',
34
40
  prevent: 'aegis:ui:prevent',
41
+ revokeObjectURL: 'aegis:ui:revokeObjectURL',
42
+ cancelAnimationFrame: 'aegis:ui:cancelAnimationFrame',
43
+ abortController: 'aegis:ui:controller:abort',
35
44
  },
36
45
  };
37
46
 
@@ -43,6 +52,7 @@ const registry = new Map([
43
52
  [FUNCS.navigate.back, () => history.back()],
44
53
  [FUNCS.navigate.forward, () => history.forward()],
45
54
  [FUNCS.navigate.reload, () => history.go(0)],
55
+ [FUNCS.navigate.close, () => globalThis.close()],
46
56
  [FUNCS.navigate.link, event => {
47
57
  if (event.isTrusted) {
48
58
  event.preventDefault();
@@ -81,6 +91,11 @@ const registry = new Map([
81
91
  });
82
92
  }
83
93
  }],
94
+ [FUNCS.ui.revokeObjectURL, ({ currentTarget }) => URL.revokeObjectURL(currentTarget.src)],
95
+ [FUNCS.ui.cancelAnimationFrame, ({ currentTarget }) => cancelAnimationFrame(parseInt(currentTarget.dataset.animationFrame))],
96
+ [FUNCS.ui.clearInterval, ({ currentTarget }) => clearInterval(parseInt(currentTarget.dataset.clearInterval))],
97
+ [FUNCS.ui.clearTimeout, ({ currentTarget }) => clearTimeout(parseInt(currentTarget.dataset.timeout))],
98
+ [FUNCS.ui.abortController, ({ currentTarget }) => abortController(currentTarget.dataset.aegisEventController, currentTarget.dataset.aegisControllerReason)],
84
99
  [FUNCS.ui.showModal, ({ currentTarget }) => {
85
100
  const target = $(currentTarget.dataset.showModalSelector);
86
101
 
@@ -237,3 +252,39 @@ export function getHost(target) {
237
252
  return null;
238
253
  }
239
254
  }
255
+
256
+ export function on(event, callback, { capture = false, passive = false, once = false, signal } = {}) {
257
+ if (callback instanceof Function) {
258
+ return on(event, createCallback(callback), { capture, passive, once, signal });
259
+ } else if (typeof callback !== 'string' || callback.length === 0) {
260
+ throw new TypeError('Callback must be a function or a registered callback string.');
261
+ } else if (typeof event !== 'string' || event.length === 0) {
262
+ throw new TypeError('Event must be a non-empty string.');
263
+ } else {
264
+ if (! hasEventAttribute(event)) {
265
+ registerEventAttribute(event);
266
+ }
267
+
268
+ const parts = [[eventToProp(event), callback]];
269
+
270
+ if (capture) {
271
+ parts.push([captureAttr, '']);
272
+ }
273
+
274
+ if (passive) {
275
+ parts.push([passiveAttr, '']);
276
+ }
277
+
278
+ if (once) {
279
+ parts.push([onceAttr, '']);
280
+ }
281
+
282
+ if (signal instanceof AbortSignal) {
283
+ parts.push([signalAttr, registerSignal(signal)]);
284
+ } else if (typeof signal === 'string') {
285
+ parts.push([signalAttr, signal]);
286
+ }
287
+
288
+ return parts.map(([prop, val]) => `${prop}="${val}"`).join(' ');
289
+ }
290
+ }