abledom 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -12
- package/dist/esm/index.js +1153 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.d.cts +151 -0
- package/dist/index.d.ts +151 -0
- package/dist/index.js +1184 -0
- package/dist/index.js.map +1 -0
- package/dist/ts3.9/index.d.ts +139 -0
- package/package.json +45 -6
- package/CODE_OF_CONDUCT.md +0 -9
- package/SECURITY.md +0 -41
- package/SUPPORT.md +0 -25
- package/index.js +0 -0
|
@@ -0,0 +1,1153 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
|
|
5
|
+
// inline-file:/Users/marata/Documents/Work/abledom/src/ui/ui.css
|
|
6
|
+
var ui_default = "#abledom-report {\n bottom: 20px;\n display: flex;\n flex-direction: column;\n left: 10px;\n max-height: 80%;\n max-width: 60%;\n padding: 4px 8px;\n position: absolute;\n z-index: 100500;\n}\n\n.abledom-notification-container {\n backdrop-filter: blur(3px);\n border-radius: 8px;\n box-shadow: 0px 0px 4px rgba(127, 127, 127, 0.5);\n display: inline-flex;\n margin: 2px 0;\n}\n\n.abledom-notification {\n background-color: rgba(164, 2, 2, 0.7);\n border-radius: 8px;\n color: white;\n display: inline flex;\n font-family: Arial, Helvetica, sans-serif;\n font-size: 16px;\n line-height: 26px;\n padding: 4px;\n}\n\n.abledom-notification .button {\n all: unset;\n color: #000;\n cursor: pointer;\n background: linear-gradient(\n 180deg,\n rgba(255, 255, 255, 1) 0%,\n rgba(200, 200, 200, 1) 100%\n );\n border-radius: 6px;\n border: 1px solid rgba(255, 255, 255, 0.4);\n box-sizing: border-box;\n line-height: 0px;\n margin-right: 4px;\n max-height: 26px;\n padding: 2px;\n text-decoration: none;\n}\n\n.abledom-notification .button:hover {\n opacity: 0.7;\n}\n\n.abledom-notification .button.close {\n background: none;\n border-color: transparent;\n color: #fff;\n margin: 0;\n}\n\n.abledom-highlight {\n background-color: yellow;\n border: 1px solid red;\n box-sizing: border-box;\n display: none;\n opacity: 0.6;\n position: absolute;\n z-index: 100499;\n}\n";
|
|
7
|
+
|
|
8
|
+
// inline-file:/Users/marata/Documents/Work/abledom/src/ui/close.svg
|
|
9
|
+
var close_default = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor"\n stroke-width="2" stroke-linecap="round" stroke-linejoin="round"\n xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10"/>\n <line x1="15" y1="9" x2="9" y2="15"/>\n <line x1="9" y1="9" x2="15" y2="15"/>\n</svg>\n';
|
|
10
|
+
|
|
11
|
+
// inline-file:/Users/marata/Documents/Work/abledom/src/ui/help.svg
|
|
12
|
+
var help_default = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none"\n stroke="currentColor" stroke-width="2" stroke-linecap="round"\n stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10"/>\n <path d="M9.09 9a3 3 0 1 1 5.83 1c-.28 1.02-1.22 1.5-2.01 2.1-.76.58-1 1.1-1 2"/>\n <circle cx="12" cy="17" r="0.5"/>\n</svg>';
|
|
13
|
+
|
|
14
|
+
// inline-file:/Users/marata/Documents/Work/abledom/src/ui/log.svg
|
|
15
|
+
var log_default = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor"\n stroke-width="2" stroke-linecap="round" stroke-linejoin="round"\n xmlns="http://www.w3.org/2000/svg">\n <polyline points="4 7 9 12 4 17"/>\n <line x1="11" y1="19" x2="20" y2="19"/>\n</svg>';
|
|
16
|
+
|
|
17
|
+
// inline-file:/Users/marata/Documents/Work/abledom/src/ui/reveal.svg
|
|
18
|
+
var reveal_default = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor"\n stroke-width="2" stroke-linecap="round" stroke-linejoin="round"\n xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10"/>\n <line x1="12" y1="2" x2="12" y2="6"/>\n <line x1="12" y1="18" x2="12" y2="22"/>\n <line x1="2" y1="12" x2="6" y2="12"/>\n <line x1="18" y1="12" x2="22" y2="12"/>\n</svg>';
|
|
19
|
+
|
|
20
|
+
// src/ui/ui.ts
|
|
21
|
+
var _NotificationUI = class _NotificationUI {
|
|
22
|
+
constructor(element, error) {
|
|
23
|
+
__publicField(this, "_wrapper");
|
|
24
|
+
var _a;
|
|
25
|
+
const wrapper = this._wrapper = document.createElement(
|
|
26
|
+
"div"
|
|
27
|
+
);
|
|
28
|
+
const win = element.ownerDocument.defaultView;
|
|
29
|
+
if (!win) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (!_NotificationUI._highlight) {
|
|
33
|
+
_NotificationUI._highlight = new ElementHighlighter(win);
|
|
34
|
+
}
|
|
35
|
+
wrapper.__abledomui = true;
|
|
36
|
+
wrapper.innerHTML = `
|
|
37
|
+
<div class="abledom-notification-container"><div class="abledom-notification">
|
|
38
|
+
<button class="button" title="Log to Console">${log_default}</button>
|
|
39
|
+
<button class="button" title="Reveal in Elements panel">${reveal_default}</button>
|
|
40
|
+
${error.message}
|
|
41
|
+
<a href class="button close" href="/" title="Open help" target="_blank">${help_default}</a>
|
|
42
|
+
<button class="button close" class="close" title="Hide">${close_default}</button>
|
|
43
|
+
</div></div>`;
|
|
44
|
+
const container = wrapper.firstElementChild;
|
|
45
|
+
const buttons = wrapper.querySelectorAll("button");
|
|
46
|
+
const logButton = buttons[0];
|
|
47
|
+
const revealButton = buttons[1];
|
|
48
|
+
const closeButton = buttons[2];
|
|
49
|
+
logButton.onclick = () => {
|
|
50
|
+
console.error(
|
|
51
|
+
"AbleDOM violation: ",
|
|
52
|
+
"\nerror:",
|
|
53
|
+
error.message,
|
|
54
|
+
"\nelement:",
|
|
55
|
+
element,
|
|
56
|
+
...error.rel ? ["\nrelative:", error.rel] : []
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
const hasDevTools = !!((_a = win.__ableDOMDevtools) == null ? void 0 : _a.revealElement);
|
|
60
|
+
if (hasDevTools && win.document.body.contains(element)) {
|
|
61
|
+
revealButton.onclick = () => {
|
|
62
|
+
var _a2;
|
|
63
|
+
const revealElement = (_a2 = win.__ableDOMDevtools) == null ? void 0 : _a2.revealElement;
|
|
64
|
+
if (revealElement && win.document.body.contains(element)) {
|
|
65
|
+
revealElement(element).then((revealed) => {
|
|
66
|
+
if (!revealed) {
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
} else {
|
|
72
|
+
revealButton.style.display = "none";
|
|
73
|
+
}
|
|
74
|
+
closeButton.onclick = () => {
|
|
75
|
+
var _a2;
|
|
76
|
+
wrapper.style.display = "none";
|
|
77
|
+
(_a2 = _NotificationUI._highlight) == null ? void 0 : _a2.hide();
|
|
78
|
+
};
|
|
79
|
+
container.onmouseover = () => {
|
|
80
|
+
var _a2;
|
|
81
|
+
(_a2 = _NotificationUI._highlight) == null ? void 0 : _a2.highlight(element);
|
|
82
|
+
};
|
|
83
|
+
container.onmouseout = () => {
|
|
84
|
+
var _a2;
|
|
85
|
+
(_a2 = _NotificationUI._highlight) == null ? void 0 : _a2.hide();
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
static getElement(instance) {
|
|
89
|
+
return instance._wrapper;
|
|
90
|
+
}
|
|
91
|
+
dispose() {
|
|
92
|
+
this._wrapper.remove();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
__publicField(_NotificationUI, "_highlight");
|
|
96
|
+
var NotificationUI = _NotificationUI;
|
|
97
|
+
var NotificationsUI = class {
|
|
98
|
+
constructor(win) {
|
|
99
|
+
// private _window: Window;
|
|
100
|
+
__publicField(this, "_container");
|
|
101
|
+
__publicField(this, "_notifications", /* @__PURE__ */ new Set());
|
|
102
|
+
const container = this._container = document.createElement("div");
|
|
103
|
+
container.__abledomui = true;
|
|
104
|
+
container.id = "abledom-report";
|
|
105
|
+
container.innerHTML = `<style>${ui_default}</style>`;
|
|
106
|
+
win.document.body.appendChild(container);
|
|
107
|
+
}
|
|
108
|
+
addNotification(notification) {
|
|
109
|
+
if (this._notifications.has(notification)) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this._notifications.add(notification);
|
|
113
|
+
this._container.appendChild(NotificationUI.getElement(notification));
|
|
114
|
+
}
|
|
115
|
+
removeNotification(notification) {
|
|
116
|
+
if (!this._notifications.has(notification)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this._notifications.delete(notification);
|
|
120
|
+
this._container.removeChild(NotificationUI.getElement(notification));
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
var ElementHighlighter = class {
|
|
124
|
+
constructor(win) {
|
|
125
|
+
__publicField(this, "_window");
|
|
126
|
+
__publicField(this, "_container");
|
|
127
|
+
this._window = win;
|
|
128
|
+
const container = this._container = win.document.createElement("div");
|
|
129
|
+
container.__abledomui = true;
|
|
130
|
+
container.className = "abledom-highlight";
|
|
131
|
+
}
|
|
132
|
+
highlight(element) {
|
|
133
|
+
const rect = element.getBoundingClientRect();
|
|
134
|
+
if (rect.width === 0 || rect.height === 0) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const win = this._window;
|
|
138
|
+
const container = this._container;
|
|
139
|
+
const style = container.style;
|
|
140
|
+
if (container.parentElement !== win.document.body) {
|
|
141
|
+
win.document.body.appendChild(container);
|
|
142
|
+
}
|
|
143
|
+
style.width = `${rect.width}px`;
|
|
144
|
+
style.height = `${rect.height}px`;
|
|
145
|
+
style.top = `${rect.top}px`;
|
|
146
|
+
style.left = `${rect.left}px`;
|
|
147
|
+
container.style.display = "block";
|
|
148
|
+
}
|
|
149
|
+
hide() {
|
|
150
|
+
this._container.style.display = "none";
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
function isAbleDOMUIElement(element) {
|
|
154
|
+
for (let el = element; el; el = el.parentElement) {
|
|
155
|
+
if (el.__abledomui) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/utils.ts
|
|
163
|
+
var focusableElementSelector = [
|
|
164
|
+
"a[href]",
|
|
165
|
+
"button:not([disabled])",
|
|
166
|
+
"input:not([disabled])",
|
|
167
|
+
"select:not([disabled])",
|
|
168
|
+
"textarea:not([disabled])",
|
|
169
|
+
"*[tabindex]",
|
|
170
|
+
"*[contenteditable]",
|
|
171
|
+
"details > summary",
|
|
172
|
+
"audio[controls]",
|
|
173
|
+
"video[controls]"
|
|
174
|
+
].join(", ");
|
|
175
|
+
var AccessibilityAffectingElements = {
|
|
176
|
+
a: true,
|
|
177
|
+
area: true,
|
|
178
|
+
article: true,
|
|
179
|
+
aside: true,
|
|
180
|
+
body: true,
|
|
181
|
+
button: true,
|
|
182
|
+
datalist: true,
|
|
183
|
+
details: true,
|
|
184
|
+
dialog: true,
|
|
185
|
+
dl: true,
|
|
186
|
+
form: true,
|
|
187
|
+
h1: true,
|
|
188
|
+
h2: true,
|
|
189
|
+
h3: true,
|
|
190
|
+
h4: true,
|
|
191
|
+
h5: true,
|
|
192
|
+
h6: true,
|
|
193
|
+
hr: true,
|
|
194
|
+
iframe: true,
|
|
195
|
+
img: true,
|
|
196
|
+
input: true,
|
|
197
|
+
li: true,
|
|
198
|
+
link: true,
|
|
199
|
+
main: true,
|
|
200
|
+
menu: true,
|
|
201
|
+
menuitem: true,
|
|
202
|
+
meter: true,
|
|
203
|
+
nav: true,
|
|
204
|
+
object: true,
|
|
205
|
+
ol: true,
|
|
206
|
+
option: true,
|
|
207
|
+
progress: true,
|
|
208
|
+
section: true,
|
|
209
|
+
select: true,
|
|
210
|
+
tbody: true,
|
|
211
|
+
textarea: true,
|
|
212
|
+
tfoot: true,
|
|
213
|
+
th: true,
|
|
214
|
+
thead: true,
|
|
215
|
+
ul: true
|
|
216
|
+
};
|
|
217
|
+
var AccessibilityAttributes = {
|
|
218
|
+
role: true,
|
|
219
|
+
tabindex: true,
|
|
220
|
+
disabled: true,
|
|
221
|
+
required: true,
|
|
222
|
+
readonly: true,
|
|
223
|
+
hidden: true,
|
|
224
|
+
"aria-activedescendant": true,
|
|
225
|
+
"aria-atomic": true,
|
|
226
|
+
"aria-autocomplete": true,
|
|
227
|
+
"aria-busy": true,
|
|
228
|
+
"aria-checked": true,
|
|
229
|
+
"aria-colcount": true,
|
|
230
|
+
"aria-colindex": true,
|
|
231
|
+
"aria-colspan": true,
|
|
232
|
+
"aria-controls": true,
|
|
233
|
+
"aria-current": true,
|
|
234
|
+
"aria-describedby": true,
|
|
235
|
+
"aria-details": true,
|
|
236
|
+
"aria-disabled": true,
|
|
237
|
+
"aria-dropeffect": true,
|
|
238
|
+
"aria-errormessage": true,
|
|
239
|
+
"aria-expanded": true,
|
|
240
|
+
"aria-flowto": true,
|
|
241
|
+
"aria-grabbed": true,
|
|
242
|
+
"aria-haspopup": true,
|
|
243
|
+
"aria-hidden": true,
|
|
244
|
+
"aria-invalid": true,
|
|
245
|
+
"aria-keyshortcuts": true,
|
|
246
|
+
"aria-label": true,
|
|
247
|
+
"aria-labelledby": true,
|
|
248
|
+
"aria-level": true,
|
|
249
|
+
"aria-live": true,
|
|
250
|
+
"aria-modal": true,
|
|
251
|
+
"aria-multiline": true,
|
|
252
|
+
"aria-multiselectable": true,
|
|
253
|
+
"aria-orientation": true,
|
|
254
|
+
"aria-owns": true,
|
|
255
|
+
"aria-placeholder": true,
|
|
256
|
+
"aria-posinset": true,
|
|
257
|
+
"aria-pressed": true,
|
|
258
|
+
"aria-readonly": true,
|
|
259
|
+
"aria-relevant": true,
|
|
260
|
+
"aria-required": true,
|
|
261
|
+
"aria-roledescription": true,
|
|
262
|
+
"aria-rowcount": true,
|
|
263
|
+
"aria-rowindex": true,
|
|
264
|
+
"aria-rowspan": true,
|
|
265
|
+
"aria-selected": true,
|
|
266
|
+
"aria-setsize": true,
|
|
267
|
+
"aria-sort": true,
|
|
268
|
+
"aria-valuemax": true,
|
|
269
|
+
"aria-valuemin": true,
|
|
270
|
+
"aria-valuenow": true,
|
|
271
|
+
"aria-valuetext": true
|
|
272
|
+
};
|
|
273
|
+
function isAccessibilityAffectingElement(element) {
|
|
274
|
+
const tagName = element.tagName.toLowerCase();
|
|
275
|
+
const attributes = getAttributes(element);
|
|
276
|
+
return tagName in AccessibilityAffectingElements || hasAccessibilityAttribute(attributes);
|
|
277
|
+
}
|
|
278
|
+
function hasAccessibilityAttribute(attributes) {
|
|
279
|
+
for (let attrName of Object.keys(attributes)) {
|
|
280
|
+
if (attrName in AccessibilityAttributes && !(attrName === "role" && (attributes[attrName] === "none" || attributes[attrName] === "presentation"))) {
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
function getAttributes(element) {
|
|
287
|
+
const names = element.getAttributeNames();
|
|
288
|
+
const attributes = {};
|
|
289
|
+
for (let name of names) {
|
|
290
|
+
attributes[name] = element.getAttribute(name) || "";
|
|
291
|
+
}
|
|
292
|
+
return attributes;
|
|
293
|
+
}
|
|
294
|
+
function matchesSelector(element, selector) {
|
|
295
|
+
const matches = element.matches || element.matchesSelector || element.msMatchesSelector || element.webkitMatchesSelector;
|
|
296
|
+
return matches && matches.call(element, selector);
|
|
297
|
+
}
|
|
298
|
+
function isDisplayNone(element) {
|
|
299
|
+
var _a, _b;
|
|
300
|
+
const elementDocument = element.ownerDocument;
|
|
301
|
+
const computedStyle = (_a = elementDocument.defaultView) == null ? void 0 : _a.getComputedStyle(element);
|
|
302
|
+
if (element.offsetParent === null && elementDocument.body !== element && (computedStyle == null ? void 0 : computedStyle.position) !== "fixed") {
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
if ((computedStyle == null ? void 0 : computedStyle.visibility) === "hidden") {
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
if ((computedStyle == null ? void 0 : computedStyle.position) === "fixed") {
|
|
309
|
+
if (computedStyle.display === "none") {
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
if (((_b = element.parentElement) == null ? void 0 : _b.offsetParent) === null && elementDocument.body !== element.parentElement) {
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
function isElementVisible(element) {
|
|
319
|
+
if (!element.ownerDocument || element.nodeType !== Node.ELEMENT_NODE) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
if (isDisplayNone(element)) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
const rect = element.ownerDocument.body.getBoundingClientRect();
|
|
326
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
function getStackTrace() {
|
|
332
|
+
var _a;
|
|
333
|
+
const oldStackTraceLimit = Error.stackTraceLimit;
|
|
334
|
+
try {
|
|
335
|
+
Error.stackTraceLimit = 1e3;
|
|
336
|
+
throw new Error();
|
|
337
|
+
} catch (e) {
|
|
338
|
+
Error.stackTraceLimit = oldStackTraceLimit;
|
|
339
|
+
return ((_a = e.stack) == null ? void 0 : _a.split("\n").slice(1).map((line) => line.trim()).filter((line) => line.startsWith("at "))) || [];
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/rules/base.ts
|
|
344
|
+
var ValidationRule = class {
|
|
345
|
+
constructor() {
|
|
346
|
+
__publicField(this, "_window");
|
|
347
|
+
__publicField(this, "_exceptions", []);
|
|
348
|
+
}
|
|
349
|
+
static setWindow(instance, window2) {
|
|
350
|
+
instance._window = window2;
|
|
351
|
+
}
|
|
352
|
+
static checkExceptions(instance, element) {
|
|
353
|
+
for (const exception of instance._exceptions) {
|
|
354
|
+
if (exception(element)) {
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
addException(checkException) {
|
|
361
|
+
var _a;
|
|
362
|
+
(_a = this._exceptions) == null ? void 0 : _a.push(checkException);
|
|
363
|
+
}
|
|
364
|
+
removeException(checkException) {
|
|
365
|
+
const index = this._exceptions.indexOf(checkException);
|
|
366
|
+
if (index >= 0) {
|
|
367
|
+
this._exceptions.splice(index, 1);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Window is set when the rule is added to the AbleDOM instance.
|
|
372
|
+
*/
|
|
373
|
+
get window() {
|
|
374
|
+
return this._window;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// src/core.ts
|
|
379
|
+
var _ValidationErrorReport = class _ValidationErrorReport {
|
|
380
|
+
constructor(element, error, onHide) {
|
|
381
|
+
this.element = element;
|
|
382
|
+
this.error = error;
|
|
383
|
+
__publicField(this, "_notification");
|
|
384
|
+
__publicField(this, "_onHide");
|
|
385
|
+
this._onHide = onHide;
|
|
386
|
+
this.report();
|
|
387
|
+
}
|
|
388
|
+
update(error) {
|
|
389
|
+
this.error = error;
|
|
390
|
+
this.report();
|
|
391
|
+
}
|
|
392
|
+
report() {
|
|
393
|
+
if (!_ValidationErrorReport._notificationsUI) {
|
|
394
|
+
_ValidationErrorReport._notificationsUI = new NotificationsUI(window);
|
|
395
|
+
}
|
|
396
|
+
let notification = this._notification;
|
|
397
|
+
if (notification) {
|
|
398
|
+
} else {
|
|
399
|
+
notification = this._notification = new NotificationUI(
|
|
400
|
+
this.element,
|
|
401
|
+
this.error
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
_ValidationErrorReport._notificationsUI.addNotification(notification);
|
|
405
|
+
}
|
|
406
|
+
hide() {
|
|
407
|
+
var _a;
|
|
408
|
+
(_a = this._onHide) == null ? void 0 : _a.call(this);
|
|
409
|
+
}
|
|
410
|
+
remove() {
|
|
411
|
+
var _a;
|
|
412
|
+
if (this._notification) {
|
|
413
|
+
(_a = _ValidationErrorReport._notificationsUI) == null ? void 0 : _a.removeNotification(
|
|
414
|
+
this._notification
|
|
415
|
+
);
|
|
416
|
+
delete this._notification;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
__publicField(_ValidationErrorReport, "_notificationsUI");
|
|
421
|
+
var ValidationErrorReport = _ValidationErrorReport;
|
|
422
|
+
var AbleDOM = class {
|
|
423
|
+
constructor(win) {
|
|
424
|
+
__publicField(this, "_window");
|
|
425
|
+
__publicField(this, "_observer");
|
|
426
|
+
__publicField(this, "_clearValidationTimeout");
|
|
427
|
+
__publicField(this, "_elementsWithErrors", /* @__PURE__ */ new Set());
|
|
428
|
+
__publicField(this, "_changedElementIds", /* @__PURE__ */ new Set());
|
|
429
|
+
__publicField(this, "_elementsDependingOnId", /* @__PURE__ */ new Map());
|
|
430
|
+
__publicField(this, "_dependantIdsByElement", /* @__PURE__ */ new Map());
|
|
431
|
+
__publicField(this, "_idByElement", /* @__PURE__ */ new Map());
|
|
432
|
+
__publicField(this, "_rules", []);
|
|
433
|
+
__publicField(this, "_startFunc");
|
|
434
|
+
__publicField(this, "_isStarted", false);
|
|
435
|
+
__publicField(this, "_onFocusIn", (event) => {
|
|
436
|
+
var _a;
|
|
437
|
+
const target = event.target;
|
|
438
|
+
if (target && isAbleDOMUIElement(target)) {
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
for (const rule of this._rules) {
|
|
442
|
+
(_a = rule.focused) == null ? void 0 : _a.call(rule, event).then(
|
|
443
|
+
(focusError) => {
|
|
444
|
+
if (focusError) {
|
|
445
|
+
this._addValidationError(
|
|
446
|
+
focusError.element,
|
|
447
|
+
rule,
|
|
448
|
+
focusError.error
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
() => {
|
|
453
|
+
}
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
__publicField(this, "_onFocusOut", (event) => {
|
|
458
|
+
var _a;
|
|
459
|
+
const target = event.target;
|
|
460
|
+
if (target && isAbleDOMUIElement(target)) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
for (const rule of this._rules) {
|
|
464
|
+
(_a = rule.blurred) == null ? void 0 : _a.call(rule, event).then(
|
|
465
|
+
(focusError) => {
|
|
466
|
+
if (focusError) {
|
|
467
|
+
this._addValidationError(
|
|
468
|
+
focusError.element,
|
|
469
|
+
rule,
|
|
470
|
+
focusError.error
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
() => {
|
|
475
|
+
}
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
this._window = win;
|
|
480
|
+
const _elementsToValidate = /* @__PURE__ */ new Set();
|
|
481
|
+
const _elementsToRemove = /* @__PURE__ */ new Set();
|
|
482
|
+
win.document.addEventListener("focusin", this._onFocusIn, true);
|
|
483
|
+
win.document.addEventListener("focusout", this._onFocusOut, true);
|
|
484
|
+
this._observer = new MutationObserver((mutations) => {
|
|
485
|
+
var _a;
|
|
486
|
+
for (let mutation of mutations) {
|
|
487
|
+
if (mutation.target.__abledomui) {
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
const added = mutation.addedNodes;
|
|
491
|
+
const removed = mutation.removedNodes;
|
|
492
|
+
const attributeName = mutation.attributeName;
|
|
493
|
+
if (attributeName === "id") {
|
|
494
|
+
this._onElementId(mutation.target, false);
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
lookUp(mutation.target);
|
|
498
|
+
for (let i = 0; i < added.length; i++) {
|
|
499
|
+
findTargets(added[i], false);
|
|
500
|
+
}
|
|
501
|
+
for (let i = 0; i < removed.length; i++) {
|
|
502
|
+
findTargets(removed[i], true);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
(_a = this._clearValidationTimeout) == null ? void 0 : _a.call(this);
|
|
506
|
+
const _validationTimeout = win.setTimeout(() => {
|
|
507
|
+
delete this._clearValidationTimeout;
|
|
508
|
+
this._remove(_elementsToRemove);
|
|
509
|
+
this._validate(_elementsToValidate);
|
|
510
|
+
_elementsToRemove.clear();
|
|
511
|
+
_elementsToValidate.clear();
|
|
512
|
+
this._changedElementIds.clear();
|
|
513
|
+
}, 200);
|
|
514
|
+
this._clearValidationTimeout = () => {
|
|
515
|
+
win.clearTimeout(_validationTimeout);
|
|
516
|
+
delete this._clearValidationTimeout;
|
|
517
|
+
};
|
|
518
|
+
});
|
|
519
|
+
this._startFunc = () => {
|
|
520
|
+
delete this._startFunc;
|
|
521
|
+
this._observer.observe(win.document, {
|
|
522
|
+
childList: true,
|
|
523
|
+
subtree: true,
|
|
524
|
+
attributes: true
|
|
525
|
+
});
|
|
526
|
+
findTargets(win.document.body, false);
|
|
527
|
+
this._validate(_elementsToValidate);
|
|
528
|
+
_elementsToValidate.clear();
|
|
529
|
+
};
|
|
530
|
+
const addTarget = (node, removed) => {
|
|
531
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const id = node.id;
|
|
535
|
+
if (id) {
|
|
536
|
+
this._onElementId(node, removed);
|
|
537
|
+
}
|
|
538
|
+
if (removed) {
|
|
539
|
+
_elementsToRemove.add(node);
|
|
540
|
+
_elementsToValidate.delete(node);
|
|
541
|
+
} else {
|
|
542
|
+
_elementsToValidate.add(node);
|
|
543
|
+
_elementsToRemove.delete(node);
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
function lookUp(node) {
|
|
547
|
+
for (let n = node; n; n = n.parentNode) {
|
|
548
|
+
addTarget(n, false);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
function findTargets(node, removed) {
|
|
552
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
addTarget(node, removed);
|
|
556
|
+
const walker = win.document.createTreeWalker(
|
|
557
|
+
node,
|
|
558
|
+
NodeFilter.SHOW_ELEMENT,
|
|
559
|
+
(node2) => {
|
|
560
|
+
addTarget(node2, removed);
|
|
561
|
+
return NodeFilter.FILTER_SKIP;
|
|
562
|
+
}
|
|
563
|
+
);
|
|
564
|
+
if (walker) {
|
|
565
|
+
while (walker.nextNode()) {
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
_onElementId(element, removed) {
|
|
571
|
+
const elementId = element.getAttribute("id");
|
|
572
|
+
const oldElementId = this._idByElement.get(element);
|
|
573
|
+
if (oldElementId) {
|
|
574
|
+
this._changedElementIds.add(oldElementId);
|
|
575
|
+
const elements = this._elementsDependingOnId.get(oldElementId);
|
|
576
|
+
if (elements) {
|
|
577
|
+
elements.delete(element);
|
|
578
|
+
if (elements.size === 0) {
|
|
579
|
+
this._elementsDependingOnId.delete(oldElementId);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
if (elementId) {
|
|
584
|
+
this._changedElementIds.add(elementId);
|
|
585
|
+
let elements = this._elementsDependingOnId.get(elementId);
|
|
586
|
+
if (removed) {
|
|
587
|
+
this._idByElement.delete(element);
|
|
588
|
+
if (elements) {
|
|
589
|
+
elements.delete(element);
|
|
590
|
+
if (elements.size === 0) {
|
|
591
|
+
this._elementsDependingOnId.delete(elementId);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
} else {
|
|
595
|
+
this._idByElement.set(element, elementId);
|
|
596
|
+
if (!elements) {
|
|
597
|
+
elements = /* @__PURE__ */ new Set();
|
|
598
|
+
this._elementsDependingOnId.set(elementId, elements);
|
|
599
|
+
}
|
|
600
|
+
elements.add(element);
|
|
601
|
+
}
|
|
602
|
+
} else {
|
|
603
|
+
this._idByElement.delete(element);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
_addValidationError(element, rule, error) {
|
|
607
|
+
if (!error) {
|
|
608
|
+
this._removeElementError(element, rule);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (rule.anchored) {
|
|
612
|
+
let abledomOnElement = element.__abledom;
|
|
613
|
+
if (!abledomOnElement) {
|
|
614
|
+
abledomOnElement = element.__abledom = {};
|
|
615
|
+
}
|
|
616
|
+
let errors = abledomOnElement.errors;
|
|
617
|
+
if (!errors) {
|
|
618
|
+
errors = abledomOnElement.errors = /* @__PURE__ */ new Map();
|
|
619
|
+
}
|
|
620
|
+
const report = errors.get(rule);
|
|
621
|
+
if (report) {
|
|
622
|
+
report.update(error);
|
|
623
|
+
} else {
|
|
624
|
+
errors.set(rule, new ValidationErrorReport(element, error));
|
|
625
|
+
}
|
|
626
|
+
this._elementsWithErrors.add(element);
|
|
627
|
+
} else {
|
|
628
|
+
const report = new ValidationErrorReport(element, error, () => {
|
|
629
|
+
report.remove();
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
_removeElementError(element, rule) {
|
|
634
|
+
var _a;
|
|
635
|
+
if (!rule.anchored) {
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
const errors = (_a = element.__abledom) == null ? void 0 : _a.errors;
|
|
639
|
+
if (!errors) {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
const report = errors.get(rule);
|
|
643
|
+
if (report) {
|
|
644
|
+
report.remove();
|
|
645
|
+
errors.delete(rule);
|
|
646
|
+
}
|
|
647
|
+
if (errors.size === 0) {
|
|
648
|
+
this._elementsWithErrors.delete(element);
|
|
649
|
+
delete element.__abledom;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
_validate(elements) {
|
|
653
|
+
for (const id of this._changedElementIds) {
|
|
654
|
+
const dependingOnId = this._elementsDependingOnId.get(id);
|
|
655
|
+
if (dependingOnId) {
|
|
656
|
+
for (const element of dependingOnId) {
|
|
657
|
+
elements.add(element);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
elements.forEach((element) => {
|
|
662
|
+
var _a, _b, _c, _d, _e;
|
|
663
|
+
if (isAccessibilityAffectingElement(element) || ((_a = element.__abledom) == null ? void 0 : _a.errors)) {
|
|
664
|
+
const dependsOnIds = /* @__PURE__ */ new Set();
|
|
665
|
+
for (const rule of this._rules) {
|
|
666
|
+
if (((_b = rule.accept) == null ? void 0 : _b.call(rule, element)) === false) {
|
|
667
|
+
continue;
|
|
668
|
+
}
|
|
669
|
+
if (ValidationRule.checkExceptions(rule, element)) {
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
const validationResult = (_c = rule.validate) == null ? void 0 : _c.call(rule, element);
|
|
673
|
+
if (validationResult) {
|
|
674
|
+
this._addValidationError(element, rule, validationResult.error);
|
|
675
|
+
const ids = validationResult.dependsOnIds;
|
|
676
|
+
if (ids) {
|
|
677
|
+
for (const id of ids) {
|
|
678
|
+
dependsOnIds.add(id);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
} else if ((_e = (_d = element.__abledom) == null ? void 0 : _d.errors) == null ? void 0 : _e.has(rule)) {
|
|
682
|
+
this._removeElementError(element, rule);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
this._processElementDependingOnIds(
|
|
686
|
+
element,
|
|
687
|
+
dependsOnIds.size === 0 ? null : dependsOnIds
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
_processElementDependingOnIds(element, ids) {
|
|
693
|
+
let dependsOnIds = this._dependantIdsByElement.get(element);
|
|
694
|
+
if (!ids && !dependsOnIds) {
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
if (!dependsOnIds) {
|
|
698
|
+
dependsOnIds = /* @__PURE__ */ new Set();
|
|
699
|
+
}
|
|
700
|
+
if (!ids) {
|
|
701
|
+
ids = /* @__PURE__ */ new Set();
|
|
702
|
+
}
|
|
703
|
+
for (const id of dependsOnIds) {
|
|
704
|
+
if (!ids.has(id)) {
|
|
705
|
+
const elements = this._elementsDependingOnId.get(id);
|
|
706
|
+
if (elements) {
|
|
707
|
+
elements.delete(element);
|
|
708
|
+
if (elements.size === 0) {
|
|
709
|
+
this._elementsDependingOnId.delete(id);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
for (const id of ids) {
|
|
715
|
+
if (!dependsOnIds.has(id)) {
|
|
716
|
+
dependsOnIds.add(id);
|
|
717
|
+
let elements = this._elementsDependingOnId.get(id);
|
|
718
|
+
if (!elements) {
|
|
719
|
+
elements = /* @__PURE__ */ new Set();
|
|
720
|
+
this._elementsDependingOnId.set(id, elements);
|
|
721
|
+
}
|
|
722
|
+
elements.add(element);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
if (dependsOnIds.size === 0) {
|
|
726
|
+
this._dependantIdsByElement.delete(element);
|
|
727
|
+
} else {
|
|
728
|
+
this._dependantIdsByElement.set(element, dependsOnIds);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
_remove(elements) {
|
|
732
|
+
elements.forEach((element) => {
|
|
733
|
+
var _a, _b;
|
|
734
|
+
const rules = [...((_b = (_a = element.__abledom) == null ? void 0 : _a.errors) == null ? void 0 : _b.keys()) || []];
|
|
735
|
+
rules.forEach((rule) => this._removeElementError(element, rule));
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
addRule(rule) {
|
|
739
|
+
this._rules.push(rule);
|
|
740
|
+
}
|
|
741
|
+
removeRule(rule) {
|
|
742
|
+
var _a;
|
|
743
|
+
const index = this._rules.indexOf(rule);
|
|
744
|
+
if (index >= 0) {
|
|
745
|
+
const rule2 = this._rules[index];
|
|
746
|
+
this._rules.splice(index, 1);
|
|
747
|
+
(_a = rule2.stop) == null ? void 0 : _a.call(rule2);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
start() {
|
|
751
|
+
var _a, _b;
|
|
752
|
+
if (this._isStarted) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
this._isStarted = true;
|
|
756
|
+
for (const rule of this._rules) {
|
|
757
|
+
ValidationRule.setWindow(rule, this._window);
|
|
758
|
+
(_a = rule.start) == null ? void 0 : _a.call(rule);
|
|
759
|
+
}
|
|
760
|
+
(_b = this._startFunc) == null ? void 0 : _b.call(this);
|
|
761
|
+
}
|
|
762
|
+
dispose() {
|
|
763
|
+
var _a, _b;
|
|
764
|
+
this._window.document.addEventListener("focusin", this._onFocusIn, true);
|
|
765
|
+
this._window.document.addEventListener("focusout", this._onFocusOut, true);
|
|
766
|
+
this._remove(this._elementsWithErrors);
|
|
767
|
+
this._elementsWithErrors.clear();
|
|
768
|
+
this._dependantIdsByElement.clear();
|
|
769
|
+
this._elementsDependingOnId.clear();
|
|
770
|
+
this._idByElement.clear();
|
|
771
|
+
(_a = this._clearValidationTimeout) == null ? void 0 : _a.call(this);
|
|
772
|
+
for (const rule of this._rules) {
|
|
773
|
+
(_b = rule.stop) == null ? void 0 : _b.call(rule);
|
|
774
|
+
}
|
|
775
|
+
this._rules = [];
|
|
776
|
+
if (this._startFunc) {
|
|
777
|
+
delete this._startFunc;
|
|
778
|
+
} else {
|
|
779
|
+
this._observer.disconnect();
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
// src/rules/atomic.ts
|
|
785
|
+
var AtomicRule = class extends ValidationRule {
|
|
786
|
+
constructor() {
|
|
787
|
+
super(...arguments);
|
|
788
|
+
__publicField(this, "name", "atomic");
|
|
789
|
+
__publicField(this, "anchored", true);
|
|
790
|
+
}
|
|
791
|
+
accept(element) {
|
|
792
|
+
return matchesSelector(element, focusableElementSelector);
|
|
793
|
+
}
|
|
794
|
+
validate(element) {
|
|
795
|
+
const parentAtomic = document.evaluate(
|
|
796
|
+
`ancestor::*[
|
|
797
|
+
@role = 'button' or
|
|
798
|
+
@role = 'checkbox' or
|
|
799
|
+
@role = 'link' or
|
|
800
|
+
@role = 'menuitem' or
|
|
801
|
+
@role = 'menuitemcheckbox' or
|
|
802
|
+
@role = 'menuitemradio' or
|
|
803
|
+
@role = 'option' or
|
|
804
|
+
@role = 'radio' or
|
|
805
|
+
@role = 'switch' or
|
|
806
|
+
@role = 'tab' or
|
|
807
|
+
@role = 'treeitem' or
|
|
808
|
+
self::a or
|
|
809
|
+
self::button or
|
|
810
|
+
self::input or
|
|
811
|
+
self::option or
|
|
812
|
+
self::textarea
|
|
813
|
+
][1]`,
|
|
814
|
+
element,
|
|
815
|
+
null,
|
|
816
|
+
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
|
817
|
+
null
|
|
818
|
+
).snapshotItem(0);
|
|
819
|
+
if (parentAtomic) {
|
|
820
|
+
return {
|
|
821
|
+
error: {
|
|
822
|
+
id: "focusable-in-atomic",
|
|
823
|
+
message: "Focusable element inside atomic focusable.",
|
|
824
|
+
rel: parentAtomic
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
// src/rules/label.ts
|
|
833
|
+
var _keyboardEditableInputTypes = /* @__PURE__ */ new Set([
|
|
834
|
+
"text",
|
|
835
|
+
"password",
|
|
836
|
+
"email",
|
|
837
|
+
"search",
|
|
838
|
+
"tel",
|
|
839
|
+
"url",
|
|
840
|
+
"number",
|
|
841
|
+
"date",
|
|
842
|
+
"month",
|
|
843
|
+
"week",
|
|
844
|
+
"time",
|
|
845
|
+
"datetime-local"
|
|
846
|
+
]);
|
|
847
|
+
var FocusableElementLabelRule = class extends ValidationRule {
|
|
848
|
+
constructor() {
|
|
849
|
+
super(...arguments);
|
|
850
|
+
__publicField(this, "name", "FocusableElementLabelRule");
|
|
851
|
+
__publicField(this, "anchored", true);
|
|
852
|
+
}
|
|
853
|
+
_isAriaHidden(element) {
|
|
854
|
+
return document.evaluate(
|
|
855
|
+
`ancestor-or-self::*[@aria-hidden = 'true' or @hidden]`,
|
|
856
|
+
element,
|
|
857
|
+
null,
|
|
858
|
+
XPathResult.BOOLEAN_TYPE,
|
|
859
|
+
null
|
|
860
|
+
).booleanValue;
|
|
861
|
+
}
|
|
862
|
+
_hasLabel(element) {
|
|
863
|
+
var _a, _b;
|
|
864
|
+
const labels = element.labels;
|
|
865
|
+
if (labels && labels.length > 0) {
|
|
866
|
+
for (let i = 0; i < labels.length; i++) {
|
|
867
|
+
const label = labels[i];
|
|
868
|
+
if (label.innerText.trim()) {
|
|
869
|
+
return true;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (element.tagName === "IMG") {
|
|
874
|
+
if (element.alt.trim()) {
|
|
875
|
+
return true;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
const labelNodes = document.evaluate(
|
|
879
|
+
`(
|
|
880
|
+
.//@aria-label |
|
|
881
|
+
.//text() |
|
|
882
|
+
.//@title |
|
|
883
|
+
.//img/@alt |
|
|
884
|
+
.//input[@type = 'image']/@alt |
|
|
885
|
+
.//input[@type != 'hidden'][@type = 'submit' or @type = 'reset' or @type = 'button']/@value
|
|
886
|
+
)[not(ancestor-or-self::*[@aria-hidden = 'true' or @hidden])]`,
|
|
887
|
+
element,
|
|
888
|
+
null,
|
|
889
|
+
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
|
890
|
+
null
|
|
891
|
+
);
|
|
892
|
+
for (let i = 0; i < labelNodes.snapshotLength; i++) {
|
|
893
|
+
const val = (_b = (_a = labelNodes.snapshotItem(i)) == null ? void 0 : _a.nodeValue) == null ? void 0 : _b.trim();
|
|
894
|
+
if (val) {
|
|
895
|
+
return true;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
return false;
|
|
899
|
+
}
|
|
900
|
+
accept(element) {
|
|
901
|
+
return matchesSelector(element, focusableElementSelector);
|
|
902
|
+
}
|
|
903
|
+
validate(element) {
|
|
904
|
+
var _a, _b;
|
|
905
|
+
if (element.tagName === "INPUT") {
|
|
906
|
+
const type = element.type;
|
|
907
|
+
if (type === "hidden") {
|
|
908
|
+
return null;
|
|
909
|
+
}
|
|
910
|
+
if (_keyboardEditableInputTypes.has(type)) {
|
|
911
|
+
return null;
|
|
912
|
+
}
|
|
913
|
+
if (type === "image") {
|
|
914
|
+
if (element.alt.trim()) {
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
if (type === "submit" || type === "reset" || type === "button") {
|
|
919
|
+
if (element.value.trim()) {
|
|
920
|
+
return null;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if (this._isAriaHidden(element)) {
|
|
925
|
+
return null;
|
|
926
|
+
}
|
|
927
|
+
if (this._hasLabel(element)) {
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
const labelledByNodes = document.evaluate(
|
|
931
|
+
`.//@aria-labelledby[not(ancestor-or-self::*[@aria-hidden = 'true' or @hidden])]`,
|
|
932
|
+
element,
|
|
933
|
+
null,
|
|
934
|
+
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
|
935
|
+
null
|
|
936
|
+
);
|
|
937
|
+
const labelledByValues = [];
|
|
938
|
+
for (let i = 0; i < labelledByNodes.snapshotLength; i++) {
|
|
939
|
+
const val = (_b = (_a = labelledByNodes.snapshotItem(i)) == null ? void 0 : _a.value) == null ? void 0 : _b.trim().split(" ");
|
|
940
|
+
if (val == null ? void 0 : val.length) {
|
|
941
|
+
labelledByValues.push(...val);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
for (const id of labelledByValues) {
|
|
945
|
+
const labelElement = document.getElementById(id);
|
|
946
|
+
if (labelElement && this._hasLabel(labelElement)) {
|
|
947
|
+
return {
|
|
948
|
+
dependsOnIds: new Set(labelledByValues)
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
return {
|
|
953
|
+
error: isElementVisible(element) ? {
|
|
954
|
+
id: "focusable-element-label",
|
|
955
|
+
message: "Focusable element must have a non-empty text label."
|
|
956
|
+
} : void 0,
|
|
957
|
+
dependsOnIds: new Set(labelledByValues)
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
// src/rules/existingid.ts
|
|
963
|
+
var ExistingIdRule = class extends ValidationRule {
|
|
964
|
+
constructor() {
|
|
965
|
+
super(...arguments);
|
|
966
|
+
__publicField(this, "name", "existing-id");
|
|
967
|
+
__publicField(this, "anchored", true);
|
|
968
|
+
}
|
|
969
|
+
accept(element) {
|
|
970
|
+
return element.hasAttribute("aria-labelledby") || element.hasAttribute("aria-describedby") || element.tagName === "LABEL" && !!element.htmlFor;
|
|
971
|
+
}
|
|
972
|
+
validate(element) {
|
|
973
|
+
var _a, _b;
|
|
974
|
+
const ids = [
|
|
975
|
+
...((_a = element.getAttribute("aria-labelledby")) == null ? void 0 : _a.split(" ")) || [],
|
|
976
|
+
...((_b = element.getAttribute("aria-describedby")) == null ? void 0 : _b.split(" ")) || [],
|
|
977
|
+
...element.tagName === "LABEL" ? [element.htmlFor] : []
|
|
978
|
+
].filter((id) => !!id);
|
|
979
|
+
if (ids.length === 0) {
|
|
980
|
+
return null;
|
|
981
|
+
}
|
|
982
|
+
for (const id of ids) {
|
|
983
|
+
if (document.getElementById(id)) {
|
|
984
|
+
return {
|
|
985
|
+
dependsOnIds: new Set(ids)
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
return {
|
|
990
|
+
error: {
|
|
991
|
+
id: "missing-id",
|
|
992
|
+
message: `Elements with referenced ids do not extist.`
|
|
993
|
+
},
|
|
994
|
+
dependsOnIds: new Set(ids)
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
// src/rules/focuslost.ts
|
|
1000
|
+
var FocusLostRule = class extends ValidationRule {
|
|
1001
|
+
constructor() {
|
|
1002
|
+
super(...arguments);
|
|
1003
|
+
__publicField(this, "name", "focus-lost");
|
|
1004
|
+
__publicField(this, "anchored", false);
|
|
1005
|
+
__publicField(this, "_focusLostTimeout", 2e3);
|
|
1006
|
+
// For now reporting lost focus after 2 seconds of it being lost.
|
|
1007
|
+
__publicField(this, "_clearScheduledFocusLost");
|
|
1008
|
+
__publicField(this, "_focusedElement");
|
|
1009
|
+
__publicField(this, "_focusedElementPosition");
|
|
1010
|
+
__publicField(this, "_lastFocusStack");
|
|
1011
|
+
__publicField(this, "_lastBlurStack");
|
|
1012
|
+
}
|
|
1013
|
+
_serializeElementPosition(element) {
|
|
1014
|
+
const position = [];
|
|
1015
|
+
const parentElement = element.parentElement;
|
|
1016
|
+
if (!parentElement) {
|
|
1017
|
+
return position;
|
|
1018
|
+
}
|
|
1019
|
+
for (let el = parentElement; el; el = el.parentElement) {
|
|
1020
|
+
const tagName = el.tagName.toLowerCase();
|
|
1021
|
+
position.push(tagName);
|
|
1022
|
+
}
|
|
1023
|
+
return position;
|
|
1024
|
+
}
|
|
1025
|
+
async focused(event) {
|
|
1026
|
+
var _a;
|
|
1027
|
+
const target = event.target;
|
|
1028
|
+
(_a = this._clearScheduledFocusLost) == null ? void 0 : _a.call(this);
|
|
1029
|
+
if (target) {
|
|
1030
|
+
this._lastFocusStack = getStackTrace();
|
|
1031
|
+
this._focusedElement = target;
|
|
1032
|
+
this._focusedElementPosition = this._serializeElementPosition(target);
|
|
1033
|
+
}
|
|
1034
|
+
return null;
|
|
1035
|
+
}
|
|
1036
|
+
async blurred(event) {
|
|
1037
|
+
var _a;
|
|
1038
|
+
const target = event.target;
|
|
1039
|
+
const win = this.window;
|
|
1040
|
+
(_a = this._clearScheduledFocusLost) == null ? void 0 : _a.call(this);
|
|
1041
|
+
if (!target || !win || event.relatedTarget) {
|
|
1042
|
+
return null;
|
|
1043
|
+
}
|
|
1044
|
+
let focusLostTimer;
|
|
1045
|
+
let rejectPromise;
|
|
1046
|
+
const targetPosition = this._focusedElement === target ? this._focusedElementPosition : void 0;
|
|
1047
|
+
this._lastBlurStack = getStackTrace();
|
|
1048
|
+
this._focusedElement = void 0;
|
|
1049
|
+
this._focusedElementPosition = void 0;
|
|
1050
|
+
this._clearScheduledFocusLost = () => {
|
|
1051
|
+
delete this._clearScheduledFocusLost;
|
|
1052
|
+
rejectPromise == null ? void 0 : rejectPromise();
|
|
1053
|
+
if (focusLostTimer) {
|
|
1054
|
+
win.clearTimeout(focusLostTimer);
|
|
1055
|
+
focusLostTimer = void 0;
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
return new Promise((resolve, reject) => {
|
|
1059
|
+
rejectPromise = () => {
|
|
1060
|
+
rejectPromise = void 0;
|
|
1061
|
+
reject();
|
|
1062
|
+
};
|
|
1063
|
+
focusLostTimer = win.setTimeout(() => {
|
|
1064
|
+
focusLostTimer = void 0;
|
|
1065
|
+
rejectPromise = void 0;
|
|
1066
|
+
delete this._clearScheduledFocusLost;
|
|
1067
|
+
if (win.document.body && (!win.document.activeElement || win.document.activeElement === win.document.body) && (!win.document.body.contains(target) || !isElementVisible(target))) {
|
|
1068
|
+
resolve({
|
|
1069
|
+
element: target,
|
|
1070
|
+
position: targetPosition || [],
|
|
1071
|
+
error: {
|
|
1072
|
+
id: "focus-lost",
|
|
1073
|
+
message: "Focus lost.",
|
|
1074
|
+
stack: this._lastBlurStack,
|
|
1075
|
+
relStack: this._lastFocusStack
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
} else {
|
|
1079
|
+
resolve(null);
|
|
1080
|
+
}
|
|
1081
|
+
}, this._focusLostTimeout);
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
// src/rules/badfocus.ts
|
|
1087
|
+
var BadFocusRule = class extends ValidationRule {
|
|
1088
|
+
constructor() {
|
|
1089
|
+
super(...arguments);
|
|
1090
|
+
__publicField(this, "name", "bad-focus");
|
|
1091
|
+
__publicField(this, "anchored", false);
|
|
1092
|
+
__publicField(this, "_lastFocusStack");
|
|
1093
|
+
__publicField(this, "_lastBlurStack");
|
|
1094
|
+
__publicField(this, "_reject");
|
|
1095
|
+
}
|
|
1096
|
+
async focused() {
|
|
1097
|
+
var _a;
|
|
1098
|
+
this._lastFocusStack = getStackTrace();
|
|
1099
|
+
(_a = this._reject) == null ? void 0 : _a.call(this);
|
|
1100
|
+
return null;
|
|
1101
|
+
}
|
|
1102
|
+
async blurred() {
|
|
1103
|
+
var _a;
|
|
1104
|
+
(_a = this._reject) == null ? void 0 : _a.call(this);
|
|
1105
|
+
const win = this.window;
|
|
1106
|
+
if (!win) {
|
|
1107
|
+
return null;
|
|
1108
|
+
}
|
|
1109
|
+
this._lastBlurStack = getStackTrace();
|
|
1110
|
+
return new Promise((resolve, reject) => {
|
|
1111
|
+
let checkTimer;
|
|
1112
|
+
this._reject = () => {
|
|
1113
|
+
if (checkTimer) {
|
|
1114
|
+
win.clearTimeout(checkTimer);
|
|
1115
|
+
checkTimer = void 0;
|
|
1116
|
+
}
|
|
1117
|
+
this._reject = void 0;
|
|
1118
|
+
reject();
|
|
1119
|
+
};
|
|
1120
|
+
checkTimer = win.setTimeout(() => {
|
|
1121
|
+
checkTimer = void 0;
|
|
1122
|
+
this._reject = void 0;
|
|
1123
|
+
if (document.activeElement && !isElementVisible(document.activeElement)) {
|
|
1124
|
+
resolve({
|
|
1125
|
+
element: document.activeElement,
|
|
1126
|
+
error: {
|
|
1127
|
+
id: "bad-focus",
|
|
1128
|
+
message: "Focused stolen by invisible element.",
|
|
1129
|
+
stack: this._lastBlurStack,
|
|
1130
|
+
relStack: this._lastFocusStack
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
} else {
|
|
1134
|
+
resolve(null);
|
|
1135
|
+
}
|
|
1136
|
+
}, 100);
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
export {
|
|
1141
|
+
AbleDOM,
|
|
1142
|
+
AtomicRule,
|
|
1143
|
+
BadFocusRule,
|
|
1144
|
+
ExistingIdRule,
|
|
1145
|
+
FocusLostRule,
|
|
1146
|
+
FocusableElementLabelRule,
|
|
1147
|
+
ValidationRule
|
|
1148
|
+
};
|
|
1149
|
+
/*!
|
|
1150
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
1151
|
+
* Licensed under the MIT License.
|
|
1152
|
+
*/
|
|
1153
|
+
//# sourceMappingURL=index.js.map
|