@aegisjsproject/callback-registry 1.0.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/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/callbackRegistry.cjs +528 -0
- package/callbackRegistry.js +2 -0
- package/callbackRegistry.mjs +509 -0
- package/callbacks.cjs +254 -0
- package/callbacks.js +239 -0
- package/events.js +269 -0
- package/package.json +83 -0
package/events.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { hasCallback, getCallback } from './callbacks.js';
|
|
2
|
+
|
|
3
|
+
const EVENT_PREFIX = 'data-aegis-event-on-';
|
|
4
|
+
const EVENT_PREFIX_LENGTH = EVENT_PREFIX.length;
|
|
5
|
+
const DATA_PREFIX = 'aegisEventOn';
|
|
6
|
+
const DATA_PREFIX_LENGTH = DATA_PREFIX.length;
|
|
7
|
+
|
|
8
|
+
const once = 'data-aegis-event-once',
|
|
9
|
+
passive = 'data-aegis-event-passive',
|
|
10
|
+
capture = 'data-aegis-event-capture';
|
|
11
|
+
|
|
12
|
+
const eventAttrs = [
|
|
13
|
+
EVENT_PREFIX + 'abort',
|
|
14
|
+
EVENT_PREFIX + 'blur',
|
|
15
|
+
EVENT_PREFIX + 'focus',
|
|
16
|
+
EVENT_PREFIX + 'cancel',
|
|
17
|
+
EVENT_PREFIX + 'auxclick',
|
|
18
|
+
EVENT_PREFIX + 'beforeinput',
|
|
19
|
+
EVENT_PREFIX + 'beforetoggle',
|
|
20
|
+
EVENT_PREFIX + 'canplay',
|
|
21
|
+
EVENT_PREFIX + 'canplaythrough',
|
|
22
|
+
EVENT_PREFIX + 'change',
|
|
23
|
+
EVENT_PREFIX + 'click',
|
|
24
|
+
EVENT_PREFIX + 'close',
|
|
25
|
+
EVENT_PREFIX + 'contextmenu',
|
|
26
|
+
EVENT_PREFIX + 'copy',
|
|
27
|
+
EVENT_PREFIX + 'cuechange',
|
|
28
|
+
EVENT_PREFIX + 'cut',
|
|
29
|
+
EVENT_PREFIX + 'dblclick',
|
|
30
|
+
EVENT_PREFIX + 'drag',
|
|
31
|
+
EVENT_PREFIX + 'dragend',
|
|
32
|
+
EVENT_PREFIX + 'dragenter',
|
|
33
|
+
EVENT_PREFIX + 'dragexit',
|
|
34
|
+
EVENT_PREFIX + 'dragleave',
|
|
35
|
+
EVENT_PREFIX + 'dragover',
|
|
36
|
+
EVENT_PREFIX + 'dragstart',
|
|
37
|
+
EVENT_PREFIX + 'drop',
|
|
38
|
+
EVENT_PREFIX + 'durationchange',
|
|
39
|
+
EVENT_PREFIX + 'emptied',
|
|
40
|
+
EVENT_PREFIX + 'ended',
|
|
41
|
+
EVENT_PREFIX + 'formdata',
|
|
42
|
+
EVENT_PREFIX + 'input',
|
|
43
|
+
EVENT_PREFIX + 'invalid',
|
|
44
|
+
EVENT_PREFIX + 'keydown',
|
|
45
|
+
EVENT_PREFIX + 'keypress',
|
|
46
|
+
EVENT_PREFIX + 'keyup',
|
|
47
|
+
EVENT_PREFIX + 'load',
|
|
48
|
+
EVENT_PREFIX + 'loadeddata',
|
|
49
|
+
EVENT_PREFIX + 'loadedmetadata',
|
|
50
|
+
EVENT_PREFIX + 'loadstart',
|
|
51
|
+
EVENT_PREFIX + 'mousedown',
|
|
52
|
+
EVENT_PREFIX + 'mouseenter',
|
|
53
|
+
EVENT_PREFIX + 'mouseleave',
|
|
54
|
+
EVENT_PREFIX + 'mousemove',
|
|
55
|
+
EVENT_PREFIX + 'mouseout',
|
|
56
|
+
EVENT_PREFIX + 'mouseover',
|
|
57
|
+
EVENT_PREFIX + 'mouseup',
|
|
58
|
+
EVENT_PREFIX + 'wheel',
|
|
59
|
+
EVENT_PREFIX + 'paste',
|
|
60
|
+
EVENT_PREFIX + 'pause',
|
|
61
|
+
EVENT_PREFIX + 'play',
|
|
62
|
+
EVENT_PREFIX + 'playing',
|
|
63
|
+
EVENT_PREFIX + 'progress',
|
|
64
|
+
EVENT_PREFIX + 'ratechange',
|
|
65
|
+
EVENT_PREFIX + 'reset',
|
|
66
|
+
EVENT_PREFIX + 'resize',
|
|
67
|
+
EVENT_PREFIX + 'scroll',
|
|
68
|
+
EVENT_PREFIX + 'scrollend',
|
|
69
|
+
EVENT_PREFIX + 'securitypolicyviolation',
|
|
70
|
+
EVENT_PREFIX + 'seeked',
|
|
71
|
+
EVENT_PREFIX + 'seeking',
|
|
72
|
+
EVENT_PREFIX + 'select',
|
|
73
|
+
EVENT_PREFIX + 'slotchange',
|
|
74
|
+
EVENT_PREFIX + 'stalled',
|
|
75
|
+
EVENT_PREFIX + 'submit',
|
|
76
|
+
EVENT_PREFIX + 'suspend',
|
|
77
|
+
EVENT_PREFIX + 'timeupdate',
|
|
78
|
+
EVENT_PREFIX + 'volumechange',
|
|
79
|
+
EVENT_PREFIX + 'waiting',
|
|
80
|
+
EVENT_PREFIX + 'selectstart',
|
|
81
|
+
EVENT_PREFIX + 'selectionchange',
|
|
82
|
+
EVENT_PREFIX + 'toggle',
|
|
83
|
+
EVENT_PREFIX + 'pointercancel',
|
|
84
|
+
EVENT_PREFIX + 'pointerdown',
|
|
85
|
+
EVENT_PREFIX + 'pointerup',
|
|
86
|
+
EVENT_PREFIX + 'pointermove',
|
|
87
|
+
EVENT_PREFIX + 'pointerout',
|
|
88
|
+
EVENT_PREFIX + 'pointerover',
|
|
89
|
+
EVENT_PREFIX + 'pointerenter',
|
|
90
|
+
EVENT_PREFIX + 'pointerleave',
|
|
91
|
+
EVENT_PREFIX + 'gotpointercapture',
|
|
92
|
+
EVENT_PREFIX + 'lostpointercapture',
|
|
93
|
+
EVENT_PREFIX + 'mozfullscreenchange',
|
|
94
|
+
EVENT_PREFIX + 'mozfullscreenerror',
|
|
95
|
+
EVENT_PREFIX + 'animationcancel',
|
|
96
|
+
EVENT_PREFIX + 'animationend',
|
|
97
|
+
EVENT_PREFIX + 'animationiteration',
|
|
98
|
+
EVENT_PREFIX + 'animationstart',
|
|
99
|
+
EVENT_PREFIX + 'transitioncancel',
|
|
100
|
+
EVENT_PREFIX + 'transitionend',
|
|
101
|
+
EVENT_PREFIX + 'transitionrun',
|
|
102
|
+
EVENT_PREFIX + 'transitionstart',
|
|
103
|
+
EVENT_PREFIX + 'webkitanimationend',
|
|
104
|
+
EVENT_PREFIX + 'webkitanimationiteration',
|
|
105
|
+
EVENT_PREFIX + 'webkitanimationstart',
|
|
106
|
+
EVENT_PREFIX + 'webkittransitionend',
|
|
107
|
+
EVENT_PREFIX + 'error',
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
let selector = eventAttrs.map(attr => `[${CSS.escape(attr)}]`).join(', ');
|
|
111
|
+
|
|
112
|
+
const attrToProp = attr => `on${attr[EVENT_PREFIX_LENGTH].toUpperCase()}${attr.substring(EVENT_PREFIX_LENGTH + 1)}`;
|
|
113
|
+
|
|
114
|
+
const attrEntriesMap = attr => [attrToProp(attr), attr];
|
|
115
|
+
|
|
116
|
+
const isEventDataAttr = ([name]) => name.startsWith(DATA_PREFIX);
|
|
117
|
+
|
|
118
|
+
const DATA_EVENTS = Object.fromEntries([...eventAttrs].map(attrEntriesMap));
|
|
119
|
+
|
|
120
|
+
function _addListeners(el, { signal, attrFilter = EVENTS } = {}) {
|
|
121
|
+
const dataset = el.dataset;
|
|
122
|
+
|
|
123
|
+
for (const [attr, val] of Object.entries(dataset).filter(isEventDataAttr)) {
|
|
124
|
+
try {
|
|
125
|
+
const event = 'on' + attr.substring(DATA_PREFIX_LENGTH);
|
|
126
|
+
|
|
127
|
+
if (attrFilter.hasOwnProperty(event) && hasCallback(val)) {
|
|
128
|
+
el.addEventListener(event.substring(2).toLowerCase(), getCallback(val), {
|
|
129
|
+
passive: dataset.hasOwnProperty('aegisEventPassive'),
|
|
130
|
+
capture: dataset.hasOwnProperty('aegisEventCapture'),
|
|
131
|
+
once: dataset.hasOwnProperty('aegisEventOnce'),
|
|
132
|
+
signal,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
} catch(err) {
|
|
136
|
+
console.error(err);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const observer = new MutationObserver(records => {
|
|
142
|
+
records.forEach(record => {
|
|
143
|
+
switch(record.type) {
|
|
144
|
+
case 'childList':
|
|
145
|
+
[...record.addedNodes]
|
|
146
|
+
.filter(node => node.nodeType === Node.ELEMENT_NODE)
|
|
147
|
+
.forEach(node => attachListeners(node));
|
|
148
|
+
break;
|
|
149
|
+
|
|
150
|
+
case 'attributes':
|
|
151
|
+
if (typeof record.oldValue === 'string' && hasCallback(record.oldValue)) {
|
|
152
|
+
record.target.removeEventListener(
|
|
153
|
+
record.attributeName.substring(EVENT_PREFIX_LENGTH),
|
|
154
|
+
getCallback(record.oldValue), {
|
|
155
|
+
once: record.target.hasAttribute(once),
|
|
156
|
+
capture: record.target.hasAttribute(capture),
|
|
157
|
+
passive: record.target.hasAttribute(passive),
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (
|
|
163
|
+
record.target.hasAttribute(record.attributeName)
|
|
164
|
+
&& hasCallback(record.target.getAttribute(record.attributeName))
|
|
165
|
+
) {
|
|
166
|
+
record.target.addEventListener(
|
|
167
|
+
record.attributeName.substring(EVENT_PREFIX_LENGTH),
|
|
168
|
+
getCallback(record.target.getAttribute(record.attributeName)), {
|
|
169
|
+
once: record.target.hasAttribute(once),
|
|
170
|
+
capture: record.target.hasAttribute(capture),
|
|
171
|
+
passive: record.target.hasAttribute(passive),
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
export const EVENTS = { ...DATA_EVENTS, once, passive, capture };
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Register an attribute to observe for adding/removing event listeners
|
|
184
|
+
*
|
|
185
|
+
* @param {string} attr Name of the attribute to observe
|
|
186
|
+
* @param {object} options
|
|
187
|
+
* @param {boolean} [options.addListeners=false] Whether or not to automatically add listeners
|
|
188
|
+
* @param {Document|Element} [options.base=document.body] Root node to observe
|
|
189
|
+
* @param {AbortSignal} [options.signal] An abort signal to remove any listeners when aborted
|
|
190
|
+
* @returns {string} The resulting `data-*` attribute name
|
|
191
|
+
*/
|
|
192
|
+
export function registerEventAttribute(attr, {
|
|
193
|
+
addListeners = false,
|
|
194
|
+
base = document.body,
|
|
195
|
+
signal,
|
|
196
|
+
} = {}) {
|
|
197
|
+
const fullAttr = EVENT_PREFIX + attr.toLowerCase();
|
|
198
|
+
|
|
199
|
+
if (! eventAttrs.includes(fullAttr)) {
|
|
200
|
+
const sel = `[${CSS.escape(fullAttr)}]`;
|
|
201
|
+
const prop = attrToProp(fullAttr);
|
|
202
|
+
eventAttrs.push(fullAttr);
|
|
203
|
+
EVENTS[prop] = fullAttr;
|
|
204
|
+
selector += `, ${sel}`;
|
|
205
|
+
|
|
206
|
+
if (addListeners) {
|
|
207
|
+
requestAnimationFrame(() => {
|
|
208
|
+
const config = { attrFilter: { [prop]: sel }, signal };
|
|
209
|
+
[base, ...base.querySelectorAll(sel)].forEach(el => _addListeners(el, config));
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return fullAttr;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Add listeners to an element and its children, matching a generated query based on registered attributes
|
|
219
|
+
*
|
|
220
|
+
* @param {Element|Document} target Root node to add listeners from
|
|
221
|
+
* @param {object} options
|
|
222
|
+
* @param {AbortSignal} [options.signal] Optional signal to remove event listeners
|
|
223
|
+
* @returns {Element|Document} Returns the passed target node
|
|
224
|
+
*/
|
|
225
|
+
export function attachListeners(target, { signal } = {}) {
|
|
226
|
+
const nodes = target instanceof Element && target.matches(selector)
|
|
227
|
+
? [target, ...target.querySelectorAll(selector)]
|
|
228
|
+
: target.querySelectorAll(selector);
|
|
229
|
+
|
|
230
|
+
nodes.forEach(el => _addListeners(el, { signal }));
|
|
231
|
+
|
|
232
|
+
return target;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Add a node to the `MutationObserver` to observe attributes and add/remove event listeners
|
|
236
|
+
*
|
|
237
|
+
* @param {Document|Element} root Element to observe attributes on
|
|
238
|
+
*/
|
|
239
|
+
export function observeEvents(root = document) {
|
|
240
|
+
attachListeners(root);
|
|
241
|
+
observer.observe(root, {
|
|
242
|
+
subtree: true,
|
|
243
|
+
childList:true,
|
|
244
|
+
attributes: true,
|
|
245
|
+
attributeOldValue: true,
|
|
246
|
+
attributeFilter: eventAttrs,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Disconnects the `MutationObserver`, disabling observing of all attribute changes
|
|
252
|
+
*
|
|
253
|
+
* @returns {void}
|
|
254
|
+
*/
|
|
255
|
+
export const disconnectEventsObserver = () => observer.disconnect();
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Register a global error handler callback
|
|
259
|
+
*
|
|
260
|
+
* @param {Function} callback Callback to register as a global error handler
|
|
261
|
+
* @param {EventInit} config Typical event listener config object
|
|
262
|
+
*/
|
|
263
|
+
export function setGlobalErrorHandler(callback, { capture, once, passive, signal } = {}) {
|
|
264
|
+
if (callback instanceof Function) {
|
|
265
|
+
globalThis.addEventListener('error', callback, { capture, once, passive, signal });
|
|
266
|
+
} else {
|
|
267
|
+
throw new TypeError('Callback is not a function.');
|
|
268
|
+
}
|
|
269
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aegisjsproject/callback-registry",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": " A callback registry for AegisJSProject",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"aegis",
|
|
7
|
+
"callback-registry",
|
|
8
|
+
"event-binding",
|
|
9
|
+
"declarative-dom"
|
|
10
|
+
],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./callbacks.cjs",
|
|
13
|
+
"module": "./callbackRegistry.js",
|
|
14
|
+
"unpkg": "./callbackRegistry.mjs",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": "./callbackRegistry.js",
|
|
18
|
+
"require": "./callbacks.cjs"
|
|
19
|
+
},
|
|
20
|
+
"./*.js": {
|
|
21
|
+
"import": "./*.js",
|
|
22
|
+
"require": "./*.cjs"
|
|
23
|
+
},
|
|
24
|
+
"./*.mjs": {
|
|
25
|
+
"import": "./*.js",
|
|
26
|
+
"require": "./*.cjs"
|
|
27
|
+
},
|
|
28
|
+
"./*.cjs": {
|
|
29
|
+
"import": "./*.js",
|
|
30
|
+
"require": "./*.cjs"
|
|
31
|
+
},
|
|
32
|
+
"./*": {
|
|
33
|
+
"import": "./*.js",
|
|
34
|
+
"require": "./*.cjs"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"private": false,
|
|
41
|
+
"scripts": {
|
|
42
|
+
"test": "npm run lint:js && npm run run:tests",
|
|
43
|
+
"preversion": "npm test && npm run build",
|
|
44
|
+
"prepare": "npm test && npm run build",
|
|
45
|
+
"lint:js": "eslint .",
|
|
46
|
+
"fix:js": "eslint . --fix",
|
|
47
|
+
"build": "npm run build:js",
|
|
48
|
+
"run:tests": "node --test",
|
|
49
|
+
"clean": "rm -f ./*.cjs",
|
|
50
|
+
"build:js": "npm run clean && rollup -c rollup.config.js",
|
|
51
|
+
"create:lock": "npm i --package-lock-only --ignore-scripts --no-audit --no-fund",
|
|
52
|
+
"version:bump": "npm run version:bump:patch",
|
|
53
|
+
"version:bump:patch": "npm version --no-git-tag-version patch && npm run create:lock",
|
|
54
|
+
"version:bump:minor": "npm version --no-git-tag-version minor && npm run create:lock",
|
|
55
|
+
"version:bump:major": "npm version --no-git-tag-version major && npm run create:lock"
|
|
56
|
+
},
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "git+https://github.com/AegisJSProject/callback-registry.git"
|
|
60
|
+
},
|
|
61
|
+
"author": "Chris Zuber <admin@kernvalley.us>",
|
|
62
|
+
"license": "MIT",
|
|
63
|
+
"funding": [
|
|
64
|
+
{
|
|
65
|
+
"type": "librepay",
|
|
66
|
+
"url": "https://liberapay.com/shgysk8zer0"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"type": "github",
|
|
70
|
+
"url": "https://github.com/sponsors/shgysk8zer0"
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"bugs": {
|
|
74
|
+
"url": "https://github.com/AegisJSProject/callback-registry/issues"
|
|
75
|
+
},
|
|
76
|
+
"homepage": "https://github.com/AegisJSProject/callback-registry#readme",
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@rollup/plugin-node-resolve": "^15.3.0",
|
|
79
|
+
"@shgysk8zer0/eslint-config": "^1.0.1",
|
|
80
|
+
"@shgysk8zer0/js-utils": "^1.0.2",
|
|
81
|
+
"@shgysk8zer0/npm-utils": "^1.1.3"
|
|
82
|
+
}
|
|
83
|
+
}
|