@out-of-order/cli 0.1.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/LICENSE +21 -0
- package/README.md +52 -0
- package/dist/cli.js +101 -0
- package/dist/inject-overlay.global.js +3686 -0
- package/dist/inject.global.js +1891 -0
- package/package.json +47 -0
|
@@ -0,0 +1,3686 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(() => {
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __typeError = (msg) => {
|
|
5
|
+
throw TypeError(msg);
|
|
6
|
+
};
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
9
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
10
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
11
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
12
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
13
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
14
|
+
|
|
15
|
+
// ../../node_modules/.pnpm/tabbable@6.5.0/node_modules/tabbable/dist/index.esm.js
|
|
16
|
+
var candidateSelectors = ["input:not([inert]):not([inert] *)", "select:not([inert]):not([inert] *)", "textarea:not([inert]):not([inert] *)", "a[href]:not([inert]):not([inert] *)", "area[href]:not([inert]):not([inert] *)", "button:not([inert]):not([inert] *)", "[tabindex]:not(slot):not([inert]):not([inert] *)", "audio[controls]:not([inert]):not([inert] *)", "video[controls]:not([inert]):not([inert] *)", '[contenteditable]:not([contenteditable="false"]):not([inert]):not([inert] *)', "details>summary:first-of-type:not([inert]):not([inert] *)", "details:not([inert]):not([inert] *)"];
|
|
17
|
+
var candidateSelector = /* @__PURE__ */ candidateSelectors.join(",");
|
|
18
|
+
var NoElement = typeof Element === "undefined";
|
|
19
|
+
var matches = NoElement ? function() {
|
|
20
|
+
} : Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
|
|
21
|
+
var getRootNode = !NoElement && Element.prototype.getRootNode ? function(element) {
|
|
22
|
+
var _element$getRootNode;
|
|
23
|
+
return element === null || element === void 0 ? void 0 : (_element$getRootNode = element.getRootNode) === null || _element$getRootNode === void 0 ? void 0 : _element$getRootNode.call(element);
|
|
24
|
+
} : function(element) {
|
|
25
|
+
return element === null || element === void 0 ? void 0 : element.ownerDocument;
|
|
26
|
+
};
|
|
27
|
+
var _isInert = function isInert(node, lookUp) {
|
|
28
|
+
var _node$getAttribute;
|
|
29
|
+
if (lookUp === void 0) {
|
|
30
|
+
lookUp = true;
|
|
31
|
+
}
|
|
32
|
+
var inertAtt = node === null || node === void 0 ? void 0 : (_node$getAttribute = node.getAttribute) === null || _node$getAttribute === void 0 ? void 0 : _node$getAttribute.call(node, "inert");
|
|
33
|
+
var inert = inertAtt === "" || inertAtt === "true";
|
|
34
|
+
var result = inert || lookUp && node && // closest does not exist on shadow roots, so we fall back to a manual
|
|
35
|
+
// lookup upward, in case it is not defined.
|
|
36
|
+
(typeof node.closest === "function" ? node.closest("[inert]") : _isInert(node.parentNode));
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
var isContentEditable = function isContentEditable2(node) {
|
|
40
|
+
var _node$getAttribute2;
|
|
41
|
+
var attValue = node === null || node === void 0 ? void 0 : (_node$getAttribute2 = node.getAttribute) === null || _node$getAttribute2 === void 0 ? void 0 : _node$getAttribute2.call(node, "contenteditable");
|
|
42
|
+
return attValue === "" || attValue === "true";
|
|
43
|
+
};
|
|
44
|
+
var getCandidates = function getCandidates2(el, includeContainer, filter) {
|
|
45
|
+
if (_isInert(el)) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
var candidates = Array.prototype.slice.apply(el.querySelectorAll(candidateSelector));
|
|
49
|
+
if (includeContainer && matches.call(el, candidateSelector)) {
|
|
50
|
+
candidates.unshift(el);
|
|
51
|
+
}
|
|
52
|
+
candidates = candidates.filter(filter);
|
|
53
|
+
return candidates;
|
|
54
|
+
};
|
|
55
|
+
var _getCandidatesIteratively = function getCandidatesIteratively(elements, includeContainer, options) {
|
|
56
|
+
var candidates = [];
|
|
57
|
+
var elementsToCheck = Array.from(elements);
|
|
58
|
+
while (elementsToCheck.length) {
|
|
59
|
+
var element = elementsToCheck.shift();
|
|
60
|
+
if (_isInert(element, false)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (element.tagName === "SLOT") {
|
|
64
|
+
var assigned = element.assignedElements();
|
|
65
|
+
var content = assigned.length ? assigned : element.children;
|
|
66
|
+
var nestedCandidates = _getCandidatesIteratively(content, true, options);
|
|
67
|
+
if (options.flatten) {
|
|
68
|
+
candidates.push.apply(candidates, nestedCandidates);
|
|
69
|
+
} else {
|
|
70
|
+
candidates.push({
|
|
71
|
+
scopeParent: element,
|
|
72
|
+
candidates: nestedCandidates
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
var validCandidate = matches.call(element, candidateSelector);
|
|
77
|
+
if (validCandidate && options.filter(element) && (includeContainer || !elements.includes(element))) {
|
|
78
|
+
candidates.push(element);
|
|
79
|
+
}
|
|
80
|
+
var shadowRoot = element.shadowRoot || // check for an undisclosed shadow
|
|
81
|
+
typeof options.getShadowRoot === "function" && options.getShadowRoot(element);
|
|
82
|
+
var validShadowRoot = !_isInert(shadowRoot, false) && (!options.shadowRootFilter || options.shadowRootFilter(element));
|
|
83
|
+
if (shadowRoot && validShadowRoot) {
|
|
84
|
+
var _nestedCandidates = _getCandidatesIteratively(shadowRoot === true ? element.children : shadowRoot.children, true, options);
|
|
85
|
+
if (options.flatten) {
|
|
86
|
+
candidates.push.apply(candidates, _nestedCandidates);
|
|
87
|
+
} else {
|
|
88
|
+
candidates.push({
|
|
89
|
+
scopeParent: element,
|
|
90
|
+
candidates: _nestedCandidates
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
elementsToCheck.unshift.apply(elementsToCheck, element.children);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return candidates;
|
|
99
|
+
};
|
|
100
|
+
var hasTabIndex = function hasTabIndex2(node) {
|
|
101
|
+
return !isNaN(parseInt(node.getAttribute("tabindex"), 10));
|
|
102
|
+
};
|
|
103
|
+
var getTabIndex = function getTabIndex2(node) {
|
|
104
|
+
if (!node) {
|
|
105
|
+
throw new Error("No node provided");
|
|
106
|
+
}
|
|
107
|
+
if (node.tabIndex < 0) {
|
|
108
|
+
if ((/^(AUDIO|VIDEO|DETAILS)$/.test(node.tagName) || isContentEditable(node)) && !hasTabIndex(node)) {
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return node.tabIndex;
|
|
113
|
+
};
|
|
114
|
+
var getSortOrderTabIndex = function getSortOrderTabIndex2(node, isScope) {
|
|
115
|
+
var tabIndex = getTabIndex(node);
|
|
116
|
+
if (tabIndex < 0 && isScope && !hasTabIndex(node)) {
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
return tabIndex;
|
|
120
|
+
};
|
|
121
|
+
var sortOrderedTabbables = function sortOrderedTabbables2(a, b) {
|
|
122
|
+
return a.tabIndex === b.tabIndex ? a.documentOrder - b.documentOrder : a.tabIndex - b.tabIndex;
|
|
123
|
+
};
|
|
124
|
+
var isInput = function isInput2(node) {
|
|
125
|
+
return node.tagName === "INPUT";
|
|
126
|
+
};
|
|
127
|
+
var isHiddenInput = function isHiddenInput2(node) {
|
|
128
|
+
return isInput(node) && node.type === "hidden";
|
|
129
|
+
};
|
|
130
|
+
var isDetailsWithSummary = function isDetailsWithSummary2(node) {
|
|
131
|
+
var r = node.tagName === "DETAILS" && Array.prototype.slice.apply(node.children).some(function(child) {
|
|
132
|
+
return child.tagName === "SUMMARY";
|
|
133
|
+
});
|
|
134
|
+
return r;
|
|
135
|
+
};
|
|
136
|
+
var getCheckedRadio = function getCheckedRadio2(nodes, form) {
|
|
137
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
138
|
+
if (nodes[i].checked && nodes[i].form === form) {
|
|
139
|
+
return nodes[i];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var isTabbableRadio = function isTabbableRadio2(node) {
|
|
144
|
+
if (!node.name) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
var radioScope = node.form || getRootNode(node);
|
|
148
|
+
var queryRadios = function queryRadios2(name) {
|
|
149
|
+
return radioScope.querySelectorAll('input[type="radio"][name="' + name + '"]');
|
|
150
|
+
};
|
|
151
|
+
var radioSet;
|
|
152
|
+
if (typeof window !== "undefined" && typeof window.CSS !== "undefined" && typeof window.CSS.escape === "function") {
|
|
153
|
+
radioSet = queryRadios(window.CSS.escape(node.name));
|
|
154
|
+
} else {
|
|
155
|
+
try {
|
|
156
|
+
radioSet = queryRadios(node.name);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
console.error("Looks like you have a radio button with a name attribute containing invalid CSS selector characters and need the CSS.escape polyfill: %s", err.message);
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
var checked = getCheckedRadio(radioSet, node.form);
|
|
163
|
+
return !checked || checked === node;
|
|
164
|
+
};
|
|
165
|
+
var isRadio = function isRadio2(node) {
|
|
166
|
+
return isInput(node) && node.type === "radio";
|
|
167
|
+
};
|
|
168
|
+
var isNonTabbableRadio = function isNonTabbableRadio2(node) {
|
|
169
|
+
return isRadio(node) && !isTabbableRadio(node);
|
|
170
|
+
};
|
|
171
|
+
var isNodeAttached = function isNodeAttached2(node) {
|
|
172
|
+
var _nodeRoot;
|
|
173
|
+
var nodeRoot = node && getRootNode(node);
|
|
174
|
+
var nodeRootHost = (_nodeRoot = nodeRoot) === null || _nodeRoot === void 0 ? void 0 : _nodeRoot.host;
|
|
175
|
+
var attached = false;
|
|
176
|
+
if (nodeRoot && nodeRoot !== node) {
|
|
177
|
+
var _nodeRootHost, _nodeRootHost$ownerDo, _node$ownerDocument;
|
|
178
|
+
attached = !!((_nodeRootHost = nodeRootHost) !== null && _nodeRootHost !== void 0 && (_nodeRootHost$ownerDo = _nodeRootHost.ownerDocument) !== null && _nodeRootHost$ownerDo !== void 0 && _nodeRootHost$ownerDo.contains(nodeRootHost) || node !== null && node !== void 0 && (_node$ownerDocument = node.ownerDocument) !== null && _node$ownerDocument !== void 0 && _node$ownerDocument.contains(node));
|
|
179
|
+
while (!attached && nodeRootHost) {
|
|
180
|
+
var _nodeRoot2, _nodeRootHost2, _nodeRootHost2$ownerD;
|
|
181
|
+
nodeRoot = getRootNode(nodeRootHost);
|
|
182
|
+
nodeRootHost = (_nodeRoot2 = nodeRoot) === null || _nodeRoot2 === void 0 ? void 0 : _nodeRoot2.host;
|
|
183
|
+
attached = !!((_nodeRootHost2 = nodeRootHost) !== null && _nodeRootHost2 !== void 0 && (_nodeRootHost2$ownerD = _nodeRootHost2.ownerDocument) !== null && _nodeRootHost2$ownerD !== void 0 && _nodeRootHost2$ownerD.contains(nodeRootHost));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return attached;
|
|
187
|
+
};
|
|
188
|
+
var isZeroArea = function isZeroArea2(node) {
|
|
189
|
+
var _node$getBoundingClie = node.getBoundingClientRect(), width = _node$getBoundingClie.width, height = _node$getBoundingClie.height;
|
|
190
|
+
return width === 0 && height === 0;
|
|
191
|
+
};
|
|
192
|
+
var isHidden = function isHidden2(node, _ref) {
|
|
193
|
+
var displayCheck = _ref.displayCheck, getShadowRoot = _ref.getShadowRoot;
|
|
194
|
+
if (displayCheck === "full-native") {
|
|
195
|
+
if ("checkVisibility" in node) {
|
|
196
|
+
var visible = node.checkVisibility({
|
|
197
|
+
// Checking opacity might be desirable for some use cases, but natively,
|
|
198
|
+
// opacity zero elements _are_ focusable and tabbable.
|
|
199
|
+
checkOpacity: false,
|
|
200
|
+
opacityProperty: false,
|
|
201
|
+
contentVisibilityAuto: true,
|
|
202
|
+
visibilityProperty: true,
|
|
203
|
+
// This is an alias for `visibilityProperty`. Contemporary browsers
|
|
204
|
+
// support both. However, this alias has wider browser support (Chrome
|
|
205
|
+
// >= 105 and Firefox >= 106, vs. Chrome >= 121 and Firefox >= 122), so
|
|
206
|
+
// we include it anyway.
|
|
207
|
+
checkVisibilityCSS: true
|
|
208
|
+
});
|
|
209
|
+
return !visible;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
var _getComputedStyle = getComputedStyle(node), visibility = _getComputedStyle.visibility;
|
|
213
|
+
if (visibility === "hidden" || visibility === "collapse") {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
var isDirectSummary = matches.call(node, "details>summary:first-of-type");
|
|
217
|
+
var nodeUnderDetails = isDirectSummary ? node.parentElement : node;
|
|
218
|
+
if (matches.call(nodeUnderDetails, "details:not([open]) *")) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
if (!displayCheck || displayCheck === "full" || // full-native can run this branch when it falls through in case
|
|
222
|
+
// Element#checkVisibility is unsupported
|
|
223
|
+
displayCheck === "full-native" || displayCheck === "legacy-full") {
|
|
224
|
+
if (typeof getShadowRoot === "function") {
|
|
225
|
+
var originalNode = node;
|
|
226
|
+
while (node) {
|
|
227
|
+
var parentElement = node.parentElement;
|
|
228
|
+
var rootNode = getRootNode(node);
|
|
229
|
+
if (parentElement && !parentElement.shadowRoot && getShadowRoot(parentElement) === true) {
|
|
230
|
+
return isZeroArea(node);
|
|
231
|
+
} else if (node.assignedSlot) {
|
|
232
|
+
node = node.assignedSlot;
|
|
233
|
+
} else if (!parentElement && rootNode !== node.ownerDocument) {
|
|
234
|
+
node = rootNode.host;
|
|
235
|
+
} else {
|
|
236
|
+
node = parentElement;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
node = originalNode;
|
|
240
|
+
}
|
|
241
|
+
if (isNodeAttached(node)) {
|
|
242
|
+
return !node.getClientRects().length;
|
|
243
|
+
}
|
|
244
|
+
if (displayCheck !== "legacy-full") {
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
} else if (displayCheck === "non-zero-area") {
|
|
248
|
+
return isZeroArea(node);
|
|
249
|
+
}
|
|
250
|
+
return false;
|
|
251
|
+
};
|
|
252
|
+
var isDisabledFromFieldset = function isDisabledFromFieldset2(node) {
|
|
253
|
+
if (/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(node.tagName)) {
|
|
254
|
+
var parentNode = node.parentElement;
|
|
255
|
+
while (parentNode) {
|
|
256
|
+
if (parentNode.tagName === "FIELDSET" && parentNode.disabled) {
|
|
257
|
+
for (var i = 0; i < parentNode.children.length; i++) {
|
|
258
|
+
var child = parentNode.children.item(i);
|
|
259
|
+
if (child.tagName === "LEGEND") {
|
|
260
|
+
return matches.call(parentNode, "fieldset[disabled] *") ? true : !child.contains(node);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
parentNode = parentNode.parentElement;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return false;
|
|
269
|
+
};
|
|
270
|
+
var isNodeMatchingSelectorFocusable = function isNodeMatchingSelectorFocusable2(options, node) {
|
|
271
|
+
if (node.disabled || isHiddenInput(node) || isHidden(node, options) || // For a details element with a summary, the summary element gets the focus
|
|
272
|
+
isDetailsWithSummary(node) || isDisabledFromFieldset(node)) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
return true;
|
|
276
|
+
};
|
|
277
|
+
var isNodeMatchingSelectorTabbable = function isNodeMatchingSelectorTabbable2(options, node) {
|
|
278
|
+
if (isNonTabbableRadio(node) || getTabIndex(node) < 0 || !isNodeMatchingSelectorFocusable(options, node)) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
return true;
|
|
282
|
+
};
|
|
283
|
+
var isShadowRootTabbable = function isShadowRootTabbable2(shadowHostNode) {
|
|
284
|
+
var tabIndex = parseInt(shadowHostNode.getAttribute("tabindex"), 10);
|
|
285
|
+
if (isNaN(tabIndex) || tabIndex >= 0) {
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
return false;
|
|
289
|
+
};
|
|
290
|
+
var _sortByOrder = function sortByOrder(candidates) {
|
|
291
|
+
var regularTabbables = [];
|
|
292
|
+
var orderedTabbables = [];
|
|
293
|
+
candidates.forEach(function(item, i) {
|
|
294
|
+
var isScope = !!item.scopeParent;
|
|
295
|
+
var element = isScope ? item.scopeParent : item;
|
|
296
|
+
var candidateTabindex = getSortOrderTabIndex(element, isScope);
|
|
297
|
+
var elements = isScope ? _sortByOrder(item.candidates) : element;
|
|
298
|
+
if (candidateTabindex === 0) {
|
|
299
|
+
isScope ? regularTabbables.push.apply(regularTabbables, elements) : regularTabbables.push(element);
|
|
300
|
+
} else {
|
|
301
|
+
orderedTabbables.push({
|
|
302
|
+
documentOrder: i,
|
|
303
|
+
tabIndex: candidateTabindex,
|
|
304
|
+
item,
|
|
305
|
+
isScope,
|
|
306
|
+
content: elements
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
return orderedTabbables.sort(sortOrderedTabbables).reduce(function(acc, sortable) {
|
|
311
|
+
sortable.isScope ? acc.push.apply(acc, sortable.content) : acc.push(sortable.content);
|
|
312
|
+
return acc;
|
|
313
|
+
}, []).concat(regularTabbables);
|
|
314
|
+
};
|
|
315
|
+
var tabbable = function tabbable2(container, options) {
|
|
316
|
+
options = options || {};
|
|
317
|
+
var candidates;
|
|
318
|
+
if (options.getShadowRoot) {
|
|
319
|
+
candidates = _getCandidatesIteratively([container], options.includeContainer, {
|
|
320
|
+
filter: isNodeMatchingSelectorTabbable.bind(null, options),
|
|
321
|
+
flatten: false,
|
|
322
|
+
getShadowRoot: options.getShadowRoot,
|
|
323
|
+
shadowRootFilter: isShadowRootTabbable
|
|
324
|
+
});
|
|
325
|
+
} else {
|
|
326
|
+
candidates = getCandidates(container, options.includeContainer, isNodeMatchingSelectorTabbable.bind(null, options));
|
|
327
|
+
}
|
|
328
|
+
return _sortByOrder(candidates);
|
|
329
|
+
};
|
|
330
|
+
var focusableCandidateSelector = /* @__PURE__ */ candidateSelectors.concat("iframe:not([inert]):not([inert] *)").join(",");
|
|
331
|
+
var isFocusable = function isFocusable2(node, options) {
|
|
332
|
+
options = options || {};
|
|
333
|
+
if (!node) {
|
|
334
|
+
throw new Error("No node provided");
|
|
335
|
+
}
|
|
336
|
+
if (matches.call(node, focusableCandidateSelector) === false) {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
return isNodeMatchingSelectorFocusable(options, node);
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// ../core/src/overlay-classes.ts
|
|
343
|
+
var OVERLAY_CLASS_PREFIX = "ooo-";
|
|
344
|
+
|
|
345
|
+
// ../core/src/dom.ts
|
|
346
|
+
function closestAncestor(start, test) {
|
|
347
|
+
for (let node = start; node; node = node.parentElement) {
|
|
348
|
+
if (test(node)) {
|
|
349
|
+
return node;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
function selectorFor(element) {
|
|
355
|
+
const parts = [];
|
|
356
|
+
let node = element;
|
|
357
|
+
let depth = 0;
|
|
358
|
+
while (node && depth < 4) {
|
|
359
|
+
let part = node.tagName.toLowerCase();
|
|
360
|
+
if (node.id) {
|
|
361
|
+
part += `#${node.id}`;
|
|
362
|
+
parts.unshift(part);
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
const cls = (node.getAttribute("class") || "").trim().split(/\s+/).filter((c) => c && !c.startsWith(OVERLAY_CLASS_PREFIX));
|
|
366
|
+
if (cls.length) {
|
|
367
|
+
part += `.${cls[0]}`;
|
|
368
|
+
}
|
|
369
|
+
parts.unshift(part);
|
|
370
|
+
node = node.parentElement;
|
|
371
|
+
depth++;
|
|
372
|
+
}
|
|
373
|
+
return parts.join(" > ");
|
|
374
|
+
}
|
|
375
|
+
var NATIVE_FOR_ROLE = {
|
|
376
|
+
button: "<button>",
|
|
377
|
+
link: "<a href>",
|
|
378
|
+
checkbox: '<input type="checkbox">',
|
|
379
|
+
radio: '<input type="radio">',
|
|
380
|
+
switch: '<input type="checkbox" role="switch">',
|
|
381
|
+
slider: '<input type="range">',
|
|
382
|
+
spinbutton: '<input type="number">',
|
|
383
|
+
searchbox: '<input type="search">',
|
|
384
|
+
textbox: "<input> or <textarea>",
|
|
385
|
+
combobox: "<select>",
|
|
386
|
+
option: "<option>"
|
|
387
|
+
};
|
|
388
|
+
var COMPOSITE_ROLES_NO_NATIVE = [
|
|
389
|
+
"menuitem",
|
|
390
|
+
"menuitemcheckbox",
|
|
391
|
+
"menuitemradio",
|
|
392
|
+
"tab",
|
|
393
|
+
"treeitem"
|
|
394
|
+
];
|
|
395
|
+
var INTERACTIVE_ROLES = [...Object.keys(NATIVE_FOR_ROLE), ...COMPOSITE_ROLES_NO_NATIVE];
|
|
396
|
+
function isInteractive(element) {
|
|
397
|
+
const tag = element.tagName.toLowerCase();
|
|
398
|
+
if (tag === "a" && element.hasAttribute("href")) {
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
if (["button", "select", "textarea", "summary"].includes(tag)) {
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
if (tag === "input") {
|
|
405
|
+
const type = (element.getAttribute("type") || "text").toLowerCase();
|
|
406
|
+
return type !== "hidden";
|
|
407
|
+
}
|
|
408
|
+
const role = element.getAttribute("role");
|
|
409
|
+
return !!role && INTERACTIVE_ROLES.includes(role);
|
|
410
|
+
}
|
|
411
|
+
function hasExplicitName(element) {
|
|
412
|
+
return (element.getAttribute("aria-label") || "").trim() !== "" || (element.getAttribute("title") || "").trim() !== "";
|
|
413
|
+
}
|
|
414
|
+
function inAriaHidden(element) {
|
|
415
|
+
return element.closest('[aria-hidden="true"]') !== null;
|
|
416
|
+
}
|
|
417
|
+
function isInert2(element) {
|
|
418
|
+
return element.closest("[inert]") !== null;
|
|
419
|
+
}
|
|
420
|
+
var IGNORE_ATTRIBUTE = "data-ooo-ignore";
|
|
421
|
+
function isRuleIgnored(element, ruleId) {
|
|
422
|
+
const value = element.getAttribute(IGNORE_ATTRIBUTE);
|
|
423
|
+
if (value === null) {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
const ids = value.trim().split(/\s+/).filter(Boolean);
|
|
427
|
+
return ids.length === 0 || ids.includes(ruleId);
|
|
428
|
+
}
|
|
429
|
+
function isTransparent(element) {
|
|
430
|
+
const check = element.checkVisibility;
|
|
431
|
+
if (typeof check === "function") {
|
|
432
|
+
return !check.call(element, { opacityProperty: true });
|
|
433
|
+
}
|
|
434
|
+
return closestAncestor(element, (node) => parseFloat(getComputedStyle(node).opacity || "1") === 0) !== null;
|
|
435
|
+
}
|
|
436
|
+
function isScreenReaderOnly(element, rect = element.getBoundingClientRect()) {
|
|
437
|
+
if (rect.width > 2 || rect.height > 2) {
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
const style = getComputedStyle(element);
|
|
441
|
+
return style.clip !== "" && style.clip !== "auto" || style.clipPath !== "" && style.clipPath !== "none" || style.overflow === "hidden";
|
|
442
|
+
}
|
|
443
|
+
function isClipped(element, rect) {
|
|
444
|
+
for (let node = element.parentElement; node; node = node.parentElement) {
|
|
445
|
+
const containerRect = node.getBoundingClientRect();
|
|
446
|
+
if (containerRect.width === 0 && containerRect.height === 0) {
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
const style = getComputedStyle(node);
|
|
450
|
+
const outX = rect.right <= containerRect.left || rect.left >= containerRect.right;
|
|
451
|
+
const outY = rect.bottom <= containerRect.top || rect.top >= containerRect.bottom;
|
|
452
|
+
const clipX = style.overflowX === "clip";
|
|
453
|
+
const clipY = style.overflowY === "clip";
|
|
454
|
+
if (outX && clipX || outY && clipY) {
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
function isOffPage(element, rect) {
|
|
461
|
+
const win = element.ownerDocument?.defaultView;
|
|
462
|
+
if (!win) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
const pageRight = rect.right + win.scrollX;
|
|
466
|
+
const pageBottom = rect.bottom + win.scrollY;
|
|
467
|
+
return pageRight <= 0 || pageBottom <= 0;
|
|
468
|
+
}
|
|
469
|
+
function staticHiddenReason(element, rect) {
|
|
470
|
+
if (isScreenReaderOnly(element, rect)) {
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
if (element.tagName.toLowerCase() === "area") {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
if (isTransparent(element)) {
|
|
477
|
+
return "opacity:0, invisible but still tabbable";
|
|
478
|
+
}
|
|
479
|
+
if (rect.width < 1 || rect.height < 1) {
|
|
480
|
+
return "zero size, no visible target";
|
|
481
|
+
}
|
|
482
|
+
if (isOffPage(element, rect)) {
|
|
483
|
+
return "positioned off-screen (e.g. left:-9999px), invisible but still tabbable";
|
|
484
|
+
}
|
|
485
|
+
if (isClipped(element, rect)) {
|
|
486
|
+
return "clipped by an overflow:clip ancestor";
|
|
487
|
+
}
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
var REVEALING_PROPS = [
|
|
491
|
+
"opacity",
|
|
492
|
+
"visibility",
|
|
493
|
+
"display",
|
|
494
|
+
"position",
|
|
495
|
+
"left",
|
|
496
|
+
"right",
|
|
497
|
+
"top",
|
|
498
|
+
"bottom",
|
|
499
|
+
"inset",
|
|
500
|
+
"clip",
|
|
501
|
+
"clip-path",
|
|
502
|
+
"transform",
|
|
503
|
+
"translate",
|
|
504
|
+
"scale",
|
|
505
|
+
"width",
|
|
506
|
+
"height",
|
|
507
|
+
"max-width",
|
|
508
|
+
"max-height",
|
|
509
|
+
"overflow"
|
|
510
|
+
];
|
|
511
|
+
function revealSelectors(rules) {
|
|
512
|
+
return Array.from(rules).flatMap((rule) => {
|
|
513
|
+
if (rule instanceof CSSStyleRule) {
|
|
514
|
+
if (!/:focus/i.test(rule.selectorText) || !REVEALING_PROPS.some((p) => rule.style.getPropertyValue(p) !== "")) {
|
|
515
|
+
return [];
|
|
516
|
+
}
|
|
517
|
+
const resting = rule.selectorText.replace(/:focus(?:-visible|-within)?/gi, "").trim();
|
|
518
|
+
return resting ? [resting] : [];
|
|
519
|
+
}
|
|
520
|
+
return "cssRules" in rule ? revealSelectors(rule.cssRules) : [];
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
function focusRevealSelectors(doc) {
|
|
524
|
+
const sheets = [...Array.from(doc.styleSheets), ...doc.adoptedStyleSheets ?? []];
|
|
525
|
+
return sheets.flatMap((sheet) => {
|
|
526
|
+
try {
|
|
527
|
+
return revealSelectors(sheet.cssRules);
|
|
528
|
+
} catch {
|
|
529
|
+
return [];
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
function hiddenReason(element, rect, revealOnFocus = []) {
|
|
534
|
+
const reason = staticHiddenReason(element, rect);
|
|
535
|
+
if (!reason) {
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
const revealed = revealOnFocus.some((selector) => {
|
|
539
|
+
try {
|
|
540
|
+
return element.matches(selector);
|
|
541
|
+
} catch {
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
return revealed ? null : reason;
|
|
546
|
+
}
|
|
547
|
+
var NATIVE_INTERACTIVE_TAGS = ["a", "button", "input", "select", "textarea", "summary", "option"];
|
|
548
|
+
function looksClickable(element) {
|
|
549
|
+
const tag = element.tagName.toLowerCase();
|
|
550
|
+
if (NATIVE_INTERACTIVE_TAGS.includes(tag)) {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
const role = element.getAttribute("role");
|
|
554
|
+
if (role && INTERACTIVE_ROLES.includes(role)) {
|
|
555
|
+
return true;
|
|
556
|
+
}
|
|
557
|
+
return element.hasAttribute("onclick");
|
|
558
|
+
}
|
|
559
|
+
function nativeReplacement(element) {
|
|
560
|
+
if (NATIVE_INTERACTIVE_TAGS.includes(element.tagName.toLowerCase())) {
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
const role = element.getAttribute("role");
|
|
564
|
+
if (!role) {
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
return NATIVE_FOR_ROLE[role] ?? null;
|
|
568
|
+
}
|
|
569
|
+
function isNativelyFocusable(element) {
|
|
570
|
+
const tag = element.tagName.toLowerCase();
|
|
571
|
+
if (tag === "a" || tag === "area") {
|
|
572
|
+
return element.hasAttribute("href");
|
|
573
|
+
}
|
|
574
|
+
if (["button", "select", "textarea", "iframe"].includes(tag)) {
|
|
575
|
+
return true;
|
|
576
|
+
}
|
|
577
|
+
if (tag === "input") {
|
|
578
|
+
return (element.getAttribute("type") || "text").toLowerCase() !== "hidden";
|
|
579
|
+
}
|
|
580
|
+
if (tag === "audio" || tag === "video") {
|
|
581
|
+
return element.hasAttribute("controls");
|
|
582
|
+
}
|
|
583
|
+
if (tag === "summary") {
|
|
584
|
+
const parent = element.parentElement;
|
|
585
|
+
return parent?.tagName.toLowerCase() === "details" && parent.querySelector("summary") === element;
|
|
586
|
+
}
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
var COMPOSITE_ROLES = [
|
|
590
|
+
"toolbar",
|
|
591
|
+
"tablist",
|
|
592
|
+
"menu",
|
|
593
|
+
"menubar",
|
|
594
|
+
"radiogroup",
|
|
595
|
+
"listbox",
|
|
596
|
+
"tree",
|
|
597
|
+
"grid"
|
|
598
|
+
];
|
|
599
|
+
function compositeAncestor(element) {
|
|
600
|
+
return closestAncestor(element.parentElement, (node) => {
|
|
601
|
+
const role = node.getAttribute("role");
|
|
602
|
+
return !!role && COMPOSITE_ROLES.includes(role);
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
function isFocusManaged(element) {
|
|
606
|
+
const tabindex = element.getAttribute("tabindex");
|
|
607
|
+
if (tabindex !== null && Number(tabindex) < 0) {
|
|
608
|
+
return true;
|
|
609
|
+
}
|
|
610
|
+
if (compositeAncestor(element)) {
|
|
611
|
+
return true;
|
|
612
|
+
}
|
|
613
|
+
return element.closest("[aria-activedescendant]") !== null;
|
|
614
|
+
}
|
|
615
|
+
function floatingAncestor(element) {
|
|
616
|
+
return closestAncestor(element, (node) => {
|
|
617
|
+
const pos = getComputedStyle(node).position;
|
|
618
|
+
return pos === "fixed" || pos === "sticky";
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
function isScrollContainer(element) {
|
|
622
|
+
const scrollable = (value) => value === "auto" || value === "scroll" || value === "overlay";
|
|
623
|
+
const style = getComputedStyle(element);
|
|
624
|
+
return scrollable(style.overflowX) || scrollable(style.overflowY);
|
|
625
|
+
}
|
|
626
|
+
function scrollAncestor(element) {
|
|
627
|
+
return closestAncestor(element.parentElement, isScrollContainer);
|
|
628
|
+
}
|
|
629
|
+
function isDisplayed(element) {
|
|
630
|
+
const check = element.checkVisibility;
|
|
631
|
+
if (typeof check === "function") {
|
|
632
|
+
return check.call(element, { visibilityProperty: true });
|
|
633
|
+
}
|
|
634
|
+
return closestAncestor(element, (node) => getComputedStyle(node).display === "none") === null;
|
|
635
|
+
}
|
|
636
|
+
function openModal(root) {
|
|
637
|
+
for (const element of root.querySelectorAll('dialog:modal, [aria-modal="true"]')) {
|
|
638
|
+
if (isDisplayed(element)) {
|
|
639
|
+
return element;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// ../../node_modules/.pnpm/dom-accessibility-api@0.7.1/node_modules/dom-accessibility-api/dist/polyfills/array.from.mjs
|
|
646
|
+
var toStr = Object.prototype.toString;
|
|
647
|
+
function isCallable(fn) {
|
|
648
|
+
return typeof fn === "function" || toStr.call(fn) === "[object Function]";
|
|
649
|
+
}
|
|
650
|
+
function toInteger(value) {
|
|
651
|
+
var number = Number(value);
|
|
652
|
+
if (isNaN(number)) {
|
|
653
|
+
return 0;
|
|
654
|
+
}
|
|
655
|
+
if (number === 0 || !isFinite(number)) {
|
|
656
|
+
return number;
|
|
657
|
+
}
|
|
658
|
+
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
|
|
659
|
+
}
|
|
660
|
+
var maxSafeInteger = Math.pow(2, 53) - 1;
|
|
661
|
+
function toLength(value) {
|
|
662
|
+
var len = toInteger(value);
|
|
663
|
+
return Math.min(Math.max(len, 0), maxSafeInteger);
|
|
664
|
+
}
|
|
665
|
+
function arrayFrom(arrayLike, mapFn) {
|
|
666
|
+
var C = Array;
|
|
667
|
+
var items = Object(arrayLike);
|
|
668
|
+
if (arrayLike == null) {
|
|
669
|
+
throw new TypeError("Array.from requires an array-like object - not null or undefined");
|
|
670
|
+
}
|
|
671
|
+
if (typeof mapFn !== "undefined") {
|
|
672
|
+
if (!isCallable(mapFn)) {
|
|
673
|
+
throw new TypeError("Array.from: when provided, the second argument must be a function");
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
var len = toLength(items.length);
|
|
677
|
+
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
|
|
678
|
+
var k = 0;
|
|
679
|
+
var kValue;
|
|
680
|
+
while (k < len) {
|
|
681
|
+
kValue = items[k];
|
|
682
|
+
if (mapFn) {
|
|
683
|
+
A[k] = mapFn(kValue, k);
|
|
684
|
+
} else {
|
|
685
|
+
A[k] = kValue;
|
|
686
|
+
}
|
|
687
|
+
k += 1;
|
|
688
|
+
}
|
|
689
|
+
A.length = len;
|
|
690
|
+
return A;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// ../../node_modules/.pnpm/dom-accessibility-api@0.7.1/node_modules/dom-accessibility-api/dist/polyfills/SetLike.mjs
|
|
694
|
+
function _typeof(o) {
|
|
695
|
+
"@babel/helpers - typeof";
|
|
696
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o2) {
|
|
697
|
+
return typeof o2;
|
|
698
|
+
} : function(o2) {
|
|
699
|
+
return o2 && "function" == typeof Symbol && o2.constructor === Symbol && o2 !== Symbol.prototype ? "symbol" : typeof o2;
|
|
700
|
+
}, _typeof(o);
|
|
701
|
+
}
|
|
702
|
+
function _classCallCheck(instance, Constructor) {
|
|
703
|
+
if (!(instance instanceof Constructor)) {
|
|
704
|
+
throw new TypeError("Cannot call a class as a function");
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
function _defineProperties(target, props) {
|
|
708
|
+
for (var i = 0; i < props.length; i++) {
|
|
709
|
+
var descriptor = props[i];
|
|
710
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
711
|
+
descriptor.configurable = true;
|
|
712
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
713
|
+
Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
function _createClass(Constructor, protoProps, staticProps) {
|
|
717
|
+
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
718
|
+
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
719
|
+
Object.defineProperty(Constructor, "prototype", { writable: false });
|
|
720
|
+
return Constructor;
|
|
721
|
+
}
|
|
722
|
+
function _defineProperty(obj, key, value) {
|
|
723
|
+
key = _toPropertyKey(key);
|
|
724
|
+
if (key in obj) {
|
|
725
|
+
Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true });
|
|
726
|
+
} else {
|
|
727
|
+
obj[key] = value;
|
|
728
|
+
}
|
|
729
|
+
return obj;
|
|
730
|
+
}
|
|
731
|
+
function _toPropertyKey(t) {
|
|
732
|
+
var i = _toPrimitive(t, "string");
|
|
733
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
734
|
+
}
|
|
735
|
+
function _toPrimitive(t, r) {
|
|
736
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
737
|
+
var e = t[Symbol.toPrimitive];
|
|
738
|
+
if (void 0 !== e) {
|
|
739
|
+
var i = e.call(t, r || "default");
|
|
740
|
+
if ("object" != _typeof(i)) return i;
|
|
741
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
742
|
+
}
|
|
743
|
+
return ("string" === r ? String : Number)(t);
|
|
744
|
+
}
|
|
745
|
+
var SetLike = /* @__PURE__ */ (function() {
|
|
746
|
+
function SetLike2() {
|
|
747
|
+
var items = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : [];
|
|
748
|
+
_classCallCheck(this, SetLike2);
|
|
749
|
+
_defineProperty(this, "items", void 0);
|
|
750
|
+
this.items = items;
|
|
751
|
+
}
|
|
752
|
+
return _createClass(SetLike2, [{
|
|
753
|
+
key: "add",
|
|
754
|
+
value: function add(value) {
|
|
755
|
+
if (this.has(value) === false) {
|
|
756
|
+
this.items.push(value);
|
|
757
|
+
}
|
|
758
|
+
return this;
|
|
759
|
+
}
|
|
760
|
+
}, {
|
|
761
|
+
key: "clear",
|
|
762
|
+
value: function clear() {
|
|
763
|
+
this.items = [];
|
|
764
|
+
}
|
|
765
|
+
}, {
|
|
766
|
+
key: "delete",
|
|
767
|
+
value: function _delete(value) {
|
|
768
|
+
var previousLength = this.items.length;
|
|
769
|
+
this.items = this.items.filter(function(item) {
|
|
770
|
+
return item !== value;
|
|
771
|
+
});
|
|
772
|
+
return previousLength !== this.items.length;
|
|
773
|
+
}
|
|
774
|
+
}, {
|
|
775
|
+
key: "forEach",
|
|
776
|
+
value: function forEach(callbackfn) {
|
|
777
|
+
var _this = this;
|
|
778
|
+
this.items.forEach(function(item) {
|
|
779
|
+
callbackfn(item, item, _this);
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
}, {
|
|
783
|
+
key: "has",
|
|
784
|
+
value: function has(value) {
|
|
785
|
+
return this.items.indexOf(value) !== -1;
|
|
786
|
+
}
|
|
787
|
+
}, {
|
|
788
|
+
key: "size",
|
|
789
|
+
get: function get() {
|
|
790
|
+
return this.items.length;
|
|
791
|
+
}
|
|
792
|
+
}]);
|
|
793
|
+
})();
|
|
794
|
+
var SetLike_default = typeof Set === "undefined" ? Set : SetLike;
|
|
795
|
+
|
|
796
|
+
// ../../node_modules/.pnpm/dom-accessibility-api@0.7.1/node_modules/dom-accessibility-api/dist/getRole.mjs
|
|
797
|
+
function getLocalName(element) {
|
|
798
|
+
var _element$localName;
|
|
799
|
+
return (
|
|
800
|
+
// eslint-disable-next-line no-restricted-properties -- actual guard for environments without localName
|
|
801
|
+
(_element$localName = element.localName) !== null && _element$localName !== void 0 ? _element$localName : (
|
|
802
|
+
// eslint-disable-next-line no-restricted-properties -- required for the fallback
|
|
803
|
+
element.tagName.toLowerCase()
|
|
804
|
+
)
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
var localNameToRoleMappings = {
|
|
808
|
+
article: "article",
|
|
809
|
+
aside: "complementary",
|
|
810
|
+
button: "button",
|
|
811
|
+
datalist: "listbox",
|
|
812
|
+
dd: "definition",
|
|
813
|
+
details: "group",
|
|
814
|
+
dialog: "dialog",
|
|
815
|
+
dt: "term",
|
|
816
|
+
fieldset: "group",
|
|
817
|
+
figure: "figure",
|
|
818
|
+
// WARNING: Only with an accessible name
|
|
819
|
+
form: "form",
|
|
820
|
+
footer: "contentinfo",
|
|
821
|
+
h1: "heading",
|
|
822
|
+
h2: "heading",
|
|
823
|
+
h3: "heading",
|
|
824
|
+
h4: "heading",
|
|
825
|
+
h5: "heading",
|
|
826
|
+
h6: "heading",
|
|
827
|
+
header: "banner",
|
|
828
|
+
hr: "separator",
|
|
829
|
+
html: "document",
|
|
830
|
+
legend: "legend",
|
|
831
|
+
li: "listitem",
|
|
832
|
+
math: "math",
|
|
833
|
+
main: "main",
|
|
834
|
+
menu: "list",
|
|
835
|
+
nav: "navigation",
|
|
836
|
+
ol: "list",
|
|
837
|
+
optgroup: "group",
|
|
838
|
+
// WARNING: Only in certain context
|
|
839
|
+
option: "option",
|
|
840
|
+
output: "status",
|
|
841
|
+
progress: "progressbar",
|
|
842
|
+
// WARNING: Only with an accessible name
|
|
843
|
+
section: "region",
|
|
844
|
+
summary: "button",
|
|
845
|
+
table: "table",
|
|
846
|
+
tbody: "rowgroup",
|
|
847
|
+
textarea: "textbox",
|
|
848
|
+
tfoot: "rowgroup",
|
|
849
|
+
// WARNING: Only in certain context
|
|
850
|
+
td: "cell",
|
|
851
|
+
th: "columnheader",
|
|
852
|
+
thead: "rowgroup",
|
|
853
|
+
tr: "row",
|
|
854
|
+
ul: "list"
|
|
855
|
+
};
|
|
856
|
+
var prohibitedAttributes = {
|
|
857
|
+
caption: /* @__PURE__ */ new Set(["aria-label", "aria-labelledby"]),
|
|
858
|
+
code: /* @__PURE__ */ new Set(["aria-label", "aria-labelledby"]),
|
|
859
|
+
deletion: /* @__PURE__ */ new Set(["aria-label", "aria-labelledby"]),
|
|
860
|
+
emphasis: /* @__PURE__ */ new Set(["aria-label", "aria-labelledby"]),
|
|
861
|
+
generic: /* @__PURE__ */ new Set(["aria-label", "aria-labelledby", "aria-roledescription"]),
|
|
862
|
+
insertion: /* @__PURE__ */ new Set(["aria-label", "aria-labelledby"]),
|
|
863
|
+
none: /* @__PURE__ */ new Set(["aria-label", "aria-labelledby"]),
|
|
864
|
+
paragraph: /* @__PURE__ */ new Set(["aria-label", "aria-labelledby"]),
|
|
865
|
+
presentation: /* @__PURE__ */ new Set(["aria-label", "aria-labelledby"]),
|
|
866
|
+
strong: /* @__PURE__ */ new Set(["aria-label", "aria-labelledby"]),
|
|
867
|
+
subscript: /* @__PURE__ */ new Set(["aria-label", "aria-labelledby"]),
|
|
868
|
+
superscript: /* @__PURE__ */ new Set(["aria-label", "aria-labelledby"])
|
|
869
|
+
};
|
|
870
|
+
function hasGlobalAriaAttributes(element, role) {
|
|
871
|
+
return [
|
|
872
|
+
"aria-atomic",
|
|
873
|
+
"aria-busy",
|
|
874
|
+
"aria-controls",
|
|
875
|
+
"aria-current",
|
|
876
|
+
"aria-description",
|
|
877
|
+
"aria-describedby",
|
|
878
|
+
"aria-details",
|
|
879
|
+
// "disabled",
|
|
880
|
+
"aria-dropeffect",
|
|
881
|
+
// "errormessage",
|
|
882
|
+
"aria-flowto",
|
|
883
|
+
"aria-grabbed",
|
|
884
|
+
// "haspopup",
|
|
885
|
+
"aria-hidden",
|
|
886
|
+
// "invalid",
|
|
887
|
+
"aria-keyshortcuts",
|
|
888
|
+
"aria-label",
|
|
889
|
+
"aria-labelledby",
|
|
890
|
+
"aria-live",
|
|
891
|
+
"aria-owns",
|
|
892
|
+
"aria-relevant",
|
|
893
|
+
"aria-roledescription"
|
|
894
|
+
].some(function(attributeName) {
|
|
895
|
+
var _prohibitedAttributes;
|
|
896
|
+
return element.hasAttribute(attributeName) && !((_prohibitedAttributes = prohibitedAttributes[role]) !== null && _prohibitedAttributes !== void 0 && _prohibitedAttributes.has(attributeName));
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
function ignorePresentationalRole(element, implicitRole) {
|
|
900
|
+
return hasGlobalAriaAttributes(element, implicitRole);
|
|
901
|
+
}
|
|
902
|
+
function getRole(element) {
|
|
903
|
+
var explicitRole = getExplicitRole(element);
|
|
904
|
+
if (explicitRole === null || presentationRoles.indexOf(explicitRole) !== -1) {
|
|
905
|
+
var implicitRole = getImplicitRole(element);
|
|
906
|
+
if (presentationRoles.indexOf(explicitRole || "") === -1 || ignorePresentationalRole(element, implicitRole || "")) {
|
|
907
|
+
return implicitRole;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return explicitRole;
|
|
911
|
+
}
|
|
912
|
+
function getImplicitRole(element) {
|
|
913
|
+
var mappedByTag = localNameToRoleMappings[getLocalName(element)];
|
|
914
|
+
if (mappedByTag !== void 0) {
|
|
915
|
+
return mappedByTag;
|
|
916
|
+
}
|
|
917
|
+
switch (getLocalName(element)) {
|
|
918
|
+
case "a":
|
|
919
|
+
case "area":
|
|
920
|
+
case "link":
|
|
921
|
+
if (element.hasAttribute("href")) {
|
|
922
|
+
return "link";
|
|
923
|
+
}
|
|
924
|
+
break;
|
|
925
|
+
case "img":
|
|
926
|
+
if (element.getAttribute("alt") === "" && !ignorePresentationalRole(element, "img")) {
|
|
927
|
+
return "presentation";
|
|
928
|
+
}
|
|
929
|
+
return "img";
|
|
930
|
+
case "input": {
|
|
931
|
+
var _ref = element, type = _ref.type;
|
|
932
|
+
switch (type) {
|
|
933
|
+
case "button":
|
|
934
|
+
case "image":
|
|
935
|
+
case "reset":
|
|
936
|
+
case "submit":
|
|
937
|
+
return "button";
|
|
938
|
+
case "checkbox":
|
|
939
|
+
case "radio":
|
|
940
|
+
return type;
|
|
941
|
+
case "range":
|
|
942
|
+
return "slider";
|
|
943
|
+
case "email":
|
|
944
|
+
case "tel":
|
|
945
|
+
case "text":
|
|
946
|
+
case "url":
|
|
947
|
+
if (element.hasAttribute("list")) {
|
|
948
|
+
return "combobox";
|
|
949
|
+
}
|
|
950
|
+
return "textbox";
|
|
951
|
+
case "search":
|
|
952
|
+
if (element.hasAttribute("list")) {
|
|
953
|
+
return "combobox";
|
|
954
|
+
}
|
|
955
|
+
return "searchbox";
|
|
956
|
+
case "number":
|
|
957
|
+
return "spinbutton";
|
|
958
|
+
default:
|
|
959
|
+
return null;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
case "select":
|
|
963
|
+
if (element.hasAttribute("multiple") || element.size > 1) {
|
|
964
|
+
return "listbox";
|
|
965
|
+
}
|
|
966
|
+
return "combobox";
|
|
967
|
+
}
|
|
968
|
+
return null;
|
|
969
|
+
}
|
|
970
|
+
function getExplicitRole(element) {
|
|
971
|
+
var role = element.getAttribute("role");
|
|
972
|
+
if (role !== null) {
|
|
973
|
+
var explicitRole = role.trim().split(" ")[0];
|
|
974
|
+
if (explicitRole.length > 0) {
|
|
975
|
+
return explicitRole;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
return null;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// ../../node_modules/.pnpm/dom-accessibility-api@0.7.1/node_modules/dom-accessibility-api/dist/util.mjs
|
|
982
|
+
var presentationRoles = ["presentation", "none"];
|
|
983
|
+
function isElement(node) {
|
|
984
|
+
return node !== null && node.nodeType === node.ELEMENT_NODE;
|
|
985
|
+
}
|
|
986
|
+
function isHTMLTableCaptionElement(node) {
|
|
987
|
+
return isElement(node) && getLocalName(node) === "caption";
|
|
988
|
+
}
|
|
989
|
+
function isHTMLInputElement(node) {
|
|
990
|
+
return isElement(node) && getLocalName(node) === "input";
|
|
991
|
+
}
|
|
992
|
+
function isHTMLOptGroupElement(node) {
|
|
993
|
+
return isElement(node) && getLocalName(node) === "optgroup";
|
|
994
|
+
}
|
|
995
|
+
function isHTMLSelectElement(node) {
|
|
996
|
+
return isElement(node) && getLocalName(node) === "select";
|
|
997
|
+
}
|
|
998
|
+
function isHTMLTableElement(node) {
|
|
999
|
+
return isElement(node) && getLocalName(node) === "table";
|
|
1000
|
+
}
|
|
1001
|
+
function isHTMLTextAreaElement(node) {
|
|
1002
|
+
return isElement(node) && getLocalName(node) === "textarea";
|
|
1003
|
+
}
|
|
1004
|
+
function safeWindow(node) {
|
|
1005
|
+
var _ref = node.ownerDocument === null ? node : node.ownerDocument, defaultView = _ref.defaultView;
|
|
1006
|
+
if (defaultView === null) {
|
|
1007
|
+
throw new TypeError("no window available");
|
|
1008
|
+
}
|
|
1009
|
+
return defaultView;
|
|
1010
|
+
}
|
|
1011
|
+
function isHTMLFieldSetElement(node) {
|
|
1012
|
+
return isElement(node) && getLocalName(node) === "fieldset";
|
|
1013
|
+
}
|
|
1014
|
+
function isHTMLLegendElement(node) {
|
|
1015
|
+
return isElement(node) && getLocalName(node) === "legend";
|
|
1016
|
+
}
|
|
1017
|
+
function isHTMLSlotElement(node) {
|
|
1018
|
+
return isElement(node) && getLocalName(node) === "slot";
|
|
1019
|
+
}
|
|
1020
|
+
function isSVGElement(node) {
|
|
1021
|
+
return isElement(node) && node.ownerSVGElement !== void 0;
|
|
1022
|
+
}
|
|
1023
|
+
function isSVGSVGElement(node) {
|
|
1024
|
+
return isElement(node) && getLocalName(node) === "svg";
|
|
1025
|
+
}
|
|
1026
|
+
function isSVGTitleElement(node) {
|
|
1027
|
+
return isSVGElement(node) && getLocalName(node) === "title";
|
|
1028
|
+
}
|
|
1029
|
+
function queryIdRefs(node, attributeName) {
|
|
1030
|
+
if (isElement(node) && node.hasAttribute(attributeName)) {
|
|
1031
|
+
var ids = node.getAttribute(attributeName).split(" ");
|
|
1032
|
+
var root = node.getRootNode ? node.getRootNode() : node.ownerDocument;
|
|
1033
|
+
return ids.map(function(id) {
|
|
1034
|
+
return root.getElementById(id);
|
|
1035
|
+
}).filter(
|
|
1036
|
+
function(element) {
|
|
1037
|
+
return element !== null;
|
|
1038
|
+
}
|
|
1039
|
+
// TODO: why does this not narrow?
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
return [];
|
|
1043
|
+
}
|
|
1044
|
+
function hasAnyConcreteRoles(node, roles) {
|
|
1045
|
+
if (isElement(node)) {
|
|
1046
|
+
return roles.indexOf(getRole(node)) !== -1;
|
|
1047
|
+
}
|
|
1048
|
+
return false;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// ../../node_modules/.pnpm/dom-accessibility-api@0.7.1/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs
|
|
1052
|
+
function asFlatString(s) {
|
|
1053
|
+
return s.trim().replace(/\s\s+/g, " ");
|
|
1054
|
+
}
|
|
1055
|
+
function isHidden3(node, getComputedStyleImplementation) {
|
|
1056
|
+
if (!isElement(node)) {
|
|
1057
|
+
return false;
|
|
1058
|
+
}
|
|
1059
|
+
if (node.hasAttribute("hidden") || node.getAttribute("aria-hidden") === "true") {
|
|
1060
|
+
return true;
|
|
1061
|
+
}
|
|
1062
|
+
var style = getComputedStyleImplementation(node);
|
|
1063
|
+
return style.getPropertyValue("display") === "none" || style.getPropertyValue("visibility") === "hidden";
|
|
1064
|
+
}
|
|
1065
|
+
function isControl(node) {
|
|
1066
|
+
return hasAnyConcreteRoles(node, ["button", "combobox", "listbox", "textbox"]) || hasAbstractRole(node, "range");
|
|
1067
|
+
}
|
|
1068
|
+
function hasAbstractRole(node, role) {
|
|
1069
|
+
if (!isElement(node)) {
|
|
1070
|
+
return false;
|
|
1071
|
+
}
|
|
1072
|
+
switch (role) {
|
|
1073
|
+
case "range":
|
|
1074
|
+
return hasAnyConcreteRoles(node, ["meter", "progressbar", "scrollbar", "slider", "spinbutton"]);
|
|
1075
|
+
default:
|
|
1076
|
+
throw new TypeError("No knowledge about abstract role '".concat(role, "'. This is likely a bug :("));
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
function querySelectorAllSubtree(element, selectors) {
|
|
1080
|
+
var elements = arrayFrom(element.querySelectorAll(selectors));
|
|
1081
|
+
queryIdRefs(element, "aria-owns").forEach(function(root) {
|
|
1082
|
+
elements.push.apply(elements, arrayFrom(root.querySelectorAll(selectors)));
|
|
1083
|
+
});
|
|
1084
|
+
return elements;
|
|
1085
|
+
}
|
|
1086
|
+
function querySelectedOptions(listbox) {
|
|
1087
|
+
if (isHTMLSelectElement(listbox)) {
|
|
1088
|
+
return listbox.selectedOptions || querySelectorAllSubtree(listbox, "[selected]");
|
|
1089
|
+
}
|
|
1090
|
+
return querySelectorAllSubtree(listbox, '[aria-selected="true"]');
|
|
1091
|
+
}
|
|
1092
|
+
function isMarkedPresentational(node) {
|
|
1093
|
+
return hasAnyConcreteRoles(node, presentationRoles);
|
|
1094
|
+
}
|
|
1095
|
+
function isNativeHostLanguageTextAlternativeElement(node) {
|
|
1096
|
+
return isHTMLTableCaptionElement(node);
|
|
1097
|
+
}
|
|
1098
|
+
function allowsNameFromContent(node) {
|
|
1099
|
+
return hasAnyConcreteRoles(node, ["button", "cell", "checkbox", "columnheader", "gridcell", "heading", "label", "legend", "link", "menuitem", "menuitemcheckbox", "menuitemradio", "option", "radio", "row", "rowheader", "switch", "tab", "tooltip", "treeitem"]);
|
|
1100
|
+
}
|
|
1101
|
+
function isDescendantOfNativeHostLanguageTextAlternativeElement(node) {
|
|
1102
|
+
return false;
|
|
1103
|
+
}
|
|
1104
|
+
function getValueOfTextbox(element) {
|
|
1105
|
+
if (isHTMLInputElement(element) || isHTMLTextAreaElement(element)) {
|
|
1106
|
+
return element.value;
|
|
1107
|
+
}
|
|
1108
|
+
return element.textContent || "";
|
|
1109
|
+
}
|
|
1110
|
+
function getTextualContent(declaration) {
|
|
1111
|
+
var content = declaration.getPropertyValue("content");
|
|
1112
|
+
if (/^["'].*["']$/.test(content)) {
|
|
1113
|
+
return content.slice(1, -1);
|
|
1114
|
+
}
|
|
1115
|
+
return "";
|
|
1116
|
+
}
|
|
1117
|
+
function isLabelableElement(element) {
|
|
1118
|
+
var localName = getLocalName(element);
|
|
1119
|
+
return localName === "button" || localName === "input" && element.getAttribute("type") !== "hidden" || localName === "meter" || localName === "output" || localName === "progress" || localName === "select" || localName === "textarea";
|
|
1120
|
+
}
|
|
1121
|
+
function findLabelableElement(element) {
|
|
1122
|
+
if (isLabelableElement(element)) {
|
|
1123
|
+
return element;
|
|
1124
|
+
}
|
|
1125
|
+
var labelableElement = null;
|
|
1126
|
+
element.childNodes.forEach(function(childNode) {
|
|
1127
|
+
if (labelableElement === null && isElement(childNode)) {
|
|
1128
|
+
var descendantLabelableElement = findLabelableElement(childNode);
|
|
1129
|
+
if (descendantLabelableElement !== null) {
|
|
1130
|
+
labelableElement = descendantLabelableElement;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
return labelableElement;
|
|
1135
|
+
}
|
|
1136
|
+
function getControlOfLabel(label) {
|
|
1137
|
+
if (label.control !== void 0) {
|
|
1138
|
+
return label.control;
|
|
1139
|
+
}
|
|
1140
|
+
var htmlFor = label.getAttribute("for");
|
|
1141
|
+
if (htmlFor !== null) {
|
|
1142
|
+
return label.ownerDocument.getElementById(htmlFor);
|
|
1143
|
+
}
|
|
1144
|
+
return findLabelableElement(label);
|
|
1145
|
+
}
|
|
1146
|
+
function getLabels(element) {
|
|
1147
|
+
var labelsProperty = element.labels;
|
|
1148
|
+
if (labelsProperty === null) {
|
|
1149
|
+
return labelsProperty;
|
|
1150
|
+
}
|
|
1151
|
+
if (labelsProperty !== void 0) {
|
|
1152
|
+
return arrayFrom(labelsProperty);
|
|
1153
|
+
}
|
|
1154
|
+
if (!isLabelableElement(element)) {
|
|
1155
|
+
return null;
|
|
1156
|
+
}
|
|
1157
|
+
var document2 = element.ownerDocument;
|
|
1158
|
+
return arrayFrom(document2.querySelectorAll("label")).filter(function(label) {
|
|
1159
|
+
return getControlOfLabel(label) === element;
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
function getSlotContents(slot) {
|
|
1163
|
+
var assignedNodes = slot.assignedNodes();
|
|
1164
|
+
if (assignedNodes.length === 0) {
|
|
1165
|
+
return arrayFrom(slot.childNodes);
|
|
1166
|
+
}
|
|
1167
|
+
return assignedNodes;
|
|
1168
|
+
}
|
|
1169
|
+
function computeTextAlternative(root) {
|
|
1170
|
+
var options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
|
|
1171
|
+
var consultedNodes = new SetLike_default();
|
|
1172
|
+
var computedStyles = typeof Map === "undefined" ? void 0 : /* @__PURE__ */ new Map();
|
|
1173
|
+
var window2 = safeWindow(root);
|
|
1174
|
+
var _options$compute = options.compute, compute = _options$compute === void 0 ? "name" : _options$compute, _options$computedStyl = options.computedStyleSupportsPseudoElements, computedStyleSupportsPseudoElements = _options$computedStyl === void 0 ? options.getComputedStyle !== void 0 : _options$computedStyl, _options$getComputedS = options.getComputedStyle, uncachedGetComputedStyle = _options$getComputedS === void 0 ? window2.getComputedStyle.bind(window2) : _options$getComputedS, _options$hidden = options.hidden, hidden = _options$hidden === void 0 ? false : _options$hidden;
|
|
1175
|
+
var getComputedStyle2 = function getComputedStyle3(el, pseudoElement) {
|
|
1176
|
+
if (pseudoElement !== void 0) {
|
|
1177
|
+
throw new Error("use uncachedGetComputedStyle directly for pseudo elements");
|
|
1178
|
+
}
|
|
1179
|
+
if (computedStyles === void 0) {
|
|
1180
|
+
return uncachedGetComputedStyle(el);
|
|
1181
|
+
}
|
|
1182
|
+
var cachedStyles = computedStyles.get(el);
|
|
1183
|
+
if (cachedStyles) {
|
|
1184
|
+
return cachedStyles;
|
|
1185
|
+
}
|
|
1186
|
+
var style = uncachedGetComputedStyle(el, pseudoElement);
|
|
1187
|
+
computedStyles.set(el, style);
|
|
1188
|
+
return style;
|
|
1189
|
+
};
|
|
1190
|
+
function computeMiscTextAlternative(node, context) {
|
|
1191
|
+
var accumulatedText = "";
|
|
1192
|
+
if (isElement(node) && computedStyleSupportsPseudoElements) {
|
|
1193
|
+
var pseudoBefore = uncachedGetComputedStyle(node, "::before");
|
|
1194
|
+
var beforeContent = getTextualContent(pseudoBefore);
|
|
1195
|
+
accumulatedText = "".concat(beforeContent, " ").concat(accumulatedText);
|
|
1196
|
+
}
|
|
1197
|
+
var childNodes = isHTMLSlotElement(node) ? getSlotContents(node) : arrayFrom(node.childNodes).concat(queryIdRefs(node, "aria-owns"));
|
|
1198
|
+
childNodes.forEach(function(child) {
|
|
1199
|
+
var result = computeTextAlternative2(child, {
|
|
1200
|
+
isEmbeddedInLabel: context.isEmbeddedInLabel,
|
|
1201
|
+
isReferenced: false,
|
|
1202
|
+
recursion: true
|
|
1203
|
+
});
|
|
1204
|
+
var display = isElement(child) ? getComputedStyle2(child).getPropertyValue("display") : "inline";
|
|
1205
|
+
var separator = display !== "inline" ? " " : "";
|
|
1206
|
+
accumulatedText += "".concat(separator).concat(result).concat(separator);
|
|
1207
|
+
});
|
|
1208
|
+
if (isElement(node) && computedStyleSupportsPseudoElements) {
|
|
1209
|
+
var pseudoAfter = uncachedGetComputedStyle(node, "::after");
|
|
1210
|
+
var afterContent = getTextualContent(pseudoAfter);
|
|
1211
|
+
accumulatedText = "".concat(accumulatedText, " ").concat(afterContent);
|
|
1212
|
+
}
|
|
1213
|
+
return accumulatedText.trim();
|
|
1214
|
+
}
|
|
1215
|
+
function useAttribute(element, attributeName) {
|
|
1216
|
+
var attribute = element.getAttributeNode(attributeName);
|
|
1217
|
+
if (attribute !== null && !consultedNodes.has(attribute) && attribute.value.trim() !== "") {
|
|
1218
|
+
consultedNodes.add(attribute);
|
|
1219
|
+
return attribute.value;
|
|
1220
|
+
}
|
|
1221
|
+
return null;
|
|
1222
|
+
}
|
|
1223
|
+
function computeTooltipAttributeValue(node) {
|
|
1224
|
+
if (!isElement(node)) {
|
|
1225
|
+
return null;
|
|
1226
|
+
}
|
|
1227
|
+
return useAttribute(node, "title");
|
|
1228
|
+
}
|
|
1229
|
+
function computeElementTextAlternative(node) {
|
|
1230
|
+
if (!isElement(node)) {
|
|
1231
|
+
return null;
|
|
1232
|
+
}
|
|
1233
|
+
if (isHTMLFieldSetElement(node)) {
|
|
1234
|
+
consultedNodes.add(node);
|
|
1235
|
+
var children = arrayFrom(node.childNodes);
|
|
1236
|
+
for (var i = 0; i < children.length; i += 1) {
|
|
1237
|
+
var child = children[i];
|
|
1238
|
+
if (isHTMLLegendElement(child)) {
|
|
1239
|
+
return computeTextAlternative2(child, {
|
|
1240
|
+
isEmbeddedInLabel: false,
|
|
1241
|
+
isReferenced: false,
|
|
1242
|
+
recursion: false
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
} else if (isHTMLTableElement(node)) {
|
|
1247
|
+
consultedNodes.add(node);
|
|
1248
|
+
var _children = arrayFrom(node.childNodes);
|
|
1249
|
+
for (var _i = 0; _i < _children.length; _i += 1) {
|
|
1250
|
+
var _child = _children[_i];
|
|
1251
|
+
if (isHTMLTableCaptionElement(_child)) {
|
|
1252
|
+
return computeTextAlternative2(_child, {
|
|
1253
|
+
isEmbeddedInLabel: false,
|
|
1254
|
+
isReferenced: false,
|
|
1255
|
+
recursion: false
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
} else if (isSVGSVGElement(node)) {
|
|
1260
|
+
consultedNodes.add(node);
|
|
1261
|
+
var _children2 = arrayFrom(node.childNodes);
|
|
1262
|
+
for (var _i2 = 0; _i2 < _children2.length; _i2 += 1) {
|
|
1263
|
+
var _child2 = _children2[_i2];
|
|
1264
|
+
if (isSVGTitleElement(_child2)) {
|
|
1265
|
+
return _child2.textContent;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
return null;
|
|
1269
|
+
} else if (getLocalName(node) === "img" || getLocalName(node) === "area") {
|
|
1270
|
+
var nameFromAlt = useAttribute(node, "alt");
|
|
1271
|
+
if (nameFromAlt !== null) {
|
|
1272
|
+
return nameFromAlt;
|
|
1273
|
+
}
|
|
1274
|
+
} else if (isHTMLOptGroupElement(node)) {
|
|
1275
|
+
var nameFromLabel = useAttribute(node, "label");
|
|
1276
|
+
if (nameFromLabel !== null) {
|
|
1277
|
+
return nameFromLabel;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
if (isHTMLInputElement(node) && (node.type === "button" || node.type === "submit" || node.type === "reset")) {
|
|
1281
|
+
var nameFromValue = useAttribute(node, "value");
|
|
1282
|
+
if (nameFromValue !== null) {
|
|
1283
|
+
return nameFromValue;
|
|
1284
|
+
}
|
|
1285
|
+
if (node.type === "submit") {
|
|
1286
|
+
return "Submit";
|
|
1287
|
+
}
|
|
1288
|
+
if (node.type === "reset") {
|
|
1289
|
+
return "Reset";
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
var labels = getLabels(node);
|
|
1293
|
+
if (labels !== null && labels.length !== 0) {
|
|
1294
|
+
consultedNodes.add(node);
|
|
1295
|
+
return arrayFrom(labels).map(function(element) {
|
|
1296
|
+
return computeTextAlternative2(element, {
|
|
1297
|
+
isEmbeddedInLabel: true,
|
|
1298
|
+
isReferenced: false,
|
|
1299
|
+
recursion: true
|
|
1300
|
+
});
|
|
1301
|
+
}).filter(function(label) {
|
|
1302
|
+
return label.length > 0;
|
|
1303
|
+
}).join(" ");
|
|
1304
|
+
}
|
|
1305
|
+
if (isHTMLInputElement(node) && node.type === "image") {
|
|
1306
|
+
var _nameFromAlt = useAttribute(node, "alt");
|
|
1307
|
+
if (_nameFromAlt !== null) {
|
|
1308
|
+
return _nameFromAlt;
|
|
1309
|
+
}
|
|
1310
|
+
var nameFromTitle = useAttribute(node, "title");
|
|
1311
|
+
if (nameFromTitle !== null) {
|
|
1312
|
+
return nameFromTitle;
|
|
1313
|
+
}
|
|
1314
|
+
return "Submit Query";
|
|
1315
|
+
}
|
|
1316
|
+
if (hasAnyConcreteRoles(node, ["button"])) {
|
|
1317
|
+
var nameFromSubTree = computeMiscTextAlternative(node, {
|
|
1318
|
+
isEmbeddedInLabel: false,
|
|
1319
|
+
isReferenced: false
|
|
1320
|
+
});
|
|
1321
|
+
if (nameFromSubTree !== "") {
|
|
1322
|
+
return nameFromSubTree;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
return null;
|
|
1326
|
+
}
|
|
1327
|
+
function computeTextAlternative2(current, context) {
|
|
1328
|
+
if (consultedNodes.has(current)) {
|
|
1329
|
+
return "";
|
|
1330
|
+
}
|
|
1331
|
+
if (!hidden && isHidden3(current, getComputedStyle2) && !context.isReferenced) {
|
|
1332
|
+
consultedNodes.add(current);
|
|
1333
|
+
return "";
|
|
1334
|
+
}
|
|
1335
|
+
var labelAttributeNode = isElement(current) ? current.getAttributeNode("aria-labelledby") : null;
|
|
1336
|
+
var labelElements = labelAttributeNode !== null && !consultedNodes.has(labelAttributeNode) ? queryIdRefs(current, "aria-labelledby") : [];
|
|
1337
|
+
if (compute === "name" && !context.isReferenced && labelElements.length > 0) {
|
|
1338
|
+
consultedNodes.add(labelAttributeNode);
|
|
1339
|
+
return labelElements.map(function(element) {
|
|
1340
|
+
return computeTextAlternative2(element, {
|
|
1341
|
+
isEmbeddedInLabel: context.isEmbeddedInLabel,
|
|
1342
|
+
isReferenced: true,
|
|
1343
|
+
// this isn't recursion as specified, otherwise we would skip
|
|
1344
|
+
// `aria-label` in
|
|
1345
|
+
// <input id="myself" aria-label="foo" aria-labelledby="myself"
|
|
1346
|
+
recursion: false
|
|
1347
|
+
});
|
|
1348
|
+
}).join(" ");
|
|
1349
|
+
}
|
|
1350
|
+
var skipToStep2E = context.recursion && isControl(current) && compute === "name";
|
|
1351
|
+
if (!skipToStep2E) {
|
|
1352
|
+
var ariaLabel = (isElement(current) && current.getAttribute("aria-label") || "").trim();
|
|
1353
|
+
if (ariaLabel !== "" && compute === "name") {
|
|
1354
|
+
consultedNodes.add(current);
|
|
1355
|
+
return ariaLabel;
|
|
1356
|
+
}
|
|
1357
|
+
if (!isMarkedPresentational(current)) {
|
|
1358
|
+
var elementTextAlternative = computeElementTextAlternative(current);
|
|
1359
|
+
if (elementTextAlternative !== null) {
|
|
1360
|
+
consultedNodes.add(current);
|
|
1361
|
+
return elementTextAlternative;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
if (hasAnyConcreteRoles(current, ["menu"])) {
|
|
1366
|
+
consultedNodes.add(current);
|
|
1367
|
+
return "";
|
|
1368
|
+
}
|
|
1369
|
+
if (skipToStep2E || context.isEmbeddedInLabel || context.isReferenced) {
|
|
1370
|
+
if (hasAnyConcreteRoles(current, ["combobox", "listbox"])) {
|
|
1371
|
+
consultedNodes.add(current);
|
|
1372
|
+
var selectedOptions = querySelectedOptions(current);
|
|
1373
|
+
if (selectedOptions.length === 0) {
|
|
1374
|
+
return isHTMLInputElement(current) ? current.value : "";
|
|
1375
|
+
}
|
|
1376
|
+
return arrayFrom(selectedOptions).map(function(selectedOption) {
|
|
1377
|
+
return computeTextAlternative2(selectedOption, {
|
|
1378
|
+
isEmbeddedInLabel: context.isEmbeddedInLabel,
|
|
1379
|
+
isReferenced: false,
|
|
1380
|
+
recursion: true
|
|
1381
|
+
});
|
|
1382
|
+
}).join(" ");
|
|
1383
|
+
}
|
|
1384
|
+
if (hasAbstractRole(current, "range")) {
|
|
1385
|
+
consultedNodes.add(current);
|
|
1386
|
+
if (current.hasAttribute("aria-valuetext")) {
|
|
1387
|
+
return current.getAttribute("aria-valuetext");
|
|
1388
|
+
}
|
|
1389
|
+
if (current.hasAttribute("aria-valuenow")) {
|
|
1390
|
+
return current.getAttribute("aria-valuenow");
|
|
1391
|
+
}
|
|
1392
|
+
return current.getAttribute("value") || "";
|
|
1393
|
+
}
|
|
1394
|
+
if (hasAnyConcreteRoles(current, ["textbox"])) {
|
|
1395
|
+
consultedNodes.add(current);
|
|
1396
|
+
return getValueOfTextbox(current);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
if (allowsNameFromContent(current) || isElement(current) && context.isReferenced || isNativeHostLanguageTextAlternativeElement(current) || isDescendantOfNativeHostLanguageTextAlternativeElement(current)) {
|
|
1400
|
+
var accumulatedText2F = computeMiscTextAlternative(current, {
|
|
1401
|
+
isEmbeddedInLabel: context.isEmbeddedInLabel,
|
|
1402
|
+
isReferenced: false
|
|
1403
|
+
});
|
|
1404
|
+
if (accumulatedText2F !== "") {
|
|
1405
|
+
consultedNodes.add(current);
|
|
1406
|
+
return accumulatedText2F;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
if (current.nodeType === current.TEXT_NODE) {
|
|
1410
|
+
consultedNodes.add(current);
|
|
1411
|
+
return current.textContent || "";
|
|
1412
|
+
}
|
|
1413
|
+
if (context.recursion) {
|
|
1414
|
+
consultedNodes.add(current);
|
|
1415
|
+
return computeMiscTextAlternative(current, {
|
|
1416
|
+
isEmbeddedInLabel: context.isEmbeddedInLabel,
|
|
1417
|
+
isReferenced: false
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
var tooltipAttributeValue = computeTooltipAttributeValue(current);
|
|
1421
|
+
if (tooltipAttributeValue !== null) {
|
|
1422
|
+
consultedNodes.add(current);
|
|
1423
|
+
return tooltipAttributeValue;
|
|
1424
|
+
}
|
|
1425
|
+
consultedNodes.add(current);
|
|
1426
|
+
return "";
|
|
1427
|
+
}
|
|
1428
|
+
return asFlatString(computeTextAlternative2(root, {
|
|
1429
|
+
isEmbeddedInLabel: false,
|
|
1430
|
+
// by spec computeAccessibleDescription starts with the referenced elements as roots
|
|
1431
|
+
isReferenced: compute === "description",
|
|
1432
|
+
recursion: false
|
|
1433
|
+
}));
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// ../../node_modules/.pnpm/dom-accessibility-api@0.7.1/node_modules/dom-accessibility-api/dist/accessible-description.mjs
|
|
1437
|
+
function _typeof2(o) {
|
|
1438
|
+
"@babel/helpers - typeof";
|
|
1439
|
+
return _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o2) {
|
|
1440
|
+
return typeof o2;
|
|
1441
|
+
} : function(o2) {
|
|
1442
|
+
return o2 && "function" == typeof Symbol && o2.constructor === Symbol && o2 !== Symbol.prototype ? "symbol" : typeof o2;
|
|
1443
|
+
}, _typeof2(o);
|
|
1444
|
+
}
|
|
1445
|
+
function ownKeys(e, r) {
|
|
1446
|
+
var t = Object.keys(e);
|
|
1447
|
+
if (Object.getOwnPropertySymbols) {
|
|
1448
|
+
var o = Object.getOwnPropertySymbols(e);
|
|
1449
|
+
r && (o = o.filter(function(r2) {
|
|
1450
|
+
return Object.getOwnPropertyDescriptor(e, r2).enumerable;
|
|
1451
|
+
})), t.push.apply(t, o);
|
|
1452
|
+
}
|
|
1453
|
+
return t;
|
|
1454
|
+
}
|
|
1455
|
+
function _objectSpread(e) {
|
|
1456
|
+
for (var r = 1; r < arguments.length; r++) {
|
|
1457
|
+
var t = null != arguments[r] ? arguments[r] : {};
|
|
1458
|
+
r % 2 ? ownKeys(Object(t), true).forEach(function(r2) {
|
|
1459
|
+
_defineProperty2(e, r2, t[r2]);
|
|
1460
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r2) {
|
|
1461
|
+
Object.defineProperty(e, r2, Object.getOwnPropertyDescriptor(t, r2));
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
return e;
|
|
1465
|
+
}
|
|
1466
|
+
function _defineProperty2(obj, key, value) {
|
|
1467
|
+
key = _toPropertyKey2(key);
|
|
1468
|
+
if (key in obj) {
|
|
1469
|
+
Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true });
|
|
1470
|
+
} else {
|
|
1471
|
+
obj[key] = value;
|
|
1472
|
+
}
|
|
1473
|
+
return obj;
|
|
1474
|
+
}
|
|
1475
|
+
function _toPropertyKey2(t) {
|
|
1476
|
+
var i = _toPrimitive2(t, "string");
|
|
1477
|
+
return "symbol" == _typeof2(i) ? i : i + "";
|
|
1478
|
+
}
|
|
1479
|
+
function _toPrimitive2(t, r) {
|
|
1480
|
+
if ("object" != _typeof2(t) || !t) return t;
|
|
1481
|
+
var e = t[Symbol.toPrimitive];
|
|
1482
|
+
if (void 0 !== e) {
|
|
1483
|
+
var i = e.call(t, r || "default");
|
|
1484
|
+
if ("object" != _typeof2(i)) return i;
|
|
1485
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
1486
|
+
}
|
|
1487
|
+
return ("string" === r ? String : Number)(t);
|
|
1488
|
+
}
|
|
1489
|
+
function computeAccessibleDescription(root) {
|
|
1490
|
+
var options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
|
|
1491
|
+
var description = queryIdRefs(root, "aria-describedby").map(function(element) {
|
|
1492
|
+
return computeTextAlternative(element, _objectSpread(_objectSpread({}, options), {}, {
|
|
1493
|
+
compute: "description"
|
|
1494
|
+
}));
|
|
1495
|
+
}).join(" ");
|
|
1496
|
+
if (description === "") {
|
|
1497
|
+
var ariaDescription = root.getAttribute("aria-description");
|
|
1498
|
+
description = ariaDescription === null ? "" : ariaDescription;
|
|
1499
|
+
}
|
|
1500
|
+
if (description === "") {
|
|
1501
|
+
var title = root.getAttribute("title");
|
|
1502
|
+
description = title === null ? "" : title;
|
|
1503
|
+
}
|
|
1504
|
+
return description;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
// ../../node_modules/.pnpm/dom-accessibility-api@0.7.1/node_modules/dom-accessibility-api/dist/accessible-name.mjs
|
|
1508
|
+
function prohibitsNaming(node) {
|
|
1509
|
+
return hasAnyConcreteRoles(node, ["caption", "code", "deletion", "emphasis", "generic", "insertion", "none", "paragraph", "presentation", "strong", "subscript", "superscript"]);
|
|
1510
|
+
}
|
|
1511
|
+
function computeAccessibleName(root) {
|
|
1512
|
+
var options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
|
|
1513
|
+
if (prohibitsNaming(root)) {
|
|
1514
|
+
return "";
|
|
1515
|
+
}
|
|
1516
|
+
return computeTextAlternative(root, options);
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
// ../core/src/rules.ts
|
|
1520
|
+
var ROW_TOLERANCE_PX = 8;
|
|
1521
|
+
var flagEntries = (sequence, message) => sequence.flatMap((entry) => {
|
|
1522
|
+
const msg = message(entry);
|
|
1523
|
+
return msg ? [{ message: msg, target: entry }] : [];
|
|
1524
|
+
});
|
|
1525
|
+
var noPositiveTabIndex = (sequence) => flagEntries(
|
|
1526
|
+
sequence,
|
|
1527
|
+
(entry) => entry.tabIndex > 0 ? `Element has tabindex="${entry.tabIndex}". Positive tabindex overrides the natural DOM order and is fragile; use 0 or restructure the DOM.` : null
|
|
1528
|
+
);
|
|
1529
|
+
var visualOrderMismatch = (sequence) => {
|
|
1530
|
+
const floats = sequence.map((entry) => floatingAncestor(entry.element));
|
|
1531
|
+
const scrollers = sequence.map((entry) => scrollAncestor(entry.element));
|
|
1532
|
+
const out = [];
|
|
1533
|
+
for (let idx = 1; idx < sequence.length; idx++) {
|
|
1534
|
+
const prev = sequence[idx - 1];
|
|
1535
|
+
const cur = sequence[idx];
|
|
1536
|
+
if (floats[idx - 1] !== floats[idx] || scrollers[idx - 1] !== scrollers[idx]) {
|
|
1537
|
+
continue;
|
|
1538
|
+
}
|
|
1539
|
+
const prevX = prev.rect.left + prev.rect.width / 2;
|
|
1540
|
+
const curX = cur.rect.left + cur.rect.width / 2;
|
|
1541
|
+
const nextColumn = cur.rect.left >= prev.rect.right;
|
|
1542
|
+
const earlierRow = cur.rect.bottom <= prev.rect.top + ROW_TOLERANCE_PX;
|
|
1543
|
+
const sameRow = !earlierRow && cur.rect.top < prev.rect.bottom - ROW_TOLERANCE_PX;
|
|
1544
|
+
const backwardHop = !nextColumn && (earlierRow || sameRow && curX < prevX - 1);
|
|
1545
|
+
if (!backwardHop) {
|
|
1546
|
+
continue;
|
|
1547
|
+
}
|
|
1548
|
+
out.push({
|
|
1549
|
+
message: `"${cur.selector}" comes after "${prev.selector}" in the tab order, but sits visually before it (reading order is top\u2192bottom, left\u2192right). Tab makes a backward hop here.`,
|
|
1550
|
+
target: cur
|
|
1551
|
+
});
|
|
1552
|
+
}
|
|
1553
|
+
return out;
|
|
1554
|
+
};
|
|
1555
|
+
var missingAccessibleName = (sequence) => flagEntries(sequence, (entry) => {
|
|
1556
|
+
if (!isInteractive(entry.element)) {
|
|
1557
|
+
return null;
|
|
1558
|
+
}
|
|
1559
|
+
if (hasExplicitName(entry.element)) {
|
|
1560
|
+
return null;
|
|
1561
|
+
}
|
|
1562
|
+
if (computeAccessibleName(entry.element).trim() !== "") {
|
|
1563
|
+
return null;
|
|
1564
|
+
}
|
|
1565
|
+
return `Focusable element "${entry.selector}" has no accessible name (no text, aria-label, aria-labelledby, associated label, alt, or title).`;
|
|
1566
|
+
});
|
|
1567
|
+
var ariaHiddenFocusable = (sequence) => flagEntries(
|
|
1568
|
+
sequence,
|
|
1569
|
+
(entry) => inAriaHidden(entry.element) ? `"${entry.selector}" is tabbable but inside aria-hidden="true", so a screen-reader user lands on a control the SR won't announce. Add tabindex="-1"/inert, or remove aria-hidden.` : null
|
|
1570
|
+
);
|
|
1571
|
+
var hiddenWhileFocusable = (sequence, { container }) => {
|
|
1572
|
+
const revealOnFocus = focusRevealSelectors(container.ownerDocument);
|
|
1573
|
+
return flagEntries(sequence, (entry) => {
|
|
1574
|
+
const reason = hiddenReason(entry.element, entry.rect, revealOnFocus);
|
|
1575
|
+
return reason ? `"${entry.selector}" is tabbable but ${reason}. Hide it from the tab order too (display:none, the hidden attribute, or tabindex="-1").` : null;
|
|
1576
|
+
});
|
|
1577
|
+
};
|
|
1578
|
+
var clickableNotFocusable = (_sequence, { container, inSequence }) => {
|
|
1579
|
+
const wrapsFocusable = /* @__PURE__ */ new Set();
|
|
1580
|
+
for (const stop of inSequence) {
|
|
1581
|
+
for (let node = stop; node; node = node.parentElement) {
|
|
1582
|
+
if (wrapsFocusable.has(node)) {
|
|
1583
|
+
break;
|
|
1584
|
+
}
|
|
1585
|
+
wrapsFocusable.add(node);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
const out = [];
|
|
1589
|
+
for (const element of container.querySelectorAll("*")) {
|
|
1590
|
+
if (inSequence.has(element)) {
|
|
1591
|
+
continue;
|
|
1592
|
+
}
|
|
1593
|
+
if (wrapsFocusable.has(element)) {
|
|
1594
|
+
continue;
|
|
1595
|
+
}
|
|
1596
|
+
if (!looksClickable(element)) {
|
|
1597
|
+
continue;
|
|
1598
|
+
}
|
|
1599
|
+
if (isFocusManaged(element)) {
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
if (isInert2(element)) {
|
|
1603
|
+
continue;
|
|
1604
|
+
}
|
|
1605
|
+
const rect = element.getBoundingClientRect();
|
|
1606
|
+
if (rect.width < 1 || rect.height < 1) {
|
|
1607
|
+
continue;
|
|
1608
|
+
}
|
|
1609
|
+
const selector = selectorFor(element);
|
|
1610
|
+
out.push({
|
|
1611
|
+
message: `"${selector}" looks interactive (role or onclick) but is not in the tab order, so keyboard users can't reach it. Use a <button>/<a>, or add tabindex="0" plus Enter/Space handlers.`,
|
|
1612
|
+
target: element
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
return out;
|
|
1616
|
+
};
|
|
1617
|
+
var compositeRovingTabindex = (sequence) => {
|
|
1618
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1619
|
+
for (const entry of sequence) {
|
|
1620
|
+
const container = compositeAncestor(entry.element);
|
|
1621
|
+
if (!container) {
|
|
1622
|
+
continue;
|
|
1623
|
+
}
|
|
1624
|
+
const list = groups.get(container) ?? [];
|
|
1625
|
+
list.push(entry);
|
|
1626
|
+
groups.set(container, list);
|
|
1627
|
+
}
|
|
1628
|
+
const out = [];
|
|
1629
|
+
for (const [container, members] of groups) {
|
|
1630
|
+
if (members.length < 2) {
|
|
1631
|
+
continue;
|
|
1632
|
+
}
|
|
1633
|
+
const role = container.getAttribute("role");
|
|
1634
|
+
for (const member of members) {
|
|
1635
|
+
out.push({
|
|
1636
|
+
message: `${members.length} items inside role="${role}" are separate tab stops. A ${role} should expose one tab stop and move between items with the arrow keys (roving tabindex).`,
|
|
1637
|
+
target: member
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
return out;
|
|
1642
|
+
};
|
|
1643
|
+
var focusEscapesModal = (sequence, { container }) => {
|
|
1644
|
+
const modal = openModal(container);
|
|
1645
|
+
if (!modal) {
|
|
1646
|
+
return [];
|
|
1647
|
+
}
|
|
1648
|
+
const leaked = sequence.filter(
|
|
1649
|
+
(entry) => !modal.contains(entry.element) && !isInert2(entry.element)
|
|
1650
|
+
);
|
|
1651
|
+
if (leaked.length === 0) {
|
|
1652
|
+
return [];
|
|
1653
|
+
}
|
|
1654
|
+
const first = leaked[0];
|
|
1655
|
+
const subject = leaked.length === 1 ? `"${first.selector}" outside it is still tabbable` : `${leaked.length} controls outside it are still tabbable (e.g. "${first.selector}")`;
|
|
1656
|
+
return [
|
|
1657
|
+
{
|
|
1658
|
+
message: `A modal dialog is open, but ${subject}, so focus can leak behind the dialog. Mark background content inert (or aria-hidden + remove it from the tab order).`,
|
|
1659
|
+
target: first,
|
|
1660
|
+
// One finding, anchored on the first leaked control, but every other leaked
|
|
1661
|
+
// control shares the root cause (background not inert) and is ringed too.
|
|
1662
|
+
relatedElements: leaked.slice(1).map((entry) => entry.element)
|
|
1663
|
+
}
|
|
1664
|
+
];
|
|
1665
|
+
};
|
|
1666
|
+
var tabindexOnNoninteractive = (sequence) => flagEntries(sequence, (entry) => {
|
|
1667
|
+
if (entry.tabIndex !== 0) {
|
|
1668
|
+
return null;
|
|
1669
|
+
}
|
|
1670
|
+
if (entry.element.getAttribute("tabindex") === null) {
|
|
1671
|
+
return null;
|
|
1672
|
+
}
|
|
1673
|
+
const element = entry.element;
|
|
1674
|
+
if (isNativelyFocusable(element)) {
|
|
1675
|
+
return null;
|
|
1676
|
+
}
|
|
1677
|
+
if (isInteractive(element)) {
|
|
1678
|
+
return null;
|
|
1679
|
+
}
|
|
1680
|
+
if (element.isContentEditable) {
|
|
1681
|
+
return null;
|
|
1682
|
+
}
|
|
1683
|
+
const role = element.getAttribute("role");
|
|
1684
|
+
if (role && role !== "presentation" && role !== "none") {
|
|
1685
|
+
return null;
|
|
1686
|
+
}
|
|
1687
|
+
if (isScrollContainer(element)) {
|
|
1688
|
+
return null;
|
|
1689
|
+
}
|
|
1690
|
+
return `"${entry.selector}" has tabindex="0" but is non-interactive (no role, not a control). If it's decorative, remove the tabindex, since it adds a dead stop to the tab order; if it's meant to be a control, give it a real role (or use a <button>).`;
|
|
1691
|
+
});
|
|
1692
|
+
var preferNativeElement = (sequence) => flagEntries(sequence, (entry) => {
|
|
1693
|
+
const native = nativeReplacement(entry.element);
|
|
1694
|
+
if (!native) {
|
|
1695
|
+
return null;
|
|
1696
|
+
}
|
|
1697
|
+
const tag = entry.element.tagName.toLowerCase();
|
|
1698
|
+
const role = entry.element.getAttribute("role");
|
|
1699
|
+
return `"${entry.selector}" is a <${tag}> with role="${role}". Prefer a native ${native}: focus, keyboard activation (Enter/Space), and screen-reader semantics come for free, instead of being reimplemented with ARIA + JS.`;
|
|
1700
|
+
});
|
|
1701
|
+
var duplicateAutofocus = (_sequence, { container }) => {
|
|
1702
|
+
const focusableAutofocus = Array.from(container.querySelectorAll("[autofocus]")).filter(
|
|
1703
|
+
(element) => isFocusable(element, { getShadowRoot: true })
|
|
1704
|
+
);
|
|
1705
|
+
if (focusableAutofocus.length < 2) {
|
|
1706
|
+
return [];
|
|
1707
|
+
}
|
|
1708
|
+
return focusableAutofocus.slice(1).map((element) => {
|
|
1709
|
+
const selector = selectorFor(element);
|
|
1710
|
+
return {
|
|
1711
|
+
message: `"${selector}" also has autofocus, but a page can autofocus only one element; the first focusable one in document order wins, so this one is silently ignored. Remove the extra autofocus.`,
|
|
1712
|
+
target: element
|
|
1713
|
+
};
|
|
1714
|
+
});
|
|
1715
|
+
};
|
|
1716
|
+
var autofocusNotFocusable = (_sequence, { container }) => {
|
|
1717
|
+
const out = [];
|
|
1718
|
+
for (const element of container.querySelectorAll("[autofocus]")) {
|
|
1719
|
+
if (isFocusable(element, { getShadowRoot: true })) {
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1722
|
+
const selector = selectorFor(element);
|
|
1723
|
+
out.push({
|
|
1724
|
+
message: `"${selector}" has autofocus but isn't focusable (no tabindex, not a form control), so it's ignored on load. Remove the autofocus, or make the element focusable (e.g. tabindex="-1").`,
|
|
1725
|
+
target: element
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
return out;
|
|
1729
|
+
};
|
|
1730
|
+
var nestedInteractive = (sequence, { container, inSequence }) => {
|
|
1731
|
+
const stop = container.parentElement;
|
|
1732
|
+
const out = [];
|
|
1733
|
+
for (const entry of sequence) {
|
|
1734
|
+
for (let node = entry.element.parentElement; node && node !== stop; node = node.parentElement) {
|
|
1735
|
+
if (!inSequence.has(node) && !isInteractive(node)) {
|
|
1736
|
+
continue;
|
|
1737
|
+
}
|
|
1738
|
+
out.push({
|
|
1739
|
+
message: `"${entry.selector}" is focusable but nested inside another focusable element ("${selectorFor(node)}"). Nesting interactive controls stacks two tab stops in one place and can hide the inner control's role/name from screen readers; don't put a focusable element inside another.`,
|
|
1740
|
+
target: entry
|
|
1741
|
+
});
|
|
1742
|
+
break;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
return out;
|
|
1746
|
+
};
|
|
1747
|
+
var redundantTabindex = (sequence) => flagEntries(sequence, (entry) => {
|
|
1748
|
+
if (entry.tabIndex !== 0) {
|
|
1749
|
+
return null;
|
|
1750
|
+
}
|
|
1751
|
+
if (entry.element.getAttribute("tabindex") === null) {
|
|
1752
|
+
return null;
|
|
1753
|
+
}
|
|
1754
|
+
if (!isNativelyFocusable(entry.element)) {
|
|
1755
|
+
return null;
|
|
1756
|
+
}
|
|
1757
|
+
return `"${entry.selector}" is already focusable, so its tabindex="0" is redundant. Remove the attribute; the element stays in the tab order on its own.`;
|
|
1758
|
+
});
|
|
1759
|
+
var ALL_RULES = {
|
|
1760
|
+
"no-positive-tabindex": {
|
|
1761
|
+
docs: "https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html",
|
|
1762
|
+
defaultSeverity: "error",
|
|
1763
|
+
run: noPositiveTabIndex
|
|
1764
|
+
},
|
|
1765
|
+
"visual-order-mismatch": {
|
|
1766
|
+
docs: "https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html",
|
|
1767
|
+
defaultSeverity: "warning",
|
|
1768
|
+
run: visualOrderMismatch
|
|
1769
|
+
},
|
|
1770
|
+
"missing-accessible-name": {
|
|
1771
|
+
docs: "https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",
|
|
1772
|
+
defaultSeverity: "error",
|
|
1773
|
+
run: missingAccessibleName
|
|
1774
|
+
},
|
|
1775
|
+
"aria-hidden-focusable": {
|
|
1776
|
+
docs: "https://www.w3.org/TR/wai-aria-1.2/#aria-hidden",
|
|
1777
|
+
defaultSeverity: "error",
|
|
1778
|
+
run: ariaHiddenFocusable
|
|
1779
|
+
},
|
|
1780
|
+
"hidden-while-focusable": {
|
|
1781
|
+
docs: "https://www.w3.org/WAI/WCAG22/Understanding/focus-visible.html",
|
|
1782
|
+
defaultSeverity: "error",
|
|
1783
|
+
run: hiddenWhileFocusable
|
|
1784
|
+
},
|
|
1785
|
+
"clickable-not-focusable": {
|
|
1786
|
+
docs: "https://www.w3.org/WAI/WCAG22/Understanding/keyboard.html",
|
|
1787
|
+
defaultSeverity: "error",
|
|
1788
|
+
run: clickableNotFocusable
|
|
1789
|
+
},
|
|
1790
|
+
"composite-roving-tabindex": {
|
|
1791
|
+
docs: "https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/",
|
|
1792
|
+
defaultSeverity: "warning",
|
|
1793
|
+
run: compositeRovingTabindex
|
|
1794
|
+
},
|
|
1795
|
+
"focus-escapes-modal": {
|
|
1796
|
+
docs: "https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/",
|
|
1797
|
+
defaultSeverity: "error",
|
|
1798
|
+
run: focusEscapesModal
|
|
1799
|
+
},
|
|
1800
|
+
"tabindex-on-noninteractive": {
|
|
1801
|
+
docs: "https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/",
|
|
1802
|
+
defaultSeverity: "error",
|
|
1803
|
+
run: tabindexOnNoninteractive
|
|
1804
|
+
},
|
|
1805
|
+
"prefer-native-element": {
|
|
1806
|
+
docs: "https://www.w3.org/TR/using-aria/#firstrule",
|
|
1807
|
+
defaultSeverity: "warning",
|
|
1808
|
+
run: preferNativeElement
|
|
1809
|
+
},
|
|
1810
|
+
"duplicate-autofocus": {
|
|
1811
|
+
docs: "https://html.spec.whatwg.org/multipage/interaction.html#the-autofocus-attribute",
|
|
1812
|
+
defaultSeverity: "warning",
|
|
1813
|
+
run: duplicateAutofocus
|
|
1814
|
+
},
|
|
1815
|
+
"autofocus-not-focusable": {
|
|
1816
|
+
docs: "https://html.spec.whatwg.org/multipage/interaction.html#the-autofocus-attribute",
|
|
1817
|
+
defaultSeverity: "warning",
|
|
1818
|
+
run: autofocusNotFocusable
|
|
1819
|
+
},
|
|
1820
|
+
"nested-interactive": {
|
|
1821
|
+
docs: "https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html",
|
|
1822
|
+
defaultSeverity: "error",
|
|
1823
|
+
run: nestedInteractive
|
|
1824
|
+
},
|
|
1825
|
+
"redundant-tabindex": {
|
|
1826
|
+
docs: "https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex",
|
|
1827
|
+
defaultSeverity: "warning",
|
|
1828
|
+
run: redundantTabindex
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
1831
|
+
var DEFAULT_SEVERITY = Object.fromEntries(
|
|
1832
|
+
Object.entries(ALL_RULES).map(([id, rule]) => [id, rule.defaultSeverity])
|
|
1833
|
+
);
|
|
1834
|
+
|
|
1835
|
+
// ../core/src/audit.ts
|
|
1836
|
+
function resolveRule(options, rule) {
|
|
1837
|
+
const setting = options.rules?.[rule.id];
|
|
1838
|
+
if (setting === void 0) {
|
|
1839
|
+
return { enabled: true, severity: rule.defaultSeverity };
|
|
1840
|
+
}
|
|
1841
|
+
if (setting === "off") {
|
|
1842
|
+
return { enabled: false, severity: rule.defaultSeverity };
|
|
1843
|
+
}
|
|
1844
|
+
return { enabled: true, severity: setting };
|
|
1845
|
+
}
|
|
1846
|
+
function toIssue(finding, rule, severity) {
|
|
1847
|
+
return {
|
|
1848
|
+
rule: rule.id,
|
|
1849
|
+
severity,
|
|
1850
|
+
message: finding.message,
|
|
1851
|
+
docs: rule.docs,
|
|
1852
|
+
relatedElements: finding.relatedElements
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
function locate(finding) {
|
|
1856
|
+
const { target } = finding;
|
|
1857
|
+
return "orderIndex" in target ? {
|
|
1858
|
+
element: target.element,
|
|
1859
|
+
selector: target.selector,
|
|
1860
|
+
orderIndex: target.orderIndex
|
|
1861
|
+
} : { element: target, selector: selectorFor(target) };
|
|
1862
|
+
}
|
|
1863
|
+
function audit(root = document, options = {}, customRules = []) {
|
|
1864
|
+
const container = root.nodeType === 9 ? root.documentElement : root;
|
|
1865
|
+
if (!container) {
|
|
1866
|
+
return finalize({ valid: true, sequence: [], violations: [] }, options.format);
|
|
1867
|
+
}
|
|
1868
|
+
const elements = tabbable(container, {
|
|
1869
|
+
getShadowRoot: true
|
|
1870
|
+
});
|
|
1871
|
+
const sequence = elements.map((element, orderIndex) => ({
|
|
1872
|
+
element,
|
|
1873
|
+
orderIndex,
|
|
1874
|
+
selector: selectorFor(element),
|
|
1875
|
+
tabIndex: getTabIndex(element),
|
|
1876
|
+
rect: element.getBoundingClientRect()
|
|
1877
|
+
}));
|
|
1878
|
+
const ctx = {
|
|
1879
|
+
container,
|
|
1880
|
+
inSequence: new Set(sequence.map((entry) => entry.element))
|
|
1881
|
+
};
|
|
1882
|
+
const builtins = Object.entries(ALL_RULES).map(([id, def]) => ({
|
|
1883
|
+
id,
|
|
1884
|
+
...def
|
|
1885
|
+
}));
|
|
1886
|
+
const byElement2 = /* @__PURE__ */ new Map();
|
|
1887
|
+
for (const rule of [...builtins, ...customRules]) {
|
|
1888
|
+
const { enabled, severity } = resolveRule(options, rule);
|
|
1889
|
+
if (!enabled) {
|
|
1890
|
+
continue;
|
|
1891
|
+
}
|
|
1892
|
+
for (const finding of rule.run(sequence, ctx)) {
|
|
1893
|
+
const { element, selector, orderIndex } = locate(finding);
|
|
1894
|
+
let violation = byElement2.get(element);
|
|
1895
|
+
if (!violation) {
|
|
1896
|
+
violation = { element, selector, orderIndex, issues: [] };
|
|
1897
|
+
byElement2.set(element, violation);
|
|
1898
|
+
}
|
|
1899
|
+
const issue = toIssue(finding, rule, severity);
|
|
1900
|
+
if (isRuleIgnored(element, rule.id)) {
|
|
1901
|
+
issue.ignored = true;
|
|
1902
|
+
}
|
|
1903
|
+
violation.issues.push(issue);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
const violations = [...byElement2.values()].sort(
|
|
1907
|
+
(a, b) => (a.orderIndex ?? Infinity) - (b.orderIndex ?? Infinity)
|
|
1908
|
+
);
|
|
1909
|
+
for (const violation of violations) {
|
|
1910
|
+
violation.issues.sort(
|
|
1911
|
+
(a, b) => a.severity === b.severity ? 0 : a.severity === "error" ? -1 : 1
|
|
1912
|
+
);
|
|
1913
|
+
}
|
|
1914
|
+
const hasErrors = violations.some(
|
|
1915
|
+
(violation) => violation.issues.some((issue) => issue.severity === "error" && !issue.ignored)
|
|
1916
|
+
);
|
|
1917
|
+
return finalize({ valid: !hasErrors, sequence, violations }, options.format);
|
|
1918
|
+
}
|
|
1919
|
+
function finalize(result, format) {
|
|
1920
|
+
if (!format) {
|
|
1921
|
+
return result;
|
|
1922
|
+
}
|
|
1923
|
+
return { ...result, violations: reshape(result.violations, format) };
|
|
1924
|
+
}
|
|
1925
|
+
function reshape(violations, format) {
|
|
1926
|
+
switch (format) {
|
|
1927
|
+
case "text":
|
|
1928
|
+
return renderText(violations);
|
|
1929
|
+
case "by-element":
|
|
1930
|
+
return byElement(violations);
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
var related = (issue) => issue.relatedElements?.map(selectorFor);
|
|
1934
|
+
function byElement(violations) {
|
|
1935
|
+
return violations.map((violation) => ({
|
|
1936
|
+
selector: violation.selector,
|
|
1937
|
+
orderIndex: violation.orderIndex,
|
|
1938
|
+
issueCount: violation.issues.length,
|
|
1939
|
+
issues: violation.issues.map((issue) => ({
|
|
1940
|
+
rule: issue.rule,
|
|
1941
|
+
severity: issue.severity,
|
|
1942
|
+
message: issue.message,
|
|
1943
|
+
docs: issue.docs,
|
|
1944
|
+
related: related(issue),
|
|
1945
|
+
ignored: issue.ignored
|
|
1946
|
+
}))
|
|
1947
|
+
}));
|
|
1948
|
+
}
|
|
1949
|
+
function renderText(violations) {
|
|
1950
|
+
if (!violations.length) {
|
|
1951
|
+
return "No tab-order issues.";
|
|
1952
|
+
}
|
|
1953
|
+
return violations.map((violation) => {
|
|
1954
|
+
const pos = violation.orderIndex !== void 0 ? `#${violation.orderIndex + 1} ` : "";
|
|
1955
|
+
const issues = violation.issues.map((issue) => {
|
|
1956
|
+
const rel = related(issue);
|
|
1957
|
+
return ` - ${issue.severity.toUpperCase()} [${issue.rule}] ${issue.message}` + (rel?.length ? ` (related: ${rel.join(", ")})` : "") + (issue.ignored ? " (ignored via data-ooo-ignore)" : "");
|
|
1958
|
+
}).join("\n");
|
|
1959
|
+
return `${pos}${violation.selector}
|
|
1960
|
+
${issues}`;
|
|
1961
|
+
}).join("\n\n");
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
// ../trace/src/styles.ts
|
|
1965
|
+
var RING_CSS = `
|
|
1966
|
+
.ooo-ring { outline: 1px dashed rgba(47, 106, 71, 0.5); outline-offset: 2px; }
|
|
1967
|
+
.ooo-ring--warn { outline: 1.5px dashed rgba(154, 125, 26, 0.8); outline-offset: 2px; }
|
|
1968
|
+
.ooo-ring--bad { outline: 1.5px solid rgba(160, 31, 23, 0.74); outline-offset: 2px; }
|
|
1969
|
+
.ooo-ring:focus-visible, .ooo-ring--warn:focus-visible, .ooo-ring--bad:focus-visible { outline: revert; }
|
|
1970
|
+
`;
|
|
1971
|
+
var OVERLAY_CSS = `
|
|
1972
|
+
/* The overlay's own palette + mono voice, defined once on the root layer so every
|
|
1973
|
+
badge/panel/tooltip inside inherits it. Kept local (not the demo's vars) because
|
|
1974
|
+
the overlay mounts on arbitrary pages whose :root it can't reach. */
|
|
1975
|
+
.ooo-layer { position: fixed; inset: 0; pointer-events: none; z-index: 2147483646;
|
|
1976
|
+
--ooo-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1977
|
+
--ooo-ink: #18191c; --ooo-ink-2: #34352d; --ooo-muted: #74756d; --ooo-muted-2: #8a8b80;
|
|
1978
|
+
--ooo-line: #dcd9cd; --ooo-line-2: #c6c2b4; --ooo-surface: #fffefb; --ooo-btn: #ecebe4;
|
|
1979
|
+
--ooo-accent: #1f4e79; --ooo-ok: #2f6a47; --ooo-ok-strong: #1f7a44;
|
|
1980
|
+
--ooo-warn: #9a7d1a; --ooo-bad: #a01f17; }
|
|
1981
|
+
.ooo-svg { position: absolute; top: 0; left: 0; overflow: visible; }
|
|
1982
|
+
.ooo-seg { fill: none; stroke: var(--ooo-ok); stroke-width: 2; opacity: 0.92;
|
|
1983
|
+
stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 7 6; }
|
|
1984
|
+
.ooo-seg--back { stroke: var(--ooo-bad); stroke-width: 2.25; }
|
|
1985
|
+
.ooo-hit { fill: none; stroke: transparent; stroke-width: 18; pointer-events: none; cursor: help; }
|
|
1986
|
+
/* Only backward (red) hops are hoverable; forward (green) hops stay click-through. */
|
|
1987
|
+
.ooo-hit--back { pointer-events: stroke; }
|
|
1988
|
+
${RING_CSS}
|
|
1989
|
+
/* Badges read hollow by default (white fill, coloured outline and number), then
|
|
1990
|
+
fill in solid while their element is keyboard-focused, so the badge doubles as
|
|
1991
|
+
a "you are here" cursor that moves as you Tab. */
|
|
1992
|
+
/* Every badge is hoverable (auto) so its tooltip opens on hover (the layer itself
|
|
1993
|
+
is pointer-events:none). The badge is a small disc at the element's centre, so
|
|
1994
|
+
it only intercepts clicks dead-centre. */
|
|
1995
|
+
.ooo-badge { pointer-events: auto; cursor: help; color: var(--ooo-ok); }
|
|
1996
|
+
/* "Peek": turn the overlay click-through (badges + backward hit-lines stop
|
|
1997
|
+
intercepting) and fade the drawing back, so the page beneath stays usable. Scoped
|
|
1998
|
+
to .ooo-svg so the control panel, which reports the state, stays legible. */
|
|
1999
|
+
.ooo-layer[data-ooo-peek="on"] .ooo-svg { opacity: 0.25; }
|
|
2000
|
+
.ooo-layer[data-ooo-peek="on"] .ooo-badge,
|
|
2001
|
+
.ooo-layer[data-ooo-peek="on"] .ooo-hit--back { pointer-events: none; }
|
|
2002
|
+
|
|
2003
|
+
/* "Overlay" off hides the drawing + hit-lines but leaves the panel (the way back)
|
|
2004
|
+
in place; the renderer hides the on-page rings separately. */
|
|
2005
|
+
.ooo-layer.ooo-hidden .ooo-svg { display: none; }
|
|
2006
|
+
|
|
2007
|
+
/* Control panel: a fixed-width card pinned to a corner, styled to match the demo
|
|
2008
|
+
chrome. It holds two identical on/off switches (overlay + click-through). Palette
|
|
2009
|
+
inlined: the overlay mounts on arbitrary pages and can't reach the demo's vars. */
|
|
2010
|
+
.ooo-panel, .ooo-panel * { box-sizing: border-box; }
|
|
2011
|
+
.ooo-panel { position: fixed; left: 12px; bottom: 12px; z-index: 1;
|
|
2012
|
+
width: 188px; pointer-events: auto; margin: 0; padding: 10px;
|
|
2013
|
+
display: flex; flex-direction: column; gap: 9px;
|
|
2014
|
+
background: #f5f4ef; border: 1px solid var(--ooo-line-2); border-radius: 3px;
|
|
2015
|
+
box-shadow: 0 10px 30px -12px rgba(24, 25, 28, 0.32), 0 1px 2px rgba(24, 25, 28, 0.08);
|
|
2016
|
+
font: 500 12px/1 var(--ooo-mono); }
|
|
2017
|
+
/* Title doubles as the collapse toggle: full-width button, +/- sign on the right
|
|
2018
|
+
tells which way it folds. Reset the UA button look back to the card's type. */
|
|
2019
|
+
.ooo-panel-title { display: flex; align-items: center; width: 100%; margin: 0;
|
|
2020
|
+
padding: 0 0 9px; border: 0; border-bottom: 1px solid var(--ooo-line); background: none;
|
|
2021
|
+
font: 600 13px/1 inherit; color: var(--ooo-ink); letter-spacing: 0.01em;
|
|
2022
|
+
text-align: left; cursor: pointer; }
|
|
2023
|
+
.ooo-panel-title::after { content: "\\2212"; margin-left: auto; padding-left: 12px;
|
|
2024
|
+
color: var(--ooo-muted); font-size: 14px; }
|
|
2025
|
+
.ooo-panel-title:hover { color: #000; }
|
|
2026
|
+
.ooo-panel-title:hover::after { color: var(--ooo-ink); }
|
|
2027
|
+
.ooo-panel-body { display: flex; flex-direction: column; gap: 9px; }
|
|
2028
|
+
/* Collapsed: fold the body away, leave the bare title bar (no divider, +/- flips). */
|
|
2029
|
+
.ooo-panel[data-open="0"] .ooo-panel-title { padding-bottom: 0; border-bottom: 0; }
|
|
2030
|
+
.ooo-panel[data-open="0"] .ooo-panel-title::after { content: "+"; }
|
|
2031
|
+
.ooo-panel[data-open="0"] .ooo-panel-body { display: none; }
|
|
2032
|
+
.ooo-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
|
2033
|
+
.ooo-row-label { color: var(--ooo-ink-2); }
|
|
2034
|
+
/* Toggle switch: a pill track with a sliding knob; accent track + knob right when on. */
|
|
2035
|
+
.ooo-switch { flex: none; position: relative; width: 32px; height: 18px; margin: 0; padding: 0;
|
|
2036
|
+
border: 1px solid var(--ooo-line-2); border-radius: 999px; background: #e7e4da; cursor: pointer;
|
|
2037
|
+
transition: background 0.14s ease, border-color 0.14s ease; }
|
|
2038
|
+
.ooo-switch-knob { position: absolute; top: 1px; left: 1px; width: 14px; height: 14px;
|
|
2039
|
+
border-radius: 50%; background: var(--ooo-surface); box-shadow: 0 1px 2px rgba(24, 25, 28, 0.3);
|
|
2040
|
+
transition: transform 0.14s ease; }
|
|
2041
|
+
.ooo-switch--on { background: var(--ooo-accent); border-color: var(--ooo-accent); }
|
|
2042
|
+
.ooo-switch--on .ooo-switch-knob { transform: translateX(14px); }
|
|
2043
|
+
.ooo-switch:disabled { cursor: default; opacity: 0.4; }
|
|
2044
|
+
.ooo-panel-hint { margin: 1px 0 0; color: var(--ooo-muted); font-size: 10.5px; letter-spacing: 0.01em; }
|
|
2045
|
+
/* Copy findings: a split button. The main face copies in the current format; the
|
|
2046
|
+
caret to its right opens a menu to switch it, GitHub-merge-button style. */
|
|
2047
|
+
.ooo-copy-split { position: relative; display: flex; }
|
|
2048
|
+
.ooo-copy { flex: 1 1 auto; min-width: 0; margin: 0; padding: 6px 8px;
|
|
2049
|
+
border: 1px solid var(--ooo-line-2); border-radius: 3px 0 0 3px; background: var(--ooo-btn); color: var(--ooo-ink-2);
|
|
2050
|
+
font: 600 11px/1 var(--ooo-mono);
|
|
2051
|
+
letter-spacing: 0.02em; cursor: pointer;
|
|
2052
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
2053
|
+
transition: background 0.14s ease, border-color 0.14s ease, color 0.14s ease; }
|
|
2054
|
+
.ooo-copy-caret { flex: none; width: 26px; margin-left: -1px; padding: 0;
|
|
2055
|
+
border: 1px solid var(--ooo-line-2); border-radius: 0 3px 3px 0; background: var(--ooo-btn); color: var(--ooo-ink-2);
|
|
2056
|
+
font: 600 11px/1 var(--ooo-mono); cursor: pointer;
|
|
2057
|
+
transition: background 0.14s ease, border-color 0.14s ease, color 0.14s ease; }
|
|
2058
|
+
/* Raise whichever half is hovered so its border draws over the shared seam. */
|
|
2059
|
+
.ooo-copy:hover, .ooo-copy-caret:hover { position: relative; z-index: 1;
|
|
2060
|
+
background: #e2e0d6; border-color: #b7b3a4; color: var(--ooo-ink); }
|
|
2061
|
+
.ooo-copy-menu { position: absolute; left: 0; right: 0; bottom: 100%; margin-bottom: 5px;
|
|
2062
|
+
display: flex; flex-direction: column; padding: 3px; z-index: 2;
|
|
2063
|
+
background: var(--ooo-surface); border: 1px solid var(--ooo-line-2); border-radius: 3px;
|
|
2064
|
+
box-shadow: 0 10px 30px -12px rgba(24, 25, 28, 0.4), 0 1px 2px rgba(24, 25, 28, 0.08); }
|
|
2065
|
+
.ooo-copy-menu[hidden] { display: none; }
|
|
2066
|
+
.ooo-copy-item { display: flex; align-items: center; width: 100%; margin: 0;
|
|
2067
|
+
padding: 5px 7px; border: 0; border-radius: 2px; background: none; color: var(--ooo-ink-2);
|
|
2068
|
+
letter-spacing: 0.02em; text-align: left; cursor: pointer;
|
|
2069
|
+
font: 500 11px/1 var(--ooo-mono); }
|
|
2070
|
+
.ooo-copy-item:hover { background: var(--ooo-btn); color: var(--ooo-ink); }
|
|
2071
|
+
.ooo-copy-item::before { content: "\\2713"; width: 13px; color: var(--ooo-accent); visibility: hidden; }
|
|
2072
|
+
.ooo-copy-item--on { color: var(--ooo-ink); }
|
|
2073
|
+
.ooo-copy-item--on::before { visibility: visible; }
|
|
2074
|
+
.ooo-badge circle { fill: var(--ooo-surface); stroke: currentColor; stroke-width: 1.5;
|
|
2075
|
+
transform-box: fill-box; transform-origin: center; }
|
|
2076
|
+
.ooo-badge text { fill: currentColor;
|
|
2077
|
+
font: 500 11px var(--ooo-mono); }
|
|
2078
|
+
.ooo-badge--warn { color: var(--ooo-warn); }
|
|
2079
|
+
.ooo-badge--bad { color: var(--ooo-bad); }
|
|
2080
|
+
.ooo-badge--off text { font-size: 12px; }
|
|
2081
|
+
.ooo-badge--on circle { fill: currentColor; }
|
|
2082
|
+
.ooo-badge--on text { fill: var(--ooo-surface); }
|
|
2083
|
+
|
|
2084
|
+
/* Autofocus indicator */
|
|
2085
|
+
.ooo-af circle { fill: var(--ooo-accent); stroke: var(--ooo-surface); stroke-width: 1.5; }
|
|
2086
|
+
.ooo-af path { fill: var(--ooo-surface); }
|
|
2087
|
+
|
|
2088
|
+
.ooo-tip-cursor { position: fixed; top: 0; left: 0; width: 0; height: 0; pointer-events: none; anchor-name: --ooo-tip-anchor; }
|
|
2089
|
+
|
|
2090
|
+
|
|
2091
|
+
.ooo-tip { position: fixed; position-anchor: --ooo-tip-anchor;
|
|
2092
|
+
inset: auto; top: anchor(center); left: anchor(center); margin: 19px 0 0 -11px;
|
|
2093
|
+
position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
|
|
2094
|
+
pointer-events: auto;
|
|
2095
|
+
background-color: var(--ooo-surface);
|
|
2096
|
+
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.03 0'/></filter><rect width='100%25' height='100%25' filter='url(%23g)'/></svg>");
|
|
2097
|
+
color: var(--ooo-ink); border: 1px solid var(--ooo-line-2); border-radius: 2px;
|
|
2098
|
+
font: 13px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
2099
|
+
box-shadow: 0 18px 48px -14px rgba(24,25,28,0.34), 0 2px 5px rgba(24,25,28,0.08); }
|
|
2100
|
+
|
|
2101
|
+
.ooo-tip-head { display: flex; align-items: center; gap: 10px; padding: 9px 12px; }
|
|
2102
|
+
.ooo-tip-idx { flex: none; align-self: stretch; display: flex; align-items: center;
|
|
2103
|
+
padding-right: 10px; border-right: 1px solid var(--ooo-line);
|
|
2104
|
+
font: 500 14px/1 var(--ooo-mono);
|
|
2105
|
+
letter-spacing: -0.01em; color: var(--ooo-accent); }
|
|
2106
|
+
.ooo-tip-idx--off { color: var(--ooo-warn); font-size: 15px; }
|
|
2107
|
+
.ooo-tip-sel { min-width: 0; font: 12px/1.45 var(--ooo-mono);
|
|
2108
|
+
color: var(--ooo-ink-2); word-break: break-word; }
|
|
2109
|
+
|
|
2110
|
+
.ooo-tip-fields { margin: 0; padding: 8px 12px; border-top: 1px solid var(--ooo-line);
|
|
2111
|
+
display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; align-items: baseline; }
|
|
2112
|
+
.ooo-tip-fields dt { margin: 0;
|
|
2113
|
+
font: 600 9.5px/1.5 var(--ooo-mono);
|
|
2114
|
+
letter-spacing: 0.07em; text-transform: uppercase; color: var(--ooo-muted-2); }
|
|
2115
|
+
.ooo-tip-fields dd { margin: 0; min-width: 0; color: var(--ooo-ink-2); font-size: 12.5px; word-break: break-word; }
|
|
2116
|
+
.ooo-tip-mono { font-family: var(--ooo-mono);
|
|
2117
|
+
font-size: 11.5px; color: var(--ooo-ink); }
|
|
2118
|
+
.ooo-tip-dim { color: #b0b1a6; }
|
|
2119
|
+
|
|
2120
|
+
.ooo-tip-body { padding: 9px 12px; border-top: 1px solid var(--ooo-line); }
|
|
2121
|
+
.ooo-tip-list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 9px; }
|
|
2122
|
+
.ooo-tip-item { display: block; }
|
|
2123
|
+
.ooo-tip-rule { display: flex; align-items: center; gap: 5px; width: fit-content; margin-bottom: 3px;
|
|
2124
|
+
font: 600 10px/1.4 var(--ooo-mono);
|
|
2125
|
+
letter-spacing: 0.06em; text-transform: uppercase; color: var(--ooo-bad); text-decoration: none; }
|
|
2126
|
+
.ooo-tip-rule--warn { color: var(--ooo-warn); }
|
|
2127
|
+
a.ooo-tip-rule:hover > span { text-decoration: underline; text-underline-offset: 2px; }
|
|
2128
|
+
.ooo-tip-rule-ic { flex: none; opacity: 0.55; }
|
|
2129
|
+
a.ooo-tip-rule:hover .ooo-tip-rule-ic { opacity: 1; }
|
|
2130
|
+
.ooo-tip-msg { display: block; margin: 0; color: var(--ooo-ink-2); font-size: 12.5px; }
|
|
2131
|
+
.ooo-tip-ok { margin: 0; color: var(--ooo-ok-strong); font-size: 12.5px; }
|
|
2132
|
+
.ooo-tip-item--ignored { opacity: 0.7; }
|
|
2133
|
+
.ooo-tip-item--ignored .ooo-tip-rule { color: var(--ooo-muted-2); }
|
|
2134
|
+
.ooo-tip-ignored { display: block; margin-top: 4px; color: var(--ooo-muted); font-size: 11.5px; }
|
|
2135
|
+
.ooo-tip-ignored code { font-family: var(--ooo-mono);
|
|
2136
|
+
font-size: 11px; }
|
|
2137
|
+
|
|
2138
|
+
/* hop tooltips reuse the frame without the index column */
|
|
2139
|
+
.ooo-tip-flag { flex: none;
|
|
2140
|
+
font: 600 10px/1.4 var(--ooo-mono);
|
|
2141
|
+
letter-spacing: 0.06em; text-transform: uppercase; color: var(--ooo-ok-strong); }
|
|
2142
|
+
.ooo-tip-flag--back { color: var(--ooo-bad); }
|
|
2143
|
+
.ooo-tip-hop { font: 500 13px/1 var(--ooo-mono);
|
|
2144
|
+
color: var(--ooo-ink); }
|
|
2145
|
+
|
|
2146
|
+
|
|
2147
|
+
/* Advance by exactly one dash period (7 + 6 = 13) per cycle so the marching dashes
|
|
2148
|
+
loop seamlessly. Must stay in sync with .ooo-seg's stroke-dasharray. */
|
|
2149
|
+
@keyframes ooo-flow { to { stroke-dashoffset: -13; } }
|
|
2150
|
+
@keyframes ooo-cast {
|
|
2151
|
+
0% { transform: scale(1); }
|
|
2152
|
+
35% { transform: scale(1.18); }
|
|
2153
|
+
100% { transform: scale(1); }
|
|
2154
|
+
}
|
|
2155
|
+
.ooo-layer[data-ooo-motion="play"] .ooo-seg { animation: ooo-flow 1.1s linear infinite; marker-mid: none; }
|
|
2156
|
+
.ooo-layer[data-ooo-motion="play"] .ooo-seg--back { animation-duration: 0.8s; }
|
|
2157
|
+
.ooo-layer[data-ooo-motion="play"] .ooo-badge--on circle { animation: ooo-cast 0.4s ease-out; }
|
|
2158
|
+
`;
|
|
2159
|
+
var overlaySheet = new CSSStyleSheet();
|
|
2160
|
+
overlaySheet.replaceSync(OVERLAY_CSS);
|
|
2161
|
+
var ringSheet = new CSSStyleSheet();
|
|
2162
|
+
ringSheet.replaceSync(RING_CSS);
|
|
2163
|
+
function adopt(root, sheet) {
|
|
2164
|
+
if (root.adoptedStyleSheets.includes(sheet)) {
|
|
2165
|
+
return;
|
|
2166
|
+
}
|
|
2167
|
+
root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];
|
|
2168
|
+
}
|
|
2169
|
+
function ensureStyles() {
|
|
2170
|
+
adopt(document, overlaySheet);
|
|
2171
|
+
}
|
|
2172
|
+
function ensureRingStyles(root) {
|
|
2173
|
+
adopt(root, ringSheet);
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
// ../trace/src/tooltip.ts
|
|
2177
|
+
function resolver(tip) {
|
|
2178
|
+
let html = null;
|
|
2179
|
+
return () => html ?? (html = tip());
|
|
2180
|
+
}
|
|
2181
|
+
var _Tooltip = class _Tooltip {
|
|
2182
|
+
constructor(parent) {
|
|
2183
|
+
this.hideTimer = null;
|
|
2184
|
+
this.cursor = document.createElement("div");
|
|
2185
|
+
this.cursor.className = "ooo-tip-cursor";
|
|
2186
|
+
parent.appendChild(this.cursor);
|
|
2187
|
+
this.element = document.createElement("div");
|
|
2188
|
+
this.element.className = "ooo-tip";
|
|
2189
|
+
this.element.setAttribute("popover", "manual");
|
|
2190
|
+
parent.appendChild(this.element);
|
|
2191
|
+
this.element.addEventListener("mouseenter", () => this.cancelHide());
|
|
2192
|
+
this.element.addEventListener("mouseleave", () => this.scheduleHide());
|
|
2193
|
+
}
|
|
2194
|
+
/** Wire a hover target (badge or hop line) so its tooltip pops where the pointer
|
|
2195
|
+
meets it. Badges sit right under the pointer and a long backward hop's
|
|
2196
|
+
endpoints can sit far from where you hover, so the pointer suits both. */
|
|
2197
|
+
wire(target, tip) {
|
|
2198
|
+
const html = resolver(tip);
|
|
2199
|
+
target.addEventListener("mouseenter", (event) => this.show(html(), event));
|
|
2200
|
+
target.addEventListener("mouseleave", () => this.scheduleHide());
|
|
2201
|
+
}
|
|
2202
|
+
show(html, event) {
|
|
2203
|
+
this.cancelHide();
|
|
2204
|
+
this.cursor.style.left = `${event.clientX}px`;
|
|
2205
|
+
this.cursor.style.top = `${event.clientY}px`;
|
|
2206
|
+
this.element.innerHTML = html;
|
|
2207
|
+
if (!this.element.matches(":popover-open")) {
|
|
2208
|
+
this.element.showPopover();
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
scheduleHide() {
|
|
2212
|
+
this.cancelHide();
|
|
2213
|
+
this.hideTimer = setTimeout(() => this.hide(), _Tooltip.HIDE_DELAY);
|
|
2214
|
+
}
|
|
2215
|
+
cancelHide() {
|
|
2216
|
+
if (this.hideTimer !== null) {
|
|
2217
|
+
clearTimeout(this.hideTimer);
|
|
2218
|
+
this.hideTimer = null;
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
hide() {
|
|
2222
|
+
this.cancelHide();
|
|
2223
|
+
if (this.element.matches(":popover-open")) {
|
|
2224
|
+
this.element.hidePopover();
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
destroy() {
|
|
2228
|
+
this.hide();
|
|
2229
|
+
this.element.remove();
|
|
2230
|
+
this.cursor.remove();
|
|
2231
|
+
}
|
|
2232
|
+
};
|
|
2233
|
+
// Grace period for the cursor to travel from the target into the tooltip.
|
|
2234
|
+
_Tooltip.HIDE_DELAY = 150;
|
|
2235
|
+
var Tooltip = _Tooltip;
|
|
2236
|
+
|
|
2237
|
+
// ../trace/src/render.ts
|
|
2238
|
+
var RING_CLASS = `${OVERLAY_CLASS_PREFIX}ring`;
|
|
2239
|
+
var RING_BAD_CLASS = `${RING_CLASS}--bad`;
|
|
2240
|
+
var RING_WARN_CLASS = `${RING_CLASS}--warn`;
|
|
2241
|
+
var RING_CLASSES = [RING_CLASS, RING_WARN_CLASS, RING_BAD_CLASS];
|
|
2242
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
2243
|
+
var RADIUS = 11;
|
|
2244
|
+
var SEG_PAD = RADIUS + 5;
|
|
2245
|
+
var SMALL_SIZE = 30;
|
|
2246
|
+
function svgEl(tag, attrs = {}, ...kids) {
|
|
2247
|
+
const node = document.createElementNS(SVG_NS, tag);
|
|
2248
|
+
for (const attrName in attrs) {
|
|
2249
|
+
node.setAttribute(attrName, String(attrs[attrName]));
|
|
2250
|
+
}
|
|
2251
|
+
node.append(...kids);
|
|
2252
|
+
return node;
|
|
2253
|
+
}
|
|
2254
|
+
var Renderer = class {
|
|
2255
|
+
constructor(layer, tooltip) {
|
|
2256
|
+
this.layer = layer;
|
|
2257
|
+
this.tooltip = tooltip;
|
|
2258
|
+
this.svg = null;
|
|
2259
|
+
this.markers = [];
|
|
2260
|
+
this.segments = [];
|
|
2261
|
+
this.byEl = /* @__PURE__ */ new Map();
|
|
2262
|
+
// Elements we've ringed, with the class applied, kept so we can untag them on
|
|
2263
|
+
// rebuild/destroy and toggle them when the overlay is hidden.
|
|
2264
|
+
this.ringEls = [];
|
|
2265
|
+
// The rings live on the page, not in the hideable layer, so their visibility is
|
|
2266
|
+
// tracked here and applied by class — otherwise "Hide overlay" leaves them behind.
|
|
2267
|
+
this.ringsVisible = true;
|
|
2268
|
+
// The badge of the currently keyboard-focused element, filled in as a cursor.
|
|
2269
|
+
this.focused = null;
|
|
2270
|
+
// Last SVG size written; reset in clear() since each draw makes a fresh svg.
|
|
2271
|
+
this.sizeW = 0;
|
|
2272
|
+
this.sizeH = 0;
|
|
2273
|
+
}
|
|
2274
|
+
draw(stops, segs, offStops) {
|
|
2275
|
+
this.clear();
|
|
2276
|
+
const svg = svgEl("svg", { class: "ooo-svg" }, buildDefs());
|
|
2277
|
+
const segLayer = svgEl("g");
|
|
2278
|
+
svg.appendChild(segLayer);
|
|
2279
|
+
this.svg = svg;
|
|
2280
|
+
stops.forEach((stop) => this.addMarker(stop));
|
|
2281
|
+
for (let idx = 0; idx < this.markers.length - 1; idx++) {
|
|
2282
|
+
const from = this.markers[idx];
|
|
2283
|
+
const toMarker = this.markers[idx + 1];
|
|
2284
|
+
const { back, tip } = segs[idx];
|
|
2285
|
+
const hit = svgEl("line", {
|
|
2286
|
+
class: back ? "ooo-hit ooo-hit--back" : "ooo-hit"
|
|
2287
|
+
});
|
|
2288
|
+
const line = svgEl("polyline", {
|
|
2289
|
+
class: back ? "ooo-seg ooo-seg--back" : "ooo-seg",
|
|
2290
|
+
"marker-mid": back ? "url(#ooo-arrow-back)" : "url(#ooo-arrow)"
|
|
2291
|
+
});
|
|
2292
|
+
segLayer.append(hit, line);
|
|
2293
|
+
this.segments.push({ from, toMarker, line, hit, back });
|
|
2294
|
+
if (back) {
|
|
2295
|
+
this.tooltip.wire(hit, tip);
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
offStops.forEach((stop) => this.addMarker(stop));
|
|
2299
|
+
this.layer.appendChild(svg);
|
|
2300
|
+
}
|
|
2301
|
+
/** One getBoundingClientRect pass to set geometry (initial + scroll frames).
|
|
2302
|
+
All rect reads run before any SVG writes, so a scroll frame triggers a
|
|
2303
|
+
single layout flush instead of one reflow per marker (read→write→read…). */
|
|
2304
|
+
seed() {
|
|
2305
|
+
const rects = this.markers.map((marker) => marker.element.getBoundingClientRect());
|
|
2306
|
+
this.syncSize();
|
|
2307
|
+
this.markers.forEach((marker, idx) => this.applyRect(marker, rects[idx]));
|
|
2308
|
+
this.updateSegments();
|
|
2309
|
+
}
|
|
2310
|
+
applyMoved(moved) {
|
|
2311
|
+
this.syncSize();
|
|
2312
|
+
for (const { target, rect } of moved) {
|
|
2313
|
+
const marker = this.byEl.get(target);
|
|
2314
|
+
if (!marker) {
|
|
2315
|
+
continue;
|
|
2316
|
+
}
|
|
2317
|
+
this.applyRect(marker, rect);
|
|
2318
|
+
}
|
|
2319
|
+
this.updateSegments();
|
|
2320
|
+
}
|
|
2321
|
+
setFocused(element) {
|
|
2322
|
+
const marker = element ? this.byEl.get(element) ?? null : null;
|
|
2323
|
+
if (this.focused === marker) {
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2326
|
+
this.focused?.group.classList.remove("ooo-badge--on");
|
|
2327
|
+
this.focused = marker;
|
|
2328
|
+
this.focused?.group.classList.add("ooo-badge--on");
|
|
2329
|
+
}
|
|
2330
|
+
elementsToObserve() {
|
|
2331
|
+
return this.markers.map((marker) => marker.element);
|
|
2332
|
+
}
|
|
2333
|
+
/** Toggle the page-element rings with the overlay's visibility. They sit on the
|
|
2334
|
+
page rather than inside the hideable layer, so hiding the layer alone leaves
|
|
2335
|
+
them; this drives them by class and survives a rebuild (see markRing). */
|
|
2336
|
+
setRingsVisible(visible) {
|
|
2337
|
+
if (this.ringsVisible === visible) {
|
|
2338
|
+
return;
|
|
2339
|
+
}
|
|
2340
|
+
this.ringsVisible = visible;
|
|
2341
|
+
for (const { element, cls } of this.ringEls) {
|
|
2342
|
+
element.classList.toggle(cls, visible);
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
clear() {
|
|
2346
|
+
this.svg?.remove();
|
|
2347
|
+
this.svg = null;
|
|
2348
|
+
this.markers = [];
|
|
2349
|
+
this.segments = [];
|
|
2350
|
+
this.byEl.clear();
|
|
2351
|
+
this.focused = null;
|
|
2352
|
+
this.sizeW = 0;
|
|
2353
|
+
this.sizeH = 0;
|
|
2354
|
+
for (const { element } of this.ringEls) {
|
|
2355
|
+
element.classList.remove(...RING_CLASSES);
|
|
2356
|
+
}
|
|
2357
|
+
this.ringEls = [];
|
|
2358
|
+
}
|
|
2359
|
+
addMarker(spec) {
|
|
2360
|
+
this.markRing(spec.element, spec.severity);
|
|
2361
|
+
let cls = "ooo-badge";
|
|
2362
|
+
if (spec.severity) {
|
|
2363
|
+
cls += spec.severity === "error" ? " ooo-badge--bad" : " ooo-badge--warn";
|
|
2364
|
+
}
|
|
2365
|
+
if (!spec.inSeq) {
|
|
2366
|
+
cls += " ooo-badge--off";
|
|
2367
|
+
}
|
|
2368
|
+
const circle = svgEl("circle", { r: RADIUS });
|
|
2369
|
+
const text = svgEl("text", { "text-anchor": "middle", y: 4 });
|
|
2370
|
+
text.textContent = spec.label;
|
|
2371
|
+
const group = svgEl("g", { class: cls }, circle, text);
|
|
2372
|
+
if (spec.autofocus) {
|
|
2373
|
+
group.appendChild(
|
|
2374
|
+
svgEl(
|
|
2375
|
+
"g",
|
|
2376
|
+
{ class: "ooo-af", transform: `translate(${RADIUS}, ${-RADIUS})` },
|
|
2377
|
+
svgEl("circle", { r: 7 }),
|
|
2378
|
+
svgEl("path", { d: "M-3,-2 L3,-2 L0,2.5 Z" })
|
|
2379
|
+
)
|
|
2380
|
+
);
|
|
2381
|
+
}
|
|
2382
|
+
this.svg.appendChild(group);
|
|
2383
|
+
const marker = {
|
|
2384
|
+
element: spec.element,
|
|
2385
|
+
group,
|
|
2386
|
+
centerX: 0,
|
|
2387
|
+
centerY: 0
|
|
2388
|
+
};
|
|
2389
|
+
this.markers.push(marker);
|
|
2390
|
+
this.byEl.set(spec.element, marker);
|
|
2391
|
+
this.tooltip.wire(group, spec.tip);
|
|
2392
|
+
}
|
|
2393
|
+
markRing(element, severity) {
|
|
2394
|
+
const ringClass = severity === "error" ? RING_BAD_CLASS : severity === "warning" ? RING_WARN_CLASS : RING_CLASS;
|
|
2395
|
+
if (this.ringsVisible) {
|
|
2396
|
+
element.classList.add(ringClass);
|
|
2397
|
+
}
|
|
2398
|
+
this.ringEls.push({ element, cls: ringClass });
|
|
2399
|
+
const root = element.getRootNode();
|
|
2400
|
+
if (root instanceof ShadowRoot) {
|
|
2401
|
+
ensureRingStyles(root);
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
applyRect(marker, rect) {
|
|
2405
|
+
if (rect.width < SMALL_SIZE && rect.height < SMALL_SIZE) {
|
|
2406
|
+
marker.centerX = rect.right + RADIUS;
|
|
2407
|
+
marker.centerY = rect.top - RADIUS;
|
|
2408
|
+
} else {
|
|
2409
|
+
marker.centerX = rect.left + rect.width / 2;
|
|
2410
|
+
marker.centerY = rect.top + rect.height / 2;
|
|
2411
|
+
}
|
|
2412
|
+
marker.group.setAttribute("transform", `translate(${marker.centerX}, ${marker.centerY})`);
|
|
2413
|
+
}
|
|
2414
|
+
/** Redraw every hop: shorten it to the badge edges and drop the arrow at the
|
|
2415
|
+
midpoint. Colour (green forward / red backward) is fixed at analyze time
|
|
2416
|
+
(see Segment.back) and not recomputed here, so it can't drift from the ring. */
|
|
2417
|
+
updateSegments() {
|
|
2418
|
+
for (const seg of this.segments) {
|
|
2419
|
+
const deltaX = seg.toMarker.centerX - seg.from.centerX;
|
|
2420
|
+
const deltaY = seg.toMarker.centerY - seg.from.centerY;
|
|
2421
|
+
const len = Math.hypot(deltaX, deltaY) || 1;
|
|
2422
|
+
const unitX = deltaX / len;
|
|
2423
|
+
const unitY = deltaY / len;
|
|
2424
|
+
let startX = seg.from.centerX;
|
|
2425
|
+
let startY = seg.from.centerY;
|
|
2426
|
+
let endX = seg.toMarker.centerX;
|
|
2427
|
+
let endY = seg.toMarker.centerY;
|
|
2428
|
+
if (len > 2 * SEG_PAD + 6) {
|
|
2429
|
+
startX = seg.from.centerX + unitX * SEG_PAD;
|
|
2430
|
+
startY = seg.from.centerY + unitY * SEG_PAD;
|
|
2431
|
+
endX = seg.toMarker.centerX - unitX * SEG_PAD;
|
|
2432
|
+
endY = seg.toMarker.centerY - unitY * SEG_PAD;
|
|
2433
|
+
}
|
|
2434
|
+
const midX = (startX + endX) / 2;
|
|
2435
|
+
const midY = (startY + endY) / 2;
|
|
2436
|
+
seg.hit.setAttribute("x1", String(startX));
|
|
2437
|
+
seg.hit.setAttribute("y1", String(startY));
|
|
2438
|
+
seg.hit.setAttribute("x2", String(endX));
|
|
2439
|
+
seg.hit.setAttribute("y2", String(endY));
|
|
2440
|
+
seg.line.setAttribute("points", `${startX},${startY} ${midX},${midY} ${endX},${endY}`);
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
syncSize() {
|
|
2444
|
+
if (!this.svg) {
|
|
2445
|
+
return;
|
|
2446
|
+
}
|
|
2447
|
+
const width = document.documentElement.clientWidth;
|
|
2448
|
+
const height = document.documentElement.clientHeight;
|
|
2449
|
+
if (width === this.sizeW && height === this.sizeH) {
|
|
2450
|
+
return;
|
|
2451
|
+
}
|
|
2452
|
+
this.sizeW = width;
|
|
2453
|
+
this.sizeH = height;
|
|
2454
|
+
this.svg.setAttribute("width", String(width));
|
|
2455
|
+
this.svg.setAttribute("height", String(height));
|
|
2456
|
+
}
|
|
2457
|
+
};
|
|
2458
|
+
function buildDefs() {
|
|
2459
|
+
const defs = svgEl("defs");
|
|
2460
|
+
for (const [markerId, fill] of [
|
|
2461
|
+
["ooo-arrow", "#2f6a47"],
|
|
2462
|
+
["ooo-arrow-back", "#b3261e"]
|
|
2463
|
+
]) {
|
|
2464
|
+
const path = svgEl("path", { d: "M3,2.5 L14,8 L3,13.5 L6.5,8 Z", fill });
|
|
2465
|
+
const marker = svgEl(
|
|
2466
|
+
"marker",
|
|
2467
|
+
{
|
|
2468
|
+
id: markerId,
|
|
2469
|
+
markerWidth: 16,
|
|
2470
|
+
markerHeight: 16,
|
|
2471
|
+
refX: 8,
|
|
2472
|
+
refY: 8,
|
|
2473
|
+
orient: "auto",
|
|
2474
|
+
markerUnits: "userSpaceOnUse"
|
|
2475
|
+
},
|
|
2476
|
+
path
|
|
2477
|
+
);
|
|
2478
|
+
defs.appendChild(marker);
|
|
2479
|
+
}
|
|
2480
|
+
return defs;
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2483
|
+
// ../../node_modules/.pnpm/position-observer@1.0.3/node_modules/position-observer/dist/index.js
|
|
2484
|
+
var Rect = class {
|
|
2485
|
+
/**
|
|
2486
|
+
* Intersects two DOMRects.
|
|
2487
|
+
*
|
|
2488
|
+
* @param rect1 - The first DOMRect.
|
|
2489
|
+
* @param rect2 - The second DOMRect.
|
|
2490
|
+
* @returns The intersection of the two DOMRects.
|
|
2491
|
+
*/
|
|
2492
|
+
static intersect(rect1, rect2) {
|
|
2493
|
+
const left = Math.max(rect1.left, rect2.left);
|
|
2494
|
+
const right = Math.min(rect1.right, rect2.right);
|
|
2495
|
+
const top = Math.max(rect1.top, rect2.top);
|
|
2496
|
+
const bottom = Math.min(rect1.bottom, rect2.bottom);
|
|
2497
|
+
const width = Math.max(0, right - left);
|
|
2498
|
+
const height = Math.max(0, bottom - top);
|
|
2499
|
+
return new DOMRect(left, top, width, height);
|
|
2500
|
+
}
|
|
2501
|
+
/**
|
|
2502
|
+
* Clips a DOMRect to a given rectangle.
|
|
2503
|
+
*
|
|
2504
|
+
* @param rect - The DOMRect to clip.
|
|
2505
|
+
* @param clip - The rectangle to clip the DOMRect to.
|
|
2506
|
+
* @returns The clipped DOMRect.
|
|
2507
|
+
*/
|
|
2508
|
+
static clip(rect, clip) {
|
|
2509
|
+
const updatedRect = {
|
|
2510
|
+
...rect.toJSON(),
|
|
2511
|
+
top: rect.top + clip.top,
|
|
2512
|
+
left: rect.left + clip.left,
|
|
2513
|
+
bottom: rect.bottom - clip.bottom,
|
|
2514
|
+
right: rect.right - clip.right
|
|
2515
|
+
};
|
|
2516
|
+
updatedRect.width = updatedRect.right - updatedRect.left;
|
|
2517
|
+
updatedRect.height = updatedRect.bottom - updatedRect.top;
|
|
2518
|
+
return new DOMRect(
|
|
2519
|
+
updatedRect.left,
|
|
2520
|
+
updatedRect.top,
|
|
2521
|
+
updatedRect.width,
|
|
2522
|
+
updatedRect.height
|
|
2523
|
+
);
|
|
2524
|
+
}
|
|
2525
|
+
/**
|
|
2526
|
+
* Calculates the offsets of a clipped DOMRect.
|
|
2527
|
+
*
|
|
2528
|
+
* @param rect - The DOMRect to calculate the offsets for.
|
|
2529
|
+
* @param clippedRect - The clipped DOMRect.
|
|
2530
|
+
* @returns The offsets of the clipped DOMRect.
|
|
2531
|
+
*/
|
|
2532
|
+
static clipOffsets(rect, clippedRect) {
|
|
2533
|
+
return {
|
|
2534
|
+
top: clippedRect.top - rect.top,
|
|
2535
|
+
left: clippedRect.left - rect.left,
|
|
2536
|
+
bottom: rect.bottom - clippedRect.bottom,
|
|
2537
|
+
right: rect.right - clippedRect.right
|
|
2538
|
+
};
|
|
2539
|
+
}
|
|
2540
|
+
/**
|
|
2541
|
+
* Checks if two DOMRects are equal.
|
|
2542
|
+
*
|
|
2543
|
+
* @param rect1 - The first DOMRect.
|
|
2544
|
+
* @param rect2 - The second DOMRect.
|
|
2545
|
+
* @returns True if the DOMRects are equal, false otherwise.
|
|
2546
|
+
*/
|
|
2547
|
+
static equals(rect1, rect2) {
|
|
2548
|
+
if (rect1 == null || rect2 == null) return rect1 === rect2;
|
|
2549
|
+
return rect1.x === rect2.x && rect1.y === rect2.y && rect1.width === rect2.width && rect1.height === rect2.height;
|
|
2550
|
+
}
|
|
2551
|
+
static sizeEqual(rect1, rect2) {
|
|
2552
|
+
return Math.round(rect1.width) === Math.round(rect2.width) && Math.round(rect1.height) === Math.round(rect2.height);
|
|
2553
|
+
}
|
|
2554
|
+
};
|
|
2555
|
+
var INSET = -1;
|
|
2556
|
+
var MIN_SIZE = 1 - INSET * 2;
|
|
2557
|
+
function rootMargin(rect, rootBounds) {
|
|
2558
|
+
const width = Math.max(rect.width, MIN_SIZE);
|
|
2559
|
+
const height = Math.max(rect.height, MIN_SIZE);
|
|
2560
|
+
const top = rect.top - rootBounds.top - INSET;
|
|
2561
|
+
const left = rect.left - rootBounds.left - INSET;
|
|
2562
|
+
const right = rootBounds.right - rect.left - width - INSET;
|
|
2563
|
+
const bottom = rootBounds.bottom - rect.top - height - INSET;
|
|
2564
|
+
return `${-Math.round(top)}px ${-Math.round(right)}px ${-Math.round(bottom)}px ${-Math.round(left)}px`;
|
|
2565
|
+
}
|
|
2566
|
+
var threshold = [
|
|
2567
|
+
...Array.from({ length: 1e3 }, (_, i) => i / 1e3),
|
|
2568
|
+
1
|
|
2569
|
+
];
|
|
2570
|
+
var _callback, _observer, _options, _clientRect, _previousIntersectionRatio, _PositionIntersectionObserver_instances, observe_fn, _onIntersection, _a;
|
|
2571
|
+
var PositionIntersectionObserver = (_a = class {
|
|
2572
|
+
constructor(element, callback, options) {
|
|
2573
|
+
__privateAdd(this, _PositionIntersectionObserver_instances);
|
|
2574
|
+
__privateAdd(this, _callback);
|
|
2575
|
+
__privateAdd(this, _observer);
|
|
2576
|
+
__privateAdd(this, _options);
|
|
2577
|
+
__privateAdd(this, _clientRect);
|
|
2578
|
+
__privateAdd(this, _previousIntersectionRatio);
|
|
2579
|
+
/**
|
|
2580
|
+
* The callback function for the intersection observer.
|
|
2581
|
+
*/
|
|
2582
|
+
__privateAdd(this, _onIntersection, (entries) => {
|
|
2583
|
+
if (!__privateGet(this, _observer)) return;
|
|
2584
|
+
const entry = entries[entries.length - 1];
|
|
2585
|
+
if (entry) {
|
|
2586
|
+
const { intersectionRatio, boundingClientRect } = entry;
|
|
2587
|
+
const previousClientRect = __privateGet(this, _clientRect);
|
|
2588
|
+
__privateSet(this, _clientRect, boundingClientRect);
|
|
2589
|
+
const previousIntersectionRatio = __privateGet(this, _previousIntersectionRatio);
|
|
2590
|
+
const clientRectChanged = !Rect.equals(
|
|
2591
|
+
boundingClientRect,
|
|
2592
|
+
previousClientRect
|
|
2593
|
+
);
|
|
2594
|
+
if (intersectionRatio !== __privateGet(this, _previousIntersectionRatio) || clientRectChanged) {
|
|
2595
|
+
const rootBounds = __privateGet(this, _options).rootBounds;
|
|
2596
|
+
const rootIntersection = Rect.intersect(boundingClientRect, rootBounds);
|
|
2597
|
+
const isIntersecting = rootIntersection.width > 0 && rootIntersection.height > 0;
|
|
2598
|
+
if (!isIntersecting) {
|
|
2599
|
+
return;
|
|
2600
|
+
}
|
|
2601
|
+
__privateSet(this, _previousIntersectionRatio, intersectionRatio);
|
|
2602
|
+
if (previousIntersectionRatio != null || clientRectChanged) {
|
|
2603
|
+
__privateGet(this, _callback).call(this, new PositionIntersectionObserverEntry(
|
|
2604
|
+
entry.target,
|
|
2605
|
+
boundingClientRect,
|
|
2606
|
+
entry.intersectionRect,
|
|
2607
|
+
isIntersecting,
|
|
2608
|
+
rootBounds
|
|
2609
|
+
), this);
|
|
2610
|
+
__privateMethod(this, _PositionIntersectionObserver_instances, observe_fn).call(this, entry.target);
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
});
|
|
2615
|
+
__privateSet(this, _callback, callback);
|
|
2616
|
+
__privateSet(this, _options, options);
|
|
2617
|
+
__privateSet(this, _clientRect, options.clientRect);
|
|
2618
|
+
__privateMethod(this, _PositionIntersectionObserver_instances, observe_fn).call(this, element);
|
|
2619
|
+
}
|
|
2620
|
+
/**
|
|
2621
|
+
* The visible rectangle of the element within the root.
|
|
2622
|
+
*
|
|
2623
|
+
* @returns The visible rectangle of the element within the root.
|
|
2624
|
+
*/
|
|
2625
|
+
get visibleRect() {
|
|
2626
|
+
const clip = __privateGet(this, _options).clip;
|
|
2627
|
+
return clip ? Rect.clip(__privateGet(this, _clientRect), clip) : __privateGet(this, _clientRect);
|
|
2628
|
+
}
|
|
2629
|
+
/**
|
|
2630
|
+
* Whether the element is intersecting with the root.
|
|
2631
|
+
*
|
|
2632
|
+
* @returns Whether the element is intersecting with the root.
|
|
2633
|
+
*/
|
|
2634
|
+
get isIntersecting() {
|
|
2635
|
+
const { width, height } = this.visibleRect;
|
|
2636
|
+
return width > 0 && height > 0;
|
|
2637
|
+
}
|
|
2638
|
+
/**
|
|
2639
|
+
* Disconnects the position intersection observer.
|
|
2640
|
+
*/
|
|
2641
|
+
disconnect() {
|
|
2642
|
+
__privateGet(this, _observer)?.disconnect();
|
|
2643
|
+
}
|
|
2644
|
+
}, _callback = new WeakMap(), _observer = new WeakMap(), _options = new WeakMap(), _clientRect = new WeakMap(), _previousIntersectionRatio = new WeakMap(), _PositionIntersectionObserver_instances = new WeakSet(), /**
|
|
2645
|
+
* Observes the element.
|
|
2646
|
+
*/
|
|
2647
|
+
observe_fn = function(element) {
|
|
2648
|
+
const { root, rootBounds } = __privateGet(this, _options);
|
|
2649
|
+
const { visibleRect } = this;
|
|
2650
|
+
__privateGet(this, _observer)?.disconnect();
|
|
2651
|
+
__privateSet(this, _observer, new IntersectionObserver(__privateGet(this, _onIntersection), {
|
|
2652
|
+
root,
|
|
2653
|
+
rootMargin: rootMargin(visibleRect, rootBounds),
|
|
2654
|
+
threshold
|
|
2655
|
+
}));
|
|
2656
|
+
__privateGet(this, _observer).observe(element);
|
|
2657
|
+
}, _onIntersection = new WeakMap(), _a);
|
|
2658
|
+
var PositionIntersectionObserverEntry = class {
|
|
2659
|
+
constructor(target, boundingClientRect, intersectionRect, isIntersecting, rootBounds) {
|
|
2660
|
+
this.target = target;
|
|
2661
|
+
this.boundingClientRect = boundingClientRect;
|
|
2662
|
+
this.intersectionRect = intersectionRect;
|
|
2663
|
+
this.isIntersecting = isIntersecting;
|
|
2664
|
+
this.rootBounds = rootBounds;
|
|
2665
|
+
}
|
|
2666
|
+
};
|
|
2667
|
+
var _resizeObserver, _controller, _a2;
|
|
2668
|
+
var RootBoundsObserver = (_a2 = class {
|
|
2669
|
+
/**
|
|
2670
|
+
* Creates a new root bounds observer.
|
|
2671
|
+
*
|
|
2672
|
+
* @param root - The root element to observe.
|
|
2673
|
+
* @param callback - The callback function to call when the root bounds change.
|
|
2674
|
+
*/
|
|
2675
|
+
constructor(target, callback) {
|
|
2676
|
+
/**
|
|
2677
|
+
* The resize observer if the root is an element.
|
|
2678
|
+
*/
|
|
2679
|
+
__privateAdd(this, _resizeObserver);
|
|
2680
|
+
/**
|
|
2681
|
+
* The controller to disconnect the root bounds resize and scroll listeners.
|
|
2682
|
+
*/
|
|
2683
|
+
__privateAdd(this, _controller, new AbortController());
|
|
2684
|
+
/**
|
|
2685
|
+
* The bounds of the root element.
|
|
2686
|
+
*/
|
|
2687
|
+
__publicField(this, "rootBounds");
|
|
2688
|
+
const root = getRoot(target);
|
|
2689
|
+
if (isElement2(root)) {
|
|
2690
|
+
const ownerDocument = root.ownerDocument ?? document;
|
|
2691
|
+
this.rootBounds = root.getBoundingClientRect();
|
|
2692
|
+
__privateSet(this, _resizeObserver, new ResizeObserver((entries) => {
|
|
2693
|
+
for (const entry of entries) {
|
|
2694
|
+
const [{ inlineSize: width, blockSize: height }] = entry.borderBoxSize;
|
|
2695
|
+
if (Rect.sizeEqual(this.rootBounds, { width, height })) {
|
|
2696
|
+
continue;
|
|
2697
|
+
}
|
|
2698
|
+
const rect = entry.target.getBoundingClientRect();
|
|
2699
|
+
this.rootBounds = rect;
|
|
2700
|
+
callback(rect, this);
|
|
2701
|
+
}
|
|
2702
|
+
}));
|
|
2703
|
+
__privateGet(this, _resizeObserver).observe(root);
|
|
2704
|
+
ownerDocument.addEventListener(
|
|
2705
|
+
"scroll",
|
|
2706
|
+
(event) => {
|
|
2707
|
+
if (event.target && event.target !== root && isNode(event.target) && event.target.contains(root)) {
|
|
2708
|
+
this.rootBounds = root.getBoundingClientRect();
|
|
2709
|
+
callback(this.rootBounds, this);
|
|
2710
|
+
}
|
|
2711
|
+
},
|
|
2712
|
+
{ capture: true, passive: true, signal: __privateGet(this, _controller).signal }
|
|
2713
|
+
);
|
|
2714
|
+
} else {
|
|
2715
|
+
const viewport = root.visualViewport ?? root;
|
|
2716
|
+
this.rootBounds = getWindowRect(root);
|
|
2717
|
+
const handleResize = () => {
|
|
2718
|
+
const rect = getWindowRect(root);
|
|
2719
|
+
if (Rect.equals(this.rootBounds, rect)) return;
|
|
2720
|
+
this.rootBounds = rect;
|
|
2721
|
+
callback(rect, this);
|
|
2722
|
+
};
|
|
2723
|
+
viewport.addEventListener("resize", handleResize, {
|
|
2724
|
+
signal: __privateGet(this, _controller).signal
|
|
2725
|
+
});
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
/**
|
|
2729
|
+
* Disconnects the root bounds observer.
|
|
2730
|
+
*/
|
|
2731
|
+
disconnect() {
|
|
2732
|
+
__privateGet(this, _resizeObserver)?.disconnect();
|
|
2733
|
+
__privateGet(this, _controller).abort();
|
|
2734
|
+
}
|
|
2735
|
+
}, _resizeObserver = new WeakMap(), _controller = new WeakMap(), _a2);
|
|
2736
|
+
function getWindowRect(window2) {
|
|
2737
|
+
const width = window2.visualViewport?.width ?? window2.innerWidth;
|
|
2738
|
+
const height = window2.visualViewport?.height ?? window2.innerHeight;
|
|
2739
|
+
return new DOMRect(0, 0, width, height);
|
|
2740
|
+
}
|
|
2741
|
+
function isNode(target) {
|
|
2742
|
+
return "nodeType" in target;
|
|
2743
|
+
}
|
|
2744
|
+
function isElement2(target) {
|
|
2745
|
+
return isNode(target) && target.nodeType === Node.ELEMENT_NODE;
|
|
2746
|
+
}
|
|
2747
|
+
function isDocument(root) {
|
|
2748
|
+
return root.nodeType === Node.DOCUMENT_NODE;
|
|
2749
|
+
}
|
|
2750
|
+
function getRoot(target) {
|
|
2751
|
+
return !target || isDocument(target) ? target?.defaultView ?? window : target;
|
|
2752
|
+
}
|
|
2753
|
+
var _callback2, _observers, _options2, _a3;
|
|
2754
|
+
var VisibilityObserver = (_a3 = class {
|
|
2755
|
+
constructor(callback, options) {
|
|
2756
|
+
/**
|
|
2757
|
+
* The callback function to be invoked when the intersection entries change.
|
|
2758
|
+
*/
|
|
2759
|
+
__privateAdd(this, _callback2);
|
|
2760
|
+
/**
|
|
2761
|
+
* The visibility observer for each document.
|
|
2762
|
+
*/
|
|
2763
|
+
__privateAdd(this, _observers, /* @__PURE__ */ new Map());
|
|
2764
|
+
/**
|
|
2765
|
+
* The options for the visibility observer.
|
|
2766
|
+
*/
|
|
2767
|
+
__privateAdd(this, _options2);
|
|
2768
|
+
/**
|
|
2769
|
+
* The latest intersection entries for the observed elements.
|
|
2770
|
+
*/
|
|
2771
|
+
__publicField(this, "intersections", /* @__PURE__ */ new WeakMap());
|
|
2772
|
+
__privateSet(this, _options2, options);
|
|
2773
|
+
__privateSet(this, _callback2, (entries) => {
|
|
2774
|
+
const changedEntries = [];
|
|
2775
|
+
for (const entry of entries) {
|
|
2776
|
+
const previousIntersection = this.intersections.get(entry.target);
|
|
2777
|
+
this.intersections.set(entry.target, entry);
|
|
2778
|
+
if (previousIntersection?.isIntersecting !== entry.isIntersecting || !Rect.equals(
|
|
2779
|
+
previousIntersection?.intersectionRect,
|
|
2780
|
+
entry.intersectionRect
|
|
2781
|
+
)) {
|
|
2782
|
+
changedEntries.push(entry);
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
if (changedEntries.length > 0) {
|
|
2786
|
+
callback(changedEntries, this);
|
|
2787
|
+
}
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
2790
|
+
/**
|
|
2791
|
+
* Observes an element.
|
|
2792
|
+
*
|
|
2793
|
+
* @param element - The element to observe.
|
|
2794
|
+
*/
|
|
2795
|
+
observe(element) {
|
|
2796
|
+
const document2 = element.ownerDocument;
|
|
2797
|
+
if (!document2) return;
|
|
2798
|
+
let observer = __privateGet(this, _observers).get(document2);
|
|
2799
|
+
if (!observer) {
|
|
2800
|
+
observer = new IntersectionObserver(__privateGet(this, _callback2), {
|
|
2801
|
+
...__privateGet(this, _options2),
|
|
2802
|
+
threshold
|
|
2803
|
+
});
|
|
2804
|
+
__privateGet(this, _observers).set(document2, observer);
|
|
2805
|
+
}
|
|
2806
|
+
observer.observe(element);
|
|
2807
|
+
}
|
|
2808
|
+
/**
|
|
2809
|
+
* Unobserves an element.
|
|
2810
|
+
*
|
|
2811
|
+
* @param element - The element to unobserve.
|
|
2812
|
+
*/
|
|
2813
|
+
unobserve(element) {
|
|
2814
|
+
const document2 = element.ownerDocument;
|
|
2815
|
+
if (!document2) return;
|
|
2816
|
+
const observer = __privateGet(this, _observers).get(document2);
|
|
2817
|
+
if (!observer) return;
|
|
2818
|
+
observer.unobserve(element);
|
|
2819
|
+
this.intersections.delete(element);
|
|
2820
|
+
}
|
|
2821
|
+
/**
|
|
2822
|
+
* Disconnects the visibility observer.
|
|
2823
|
+
*/
|
|
2824
|
+
disconnect() {
|
|
2825
|
+
for (const observer of __privateGet(this, _observers).values()) {
|
|
2826
|
+
observer.disconnect();
|
|
2827
|
+
}
|
|
2828
|
+
__privateGet(this, _observers).clear();
|
|
2829
|
+
}
|
|
2830
|
+
}, _callback2 = new WeakMap(), _observers = new WeakMap(), _options2 = new WeakMap(), _a3);
|
|
2831
|
+
var _callback3, _options3, _positionObservers, _resizeObserver2, _positions, _rootBoundsObserver, _visibilityObserver, _PositionObserver_instances, notify_fn, _onRootBoundsChange, observePosition_fn, _onVisibilityChange, _onPositionChange, _onResize, _a4;
|
|
2832
|
+
var PositionObserver = (_a4 = class {
|
|
2833
|
+
constructor(callback, options) {
|
|
2834
|
+
__privateAdd(this, _PositionObserver_instances);
|
|
2835
|
+
/**
|
|
2836
|
+
* The callback function to be invoked when the position changes.
|
|
2837
|
+
*/
|
|
2838
|
+
__privateAdd(this, _callback3);
|
|
2839
|
+
/**
|
|
2840
|
+
* The options for the position observer.
|
|
2841
|
+
*/
|
|
2842
|
+
__privateAdd(this, _options3);
|
|
2843
|
+
/**
|
|
2844
|
+
* The position observers for the observed elements.
|
|
2845
|
+
*/
|
|
2846
|
+
__privateAdd(this, _positionObservers, /* @__PURE__ */ new Map());
|
|
2847
|
+
/**
|
|
2848
|
+
* The resize observer for the observed elements.
|
|
2849
|
+
*/
|
|
2850
|
+
__privateAdd(this, _resizeObserver2);
|
|
2851
|
+
/**
|
|
2852
|
+
* The positions of the observed elements.
|
|
2853
|
+
*/
|
|
2854
|
+
__privateAdd(this, _positions, /* @__PURE__ */ new WeakMap());
|
|
2855
|
+
/**
|
|
2856
|
+
* The root bounds observer for the observed elements.
|
|
2857
|
+
*/
|
|
2858
|
+
__privateAdd(this, _rootBoundsObserver);
|
|
2859
|
+
/**
|
|
2860
|
+
* The visibility observer for the observed elements.
|
|
2861
|
+
*/
|
|
2862
|
+
__privateAdd(this, _visibilityObserver);
|
|
2863
|
+
/**
|
|
2864
|
+
* The callback function to be invoked when the root bounds change.
|
|
2865
|
+
*/
|
|
2866
|
+
__privateAdd(this, _onRootBoundsChange, (rootBounds) => {
|
|
2867
|
+
const entries = [];
|
|
2868
|
+
for (const [element] of __privateGet(this, _positionObservers)) {
|
|
2869
|
+
const boundingClientRect = element.getBoundingClientRect();
|
|
2870
|
+
const observer = __privateMethod(this, _PositionObserver_instances, observePosition_fn).call(this, element, boundingClientRect);
|
|
2871
|
+
entries.push(
|
|
2872
|
+
new PositionObserverEntry(
|
|
2873
|
+
element,
|
|
2874
|
+
boundingClientRect,
|
|
2875
|
+
observer.visibleRect,
|
|
2876
|
+
observer.isIntersecting,
|
|
2877
|
+
rootBounds
|
|
2878
|
+
)
|
|
2879
|
+
);
|
|
2880
|
+
}
|
|
2881
|
+
__privateMethod(this, _PositionObserver_instances, notify_fn).call(this, entries);
|
|
2882
|
+
});
|
|
2883
|
+
/**
|
|
2884
|
+
* The callback function to be invoked when the visibility changes.
|
|
2885
|
+
*/
|
|
2886
|
+
__privateAdd(this, _onVisibilityChange, (entries) => {
|
|
2887
|
+
const records = [];
|
|
2888
|
+
for (const entry of entries) {
|
|
2889
|
+
const { target, isIntersecting, boundingClientRect } = entry;
|
|
2890
|
+
if (isIntersecting) {
|
|
2891
|
+
__privateMethod(this, _PositionObserver_instances, observePosition_fn).call(this, target, boundingClientRect);
|
|
2892
|
+
__privateGet(this, _resizeObserver2).observe(target);
|
|
2893
|
+
} else {
|
|
2894
|
+
__privateGet(this, _positionObservers).get(target)?.disconnect();
|
|
2895
|
+
__privateGet(this, _positionObservers).delete(target);
|
|
2896
|
+
__privateGet(this, _resizeObserver2).unobserve(target);
|
|
2897
|
+
}
|
|
2898
|
+
const observer = __privateGet(this, _positionObservers).get(target);
|
|
2899
|
+
records.push(
|
|
2900
|
+
new PositionObserverEntry(
|
|
2901
|
+
target,
|
|
2902
|
+
boundingClientRect,
|
|
2903
|
+
observer?.visibleRect ?? entry.intersectionRect,
|
|
2904
|
+
isIntersecting,
|
|
2905
|
+
__privateGet(this, _rootBoundsObserver).rootBounds
|
|
2906
|
+
)
|
|
2907
|
+
);
|
|
2908
|
+
}
|
|
2909
|
+
__privateMethod(this, _PositionObserver_instances, notify_fn).call(this, records);
|
|
2910
|
+
});
|
|
2911
|
+
/**
|
|
2912
|
+
* The callback function to be invoked when the position changes.
|
|
2913
|
+
*/
|
|
2914
|
+
__privateAdd(this, _onPositionChange, (entry, observer) => {
|
|
2915
|
+
__privateMethod(this, _PositionObserver_instances, notify_fn).call(this, [
|
|
2916
|
+
new PositionObserverEntry(
|
|
2917
|
+
entry.target,
|
|
2918
|
+
entry.boundingClientRect,
|
|
2919
|
+
observer.visibleRect,
|
|
2920
|
+
entry.isIntersecting,
|
|
2921
|
+
__privateGet(this, _rootBoundsObserver).rootBounds
|
|
2922
|
+
)
|
|
2923
|
+
]);
|
|
2924
|
+
});
|
|
2925
|
+
/**
|
|
2926
|
+
* The callback function to be invoked when the resize observer entries change.
|
|
2927
|
+
*/
|
|
2928
|
+
__privateAdd(this, _onResize, (entries) => {
|
|
2929
|
+
const records = [];
|
|
2930
|
+
for (const entry of entries) {
|
|
2931
|
+
const { target, borderBoxSize } = entry;
|
|
2932
|
+
const previous = __privateGet(this, _positions).get(target);
|
|
2933
|
+
if (previous) {
|
|
2934
|
+
const [{ inlineSize: width, blockSize: height }] = borderBoxSize;
|
|
2935
|
+
if (Rect.sizeEqual(previous.boundingClientRect, { width, height })) {
|
|
2936
|
+
continue;
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
const boundingClientRect = target.getBoundingClientRect();
|
|
2940
|
+
const observer = __privateMethod(this, _PositionObserver_instances, observePosition_fn).call(this, target, boundingClientRect);
|
|
2941
|
+
records.push(
|
|
2942
|
+
new PositionObserverEntry(
|
|
2943
|
+
target,
|
|
2944
|
+
boundingClientRect,
|
|
2945
|
+
observer.visibleRect,
|
|
2946
|
+
__privateGet(this, _visibilityObserver).intersections.get(target)?.isIntersecting ?? false,
|
|
2947
|
+
__privateGet(this, _rootBoundsObserver).rootBounds
|
|
2948
|
+
)
|
|
2949
|
+
);
|
|
2950
|
+
}
|
|
2951
|
+
__privateMethod(this, _PositionObserver_instances, notify_fn).call(this, records);
|
|
2952
|
+
});
|
|
2953
|
+
__privateSet(this, _callback3, callback);
|
|
2954
|
+
__privateSet(this, _options3, options);
|
|
2955
|
+
__privateSet(this, _rootBoundsObserver, new RootBoundsObserver(
|
|
2956
|
+
options?.root,
|
|
2957
|
+
__privateGet(this, _onRootBoundsChange)
|
|
2958
|
+
));
|
|
2959
|
+
__privateSet(this, _visibilityObserver, new VisibilityObserver(
|
|
2960
|
+
__privateGet(this, _onVisibilityChange),
|
|
2961
|
+
options
|
|
2962
|
+
));
|
|
2963
|
+
__privateSet(this, _resizeObserver2, new ResizeObserver(__privateGet(this, _onResize)));
|
|
2964
|
+
}
|
|
2965
|
+
/**
|
|
2966
|
+
* Observes an element.
|
|
2967
|
+
*
|
|
2968
|
+
* @param element - The element to observe.
|
|
2969
|
+
*/
|
|
2970
|
+
observe(element) {
|
|
2971
|
+
__privateGet(this, _visibilityObserver).observe(element);
|
|
2972
|
+
}
|
|
2973
|
+
/**
|
|
2974
|
+
* Unobserves an element.
|
|
2975
|
+
*
|
|
2976
|
+
* @param element - The element to unobserve. If not provided, the position observer is disconnected.
|
|
2977
|
+
*/
|
|
2978
|
+
unobserve(element) {
|
|
2979
|
+
if (element) {
|
|
2980
|
+
__privateGet(this, _positionObservers).get(element)?.disconnect();
|
|
2981
|
+
__privateGet(this, _visibilityObserver).unobserve(element);
|
|
2982
|
+
__privateGet(this, _positions).delete(element);
|
|
2983
|
+
} else {
|
|
2984
|
+
this.disconnect();
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
/**
|
|
2988
|
+
* Disconnects the observer.
|
|
2989
|
+
*/
|
|
2990
|
+
disconnect() {
|
|
2991
|
+
for (const positionObserver of __privateGet(this, _positionObservers).values()) {
|
|
2992
|
+
positionObserver.disconnect();
|
|
2993
|
+
}
|
|
2994
|
+
__privateGet(this, _positionObservers).clear();
|
|
2995
|
+
__privateGet(this, _resizeObserver2).disconnect();
|
|
2996
|
+
__privateGet(this, _rootBoundsObserver).disconnect();
|
|
2997
|
+
__privateGet(this, _visibilityObserver).disconnect();
|
|
2998
|
+
}
|
|
2999
|
+
}, _callback3 = new WeakMap(), _options3 = new WeakMap(), _positionObservers = new WeakMap(), _resizeObserver2 = new WeakMap(), _positions = new WeakMap(), _rootBoundsObserver = new WeakMap(), _visibilityObserver = new WeakMap(), _PositionObserver_instances = new WeakSet(), /**
|
|
3000
|
+
* Notifies the position observer of the changes.
|
|
3001
|
+
*
|
|
3002
|
+
* @param entries - The entries to notify.
|
|
3003
|
+
*/
|
|
3004
|
+
notify_fn = function(entries) {
|
|
3005
|
+
const records = [];
|
|
3006
|
+
for (const entry of entries) {
|
|
3007
|
+
const { target } = entry;
|
|
3008
|
+
const previousEntry = __privateGet(this, _positions).get(target);
|
|
3009
|
+
if (isEntryEqual(entry, previousEntry)) continue;
|
|
3010
|
+
__privateGet(this, _positions).set(target, entry);
|
|
3011
|
+
records.push(entry);
|
|
3012
|
+
}
|
|
3013
|
+
if (records.length > 0) {
|
|
3014
|
+
__privateGet(this, _callback3).call(this, records);
|
|
3015
|
+
}
|
|
3016
|
+
}, _onRootBoundsChange = new WeakMap(), /**
|
|
3017
|
+
* Observes the position of an element.
|
|
3018
|
+
*
|
|
3019
|
+
* @param element - The element to observe.
|
|
3020
|
+
* @param clientRect - The client rect of the element.
|
|
3021
|
+
*/
|
|
3022
|
+
observePosition_fn = function(element, clientRect) {
|
|
3023
|
+
const visibilityObserver = __privateGet(this, _visibilityObserver);
|
|
3024
|
+
__privateGet(this, _positionObservers).get(element)?.disconnect();
|
|
3025
|
+
const positionObserver = new PositionIntersectionObserver(
|
|
3026
|
+
element,
|
|
3027
|
+
__privateGet(this, _onPositionChange),
|
|
3028
|
+
{
|
|
3029
|
+
clientRect,
|
|
3030
|
+
root: __privateGet(this, _options3)?.root,
|
|
3031
|
+
rootBounds: __privateGet(this, _rootBoundsObserver).rootBounds,
|
|
3032
|
+
get clip() {
|
|
3033
|
+
const intersection = visibilityObserver.intersections.get(element);
|
|
3034
|
+
if (!intersection) return;
|
|
3035
|
+
const { intersectionRect, boundingClientRect } = intersection;
|
|
3036
|
+
return Rect.clipOffsets(boundingClientRect, intersectionRect);
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
);
|
|
3040
|
+
__privateGet(this, _positionObservers).set(element, positionObserver);
|
|
3041
|
+
return positionObserver;
|
|
3042
|
+
}, _onVisibilityChange = new WeakMap(), _onPositionChange = new WeakMap(), _onResize = new WeakMap(), _a4);
|
|
3043
|
+
var PositionObserverEntry = class {
|
|
3044
|
+
constructor(target, boundingClientRect, intersectionRect, isIntersecting, rootBounds) {
|
|
3045
|
+
this.target = target;
|
|
3046
|
+
this.boundingClientRect = boundingClientRect;
|
|
3047
|
+
this.intersectionRect = intersectionRect;
|
|
3048
|
+
this.isIntersecting = isIntersecting;
|
|
3049
|
+
this.rootBounds = rootBounds;
|
|
3050
|
+
}
|
|
3051
|
+
};
|
|
3052
|
+
function isEntryEqual(first, second) {
|
|
3053
|
+
if (second == null) return false;
|
|
3054
|
+
return first.target === second.target && first.isIntersecting === second.isIntersecting && Rect.equals(first.boundingClientRect, second.boundingClientRect) && Rect.equals(first.intersectionRect, second.intersectionRect);
|
|
3055
|
+
}
|
|
3056
|
+
|
|
3057
|
+
// ../trace/src/track.ts
|
|
3058
|
+
var Tracker = class {
|
|
3059
|
+
constructor(callbacks) {
|
|
3060
|
+
this.callbacks = callbacks;
|
|
3061
|
+
this.buildRaf = 0;
|
|
3062
|
+
this.posRaf = 0;
|
|
3063
|
+
this.scheduleResize = () => {
|
|
3064
|
+
if (this.buildRaf) {
|
|
3065
|
+
return;
|
|
3066
|
+
}
|
|
3067
|
+
this.buildRaf = requestAnimationFrame(() => {
|
|
3068
|
+
this.buildRaf = 0;
|
|
3069
|
+
this.callbacks.onResize();
|
|
3070
|
+
});
|
|
3071
|
+
};
|
|
3072
|
+
this.scheduleScroll = () => {
|
|
3073
|
+
if (this.posRaf) {
|
|
3074
|
+
return;
|
|
3075
|
+
}
|
|
3076
|
+
this.posRaf = requestAnimationFrame(() => {
|
|
3077
|
+
this.posRaf = 0;
|
|
3078
|
+
this.callbacks.onScroll();
|
|
3079
|
+
});
|
|
3080
|
+
};
|
|
3081
|
+
this.onFocusIn = (event) => {
|
|
3082
|
+
const target = event.target;
|
|
3083
|
+
this.callbacks.onFocus(target instanceof Element ? target : null);
|
|
3084
|
+
};
|
|
3085
|
+
this.onFocusOut = () => {
|
|
3086
|
+
this.callbacks.onFocus(null);
|
|
3087
|
+
};
|
|
3088
|
+
this.positionObserver = new PositionObserver(
|
|
3089
|
+
(entries) => callbacks.onMoved(
|
|
3090
|
+
entries.map((entry) => ({ target: entry.target, rect: entry.boundingClientRect }))
|
|
3091
|
+
)
|
|
3092
|
+
);
|
|
3093
|
+
}
|
|
3094
|
+
/** Wire the global listeners. Call once, after the first draw. */
|
|
3095
|
+
listen() {
|
|
3096
|
+
window.addEventListener("resize", this.scheduleResize);
|
|
3097
|
+
window.addEventListener("scroll", this.scheduleScroll, {
|
|
3098
|
+
capture: true,
|
|
3099
|
+
passive: true
|
|
3100
|
+
});
|
|
3101
|
+
document.addEventListener("focusin", this.onFocusIn);
|
|
3102
|
+
document.addEventListener("focusout", this.onFocusOut);
|
|
3103
|
+
}
|
|
3104
|
+
/** Point the position observer at a fresh element set (after a rebuild). */
|
|
3105
|
+
observe(elements) {
|
|
3106
|
+
this.positionObserver.disconnect();
|
|
3107
|
+
for (const element of elements) {
|
|
3108
|
+
this.positionObserver.observe(element);
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
destroy() {
|
|
3112
|
+
if (this.buildRaf) {
|
|
3113
|
+
cancelAnimationFrame(this.buildRaf);
|
|
3114
|
+
}
|
|
3115
|
+
if (this.posRaf) {
|
|
3116
|
+
cancelAnimationFrame(this.posRaf);
|
|
3117
|
+
}
|
|
3118
|
+
this.positionObserver.disconnect();
|
|
3119
|
+
window.removeEventListener("resize", this.scheduleResize);
|
|
3120
|
+
window.removeEventListener("scroll", this.scheduleScroll, true);
|
|
3121
|
+
document.removeEventListener("focusin", this.onFocusIn);
|
|
3122
|
+
document.removeEventListener("focusout", this.onFocusOut);
|
|
3123
|
+
}
|
|
3124
|
+
};
|
|
3125
|
+
|
|
3126
|
+
// ../trace/src/mutations.ts
|
|
3127
|
+
var WATCHED_ATTRS = [
|
|
3128
|
+
"tabindex",
|
|
3129
|
+
"role",
|
|
3130
|
+
"disabled",
|
|
3131
|
+
"hidden",
|
|
3132
|
+
"inert",
|
|
3133
|
+
"contenteditable",
|
|
3134
|
+
"style",
|
|
3135
|
+
"class",
|
|
3136
|
+
"aria-hidden",
|
|
3137
|
+
"aria-disabled",
|
|
3138
|
+
"aria-label",
|
|
3139
|
+
"aria-labelledby",
|
|
3140
|
+
"title",
|
|
3141
|
+
"alt",
|
|
3142
|
+
"href",
|
|
3143
|
+
"type",
|
|
3144
|
+
"open"
|
|
3145
|
+
// <details> toggling shows/hides its (focusable) contents
|
|
3146
|
+
];
|
|
3147
|
+
var Mutations = class {
|
|
3148
|
+
constructor(ignore, onMutated) {
|
|
3149
|
+
this.ignore = ignore;
|
|
3150
|
+
this.onMutated = onMutated;
|
|
3151
|
+
this.raf = 0;
|
|
3152
|
+
this.observer = new MutationObserver((records) => {
|
|
3153
|
+
if (records.every((record) => this.ignore.contains(record.target))) {
|
|
3154
|
+
return;
|
|
3155
|
+
}
|
|
3156
|
+
this.schedule();
|
|
3157
|
+
});
|
|
3158
|
+
}
|
|
3159
|
+
observe(root) {
|
|
3160
|
+
const target = root.nodeType === 9 ? root.documentElement : root;
|
|
3161
|
+
this.observer.observe(target, {
|
|
3162
|
+
subtree: true,
|
|
3163
|
+
childList: true,
|
|
3164
|
+
attributes: true,
|
|
3165
|
+
attributeFilter: WATCHED_ATTRS,
|
|
3166
|
+
characterData: true
|
|
3167
|
+
// text changes affect accessible name
|
|
3168
|
+
});
|
|
3169
|
+
}
|
|
3170
|
+
destroy() {
|
|
3171
|
+
if (this.raf) {
|
|
3172
|
+
cancelAnimationFrame(this.raf);
|
|
3173
|
+
}
|
|
3174
|
+
this.observer.disconnect();
|
|
3175
|
+
}
|
|
3176
|
+
schedule() {
|
|
3177
|
+
if (this.raf) {
|
|
3178
|
+
return;
|
|
3179
|
+
}
|
|
3180
|
+
this.raf = requestAnimationFrame(() => {
|
|
3181
|
+
this.raf = 0;
|
|
3182
|
+
this.observer.takeRecords();
|
|
3183
|
+
this.onMutated();
|
|
3184
|
+
});
|
|
3185
|
+
}
|
|
3186
|
+
};
|
|
3187
|
+
|
|
3188
|
+
// ../trace/src/tip-content.ts
|
|
3189
|
+
function badgeTip(data) {
|
|
3190
|
+
const { num, selector, tabIndex, issues, name, role, description, autofocus, srOnly } = data;
|
|
3191
|
+
const idx = num !== null ? `<span class="ooo-tip-idx">${num}</span>` : `<span class="ooo-tip-idx ooo-tip-idx--off">\u2298</span>`;
|
|
3192
|
+
const fields = field("name", name ? escapeHtml(name) : null) + field("role", role ? mono(escapeHtml(role)) : null) + (description ? field("description", escapeHtml(description)) : "") + (tabIndex && tabIndex !== 0 ? field("tabindex", mono(String(tabIndex))) : "") + (autofocus ? field("autofocus", mono("yes")) : "") + (srOnly ? field("sr-only", mono("yes")) : "");
|
|
3193
|
+
const body = issues.length ? `<ul class="ooo-tip-list">${issues.map(issueItem).join("")}</ul>` : `<p class="ooo-tip-ok">No issues found.</p>`;
|
|
3194
|
+
return `<div class="ooo-tip-head">${idx}<code class="ooo-tip-sel">${escapeHtml(selector)}</code></div><dl class="ooo-tip-fields">${fields}</dl><div class="ooo-tip-body">${body}</div>`;
|
|
3195
|
+
}
|
|
3196
|
+
function issueItem(issue) {
|
|
3197
|
+
const cls = issue.ignored ? "ooo-tip-item ooo-tip-item--ignored" : "ooo-tip-item";
|
|
3198
|
+
const note = issue.ignored ? `<span class="ooo-tip-ignored">Ignored via <code>data-ooo-ignore</code></span>` : "";
|
|
3199
|
+
return `<li class="${cls}">${ruleLabel(issue)}<span class="ooo-tip-msg">${escapeHtml(stripSelectorPrefix(issue.message))}</span>${note}</li>`;
|
|
3200
|
+
}
|
|
3201
|
+
function segTip(back, from, toStop) {
|
|
3202
|
+
const flag = back ? `<span class="ooo-tip-flag ooo-tip-flag--back">\u21A9 reverse</span>` : `<span class="ooo-tip-flag">\u2192 forward</span>`;
|
|
3203
|
+
const message = back ? "Focus moves against the reading order \u2014 up, or right-to-left." : "Forward in reading order.";
|
|
3204
|
+
return `<div class="ooo-tip-head">${flag}<span class="ooo-tip-hop">#${from} \u2192 #${toStop}</span></div><div class="ooo-tip-body"><p class="ooo-tip-msg">${message}</p></div>`;
|
|
3205
|
+
}
|
|
3206
|
+
function field(key, value) {
|
|
3207
|
+
return `<dt>${key}</dt><dd>${value ?? `<span class="ooo-tip-dim">\u2014</span>`}</dd>`;
|
|
3208
|
+
}
|
|
3209
|
+
function mono(html) {
|
|
3210
|
+
return `<span class="ooo-tip-mono">${html}</span>`;
|
|
3211
|
+
}
|
|
3212
|
+
var EXTERNAL_ICON = `<svg class="ooo-tip-rule-ic" viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 13v5a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h5"/><path d="M15 4h5v5"/><path d="M20 4l-9 9"/></svg>`;
|
|
3213
|
+
function ruleLabel(issue) {
|
|
3214
|
+
const cls = issue.severity === "warning" ? "ooo-tip-rule ooo-tip-rule--warn" : "ooo-tip-rule";
|
|
3215
|
+
if (!issue.docs) {
|
|
3216
|
+
return `<span class="${cls}">${issue.rule}</span>`;
|
|
3217
|
+
}
|
|
3218
|
+
return `<a class="${cls}" href="${escapeHtml(issue.docs)}" target="_blank" rel="noreferrer" tabindex="-1"><span>${issue.rule}</span>${EXTERNAL_ICON}</a>`;
|
|
3219
|
+
}
|
|
3220
|
+
function stripSelectorPrefix(message) {
|
|
3221
|
+
return message.replace(/^"[^"]*"\s*/, "");
|
|
3222
|
+
}
|
|
3223
|
+
var HTML_ESCAPES = {
|
|
3224
|
+
"&": "&",
|
|
3225
|
+
"<": "<",
|
|
3226
|
+
">": ">",
|
|
3227
|
+
'"': """
|
|
3228
|
+
};
|
|
3229
|
+
function escapeHtml(str) {
|
|
3230
|
+
return str.replace(/[&<>"]/g, (char) => HTML_ESCAPES[char] ?? char);
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3233
|
+
// ../trace/src/controls.ts
|
|
3234
|
+
var COPY_FORMATS = [
|
|
3235
|
+
{ value: "by-element", label: "By element" },
|
|
3236
|
+
{ value: "text", label: "Text" }
|
|
3237
|
+
];
|
|
3238
|
+
var PEEK_KEY_LABEL = {
|
|
3239
|
+
Alt: "Alt",
|
|
3240
|
+
Control: "Ctrl",
|
|
3241
|
+
Shift: "Shift",
|
|
3242
|
+
Meta: "Meta"
|
|
3243
|
+
};
|
|
3244
|
+
var PANEL_STATE_KEY = "ooo:trace";
|
|
3245
|
+
function loadPanelState() {
|
|
3246
|
+
try {
|
|
3247
|
+
return JSON.parse(sessionStorage.getItem(PANEL_STATE_KEY) ?? "{}");
|
|
3248
|
+
} catch {
|
|
3249
|
+
return {};
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
function patchPanelState(patch) {
|
|
3253
|
+
try {
|
|
3254
|
+
sessionStorage.setItem(PANEL_STATE_KEY, JSON.stringify({ ...loadPanelState(), ...patch }));
|
|
3255
|
+
} catch {
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
function setupControls(layer, opts) {
|
|
3259
|
+
const { peekKey, open, copyFormat, onToggleVisible, onTogglePeek, onToggleOpen, getReport } = opts;
|
|
3260
|
+
const abort = new AbortController();
|
|
3261
|
+
const signal = abort.signal;
|
|
3262
|
+
const panel = document.createElement("div");
|
|
3263
|
+
panel.className = "ooo-panel";
|
|
3264
|
+
panel.dataset.open = open ? "1" : "0";
|
|
3265
|
+
const title = buildTitle(panel, signal, onToggleOpen);
|
|
3266
|
+
const { body, visSwitch, peekSwitch } = buildBody(
|
|
3267
|
+
peekKey,
|
|
3268
|
+
signal,
|
|
3269
|
+
onToggleVisible,
|
|
3270
|
+
onTogglePeek,
|
|
3271
|
+
getReport,
|
|
3272
|
+
copyFormat
|
|
3273
|
+
);
|
|
3274
|
+
panel.append(title, body);
|
|
3275
|
+
layer.appendChild(panel);
|
|
3276
|
+
listenForPeekKey(peekKey, signal, onTogglePeek);
|
|
3277
|
+
return {
|
|
3278
|
+
syncVisible: (shown) => {
|
|
3279
|
+
setSwitch(visSwitch, shown);
|
|
3280
|
+
peekSwitch.disabled = !shown;
|
|
3281
|
+
},
|
|
3282
|
+
syncPeek: (on) => setSwitch(peekSwitch, on),
|
|
3283
|
+
teardown: () => {
|
|
3284
|
+
abort.abort();
|
|
3285
|
+
panel.remove();
|
|
3286
|
+
}
|
|
3287
|
+
};
|
|
3288
|
+
}
|
|
3289
|
+
function buildTitle(panel, signal, onToggleOpen) {
|
|
3290
|
+
const title = document.createElement("button");
|
|
3291
|
+
title.type = "button";
|
|
3292
|
+
title.tabIndex = -1;
|
|
3293
|
+
title.className = "ooo-panel-title";
|
|
3294
|
+
title.textContent = "Out of Order";
|
|
3295
|
+
title.addEventListener("mousedown", (event) => event.preventDefault(), {
|
|
3296
|
+
signal
|
|
3297
|
+
});
|
|
3298
|
+
title.addEventListener(
|
|
3299
|
+
"click",
|
|
3300
|
+
() => {
|
|
3301
|
+
const next = panel.dataset.open !== "1";
|
|
3302
|
+
panel.dataset.open = next ? "1" : "0";
|
|
3303
|
+
onToggleOpen(next);
|
|
3304
|
+
},
|
|
3305
|
+
{ signal }
|
|
3306
|
+
);
|
|
3307
|
+
return title;
|
|
3308
|
+
}
|
|
3309
|
+
function buildBody(peekKey, signal, onToggleVisible, onTogglePeek, getReport, copyFormat) {
|
|
3310
|
+
const body = document.createElement("div");
|
|
3311
|
+
body.className = "ooo-panel-body";
|
|
3312
|
+
const visSwitch = addSwitch(body, "vis", "Overlay", onToggleVisible, signal);
|
|
3313
|
+
const peekSwitch = addSwitch(body, "peek", "Peek", onTogglePeek, signal);
|
|
3314
|
+
setSwitch(visSwitch, true);
|
|
3315
|
+
setSwitch(peekSwitch, false);
|
|
3316
|
+
const hint = document.createElement("p");
|
|
3317
|
+
hint.className = "ooo-panel-hint";
|
|
3318
|
+
hint.textContent = `tap ${PEEK_KEY_LABEL[peekKey]} to peek`;
|
|
3319
|
+
body.appendChild(hint);
|
|
3320
|
+
addCopyButton(body, getReport, copyFormat, signal);
|
|
3321
|
+
return { body, visSwitch, peekSwitch };
|
|
3322
|
+
}
|
|
3323
|
+
function addCopyButton(parent, getReport, copyFormat, signal) {
|
|
3324
|
+
let current = copyFormat;
|
|
3325
|
+
const wrap = document.createElement("div");
|
|
3326
|
+
wrap.className = "ooo-copy-split";
|
|
3327
|
+
const main = document.createElement("button");
|
|
3328
|
+
main.type = "button";
|
|
3329
|
+
main.tabIndex = -1;
|
|
3330
|
+
main.className = "ooo-copy";
|
|
3331
|
+
const caret = document.createElement("button");
|
|
3332
|
+
caret.type = "button";
|
|
3333
|
+
caret.tabIndex = -1;
|
|
3334
|
+
caret.className = "ooo-copy-caret";
|
|
3335
|
+
caret.textContent = "\u25BE";
|
|
3336
|
+
caret.setAttribute("aria-haspopup", "menu");
|
|
3337
|
+
caret.setAttribute("aria-expanded", "false");
|
|
3338
|
+
caret.setAttribute("aria-label", "Choose copy format");
|
|
3339
|
+
const menu = document.createElement("div");
|
|
3340
|
+
menu.className = "ooo-copy-menu";
|
|
3341
|
+
menu.setAttribute("role", "menu");
|
|
3342
|
+
menu.hidden = true;
|
|
3343
|
+
const labelFor = (value) => COPY_FORMATS.find((format) => format.value === value)?.label ?? value;
|
|
3344
|
+
const mainLabel = () => `Copy ${labelFor(current).toLowerCase()}`;
|
|
3345
|
+
let revert;
|
|
3346
|
+
const flash = (text) => {
|
|
3347
|
+
main.textContent = text;
|
|
3348
|
+
clearTimeout(revert);
|
|
3349
|
+
revert = setTimeout(() => main.textContent = mainLabel(), 1200);
|
|
3350
|
+
};
|
|
3351
|
+
signal.addEventListener("abort", () => clearTimeout(revert));
|
|
3352
|
+
const copy = () => {
|
|
3353
|
+
void navigator.clipboard.writeText(getReport(current)).then(
|
|
3354
|
+
() => flash("Copied"),
|
|
3355
|
+
() => flash("Copy failed")
|
|
3356
|
+
);
|
|
3357
|
+
};
|
|
3358
|
+
const closeMenu = () => {
|
|
3359
|
+
menu.hidden = true;
|
|
3360
|
+
caret.setAttribute("aria-expanded", "false");
|
|
3361
|
+
};
|
|
3362
|
+
const items = /* @__PURE__ */ new Map();
|
|
3363
|
+
const syncSelection = () => {
|
|
3364
|
+
main.textContent = mainLabel();
|
|
3365
|
+
for (const [value, el] of items) {
|
|
3366
|
+
el.classList.toggle("ooo-copy-item--on", value === current);
|
|
3367
|
+
el.setAttribute("aria-checked", String(value === current));
|
|
3368
|
+
}
|
|
3369
|
+
};
|
|
3370
|
+
for (const format of COPY_FORMATS) {
|
|
3371
|
+
const item = document.createElement("button");
|
|
3372
|
+
item.type = "button";
|
|
3373
|
+
item.tabIndex = -1;
|
|
3374
|
+
item.className = "ooo-copy-item";
|
|
3375
|
+
item.setAttribute("role", "menuitemradio");
|
|
3376
|
+
item.textContent = format.label;
|
|
3377
|
+
item.addEventListener("mousedown", (event) => event.preventDefault(), {
|
|
3378
|
+
signal
|
|
3379
|
+
});
|
|
3380
|
+
item.addEventListener(
|
|
3381
|
+
"click",
|
|
3382
|
+
() => {
|
|
3383
|
+
current = format.value;
|
|
3384
|
+
syncSelection();
|
|
3385
|
+
patchPanelState({ copyFormat: current });
|
|
3386
|
+
closeMenu();
|
|
3387
|
+
},
|
|
3388
|
+
{ signal }
|
|
3389
|
+
);
|
|
3390
|
+
items.set(format.value, item);
|
|
3391
|
+
menu.appendChild(item);
|
|
3392
|
+
}
|
|
3393
|
+
syncSelection();
|
|
3394
|
+
for (const btn of [main, caret]) {
|
|
3395
|
+
btn.addEventListener("mousedown", (event) => event.preventDefault(), {
|
|
3396
|
+
signal
|
|
3397
|
+
});
|
|
3398
|
+
}
|
|
3399
|
+
main.addEventListener("click", copy, { signal });
|
|
3400
|
+
caret.addEventListener(
|
|
3401
|
+
"click",
|
|
3402
|
+
() => {
|
|
3403
|
+
const open = menu.hidden;
|
|
3404
|
+
menu.hidden = !open;
|
|
3405
|
+
caret.setAttribute("aria-expanded", String(open));
|
|
3406
|
+
},
|
|
3407
|
+
{ signal }
|
|
3408
|
+
);
|
|
3409
|
+
document.addEventListener(
|
|
3410
|
+
"pointerdown",
|
|
3411
|
+
(event) => {
|
|
3412
|
+
if (!wrap.contains(event.target)) {
|
|
3413
|
+
closeMenu();
|
|
3414
|
+
}
|
|
3415
|
+
},
|
|
3416
|
+
{ signal }
|
|
3417
|
+
);
|
|
3418
|
+
wrap.append(main, caret, menu);
|
|
3419
|
+
parent.appendChild(wrap);
|
|
3420
|
+
}
|
|
3421
|
+
function addSwitch(parent, name, text, onToggle, signal) {
|
|
3422
|
+
const row = document.createElement("div");
|
|
3423
|
+
row.className = "ooo-row";
|
|
3424
|
+
const lbl = document.createElement("span");
|
|
3425
|
+
lbl.className = "ooo-row-label";
|
|
3426
|
+
lbl.textContent = text;
|
|
3427
|
+
const sw = document.createElement("button");
|
|
3428
|
+
sw.type = "button";
|
|
3429
|
+
sw.tabIndex = -1;
|
|
3430
|
+
sw.className = `ooo-switch ooo-switch--${name}`;
|
|
3431
|
+
sw.setAttribute("role", "switch");
|
|
3432
|
+
sw.innerHTML = `<span class="ooo-switch-knob"></span>`;
|
|
3433
|
+
sw.addEventListener("mousedown", (event) => event.preventDefault(), {
|
|
3434
|
+
signal
|
|
3435
|
+
});
|
|
3436
|
+
sw.addEventListener("click", onToggle, { signal });
|
|
3437
|
+
row.append(lbl, sw);
|
|
3438
|
+
parent.appendChild(row);
|
|
3439
|
+
return sw;
|
|
3440
|
+
}
|
|
3441
|
+
function setSwitch(sw, on) {
|
|
3442
|
+
sw.setAttribute("aria-checked", String(on));
|
|
3443
|
+
sw.classList.toggle("ooo-switch--on", on);
|
|
3444
|
+
}
|
|
3445
|
+
function listenForPeekKey(peekKey, signal, onTap) {
|
|
3446
|
+
let armed = false;
|
|
3447
|
+
window.addEventListener(
|
|
3448
|
+
"keydown",
|
|
3449
|
+
(event) => {
|
|
3450
|
+
if (event.key !== peekKey) {
|
|
3451
|
+
armed = false;
|
|
3452
|
+
} else if (!event.repeat) {
|
|
3453
|
+
armed = true;
|
|
3454
|
+
}
|
|
3455
|
+
},
|
|
3456
|
+
{ signal }
|
|
3457
|
+
);
|
|
3458
|
+
window.addEventListener(
|
|
3459
|
+
"keyup",
|
|
3460
|
+
(event) => {
|
|
3461
|
+
if (event.key !== peekKey || !armed) {
|
|
3462
|
+
return;
|
|
3463
|
+
}
|
|
3464
|
+
armed = false;
|
|
3465
|
+
onTap();
|
|
3466
|
+
},
|
|
3467
|
+
{ signal }
|
|
3468
|
+
);
|
|
3469
|
+
window.addEventListener("pointerdown", () => armed = false, { signal });
|
|
3470
|
+
window.addEventListener("blur", () => armed = false, { signal });
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3473
|
+
// ../trace/src/index.ts
|
|
3474
|
+
function trace(options = {}) {
|
|
3475
|
+
ensureStyles();
|
|
3476
|
+
const layer = document.createElement("div");
|
|
3477
|
+
layer.className = "ooo-layer";
|
|
3478
|
+
layer.dataset.oooPeek = "off";
|
|
3479
|
+
layer.setAttribute("data-ooo-overlay", "");
|
|
3480
|
+
document.body.appendChild(layer);
|
|
3481
|
+
const motion = options.motion ?? "auto";
|
|
3482
|
+
const reduceQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
3483
|
+
const applyMotion = () => {
|
|
3484
|
+
const animate = motion === "on" || motion === "auto" && !reduceQuery.matches;
|
|
3485
|
+
layer.dataset.oooMotion = animate ? "play" : "still";
|
|
3486
|
+
};
|
|
3487
|
+
applyMotion();
|
|
3488
|
+
if (motion === "auto") {
|
|
3489
|
+
reduceQuery.addEventListener("change", applyMotion);
|
|
3490
|
+
}
|
|
3491
|
+
const tooltip = new Tooltip(layer);
|
|
3492
|
+
const renderer = new Renderer(layer, tooltip);
|
|
3493
|
+
const tracker = new Tracker({
|
|
3494
|
+
onMoved: (moved) => renderer.applyMoved(moved),
|
|
3495
|
+
onScroll: () => renderer.seed(),
|
|
3496
|
+
onResize: () => build(),
|
|
3497
|
+
onFocus: (element) => renderer.setFocused(element)
|
|
3498
|
+
});
|
|
3499
|
+
const mutations = new Mutations(layer, () => build());
|
|
3500
|
+
let lastSig = null;
|
|
3501
|
+
const saved = loadPanelState();
|
|
3502
|
+
let visible = true;
|
|
3503
|
+
let peeking = false;
|
|
3504
|
+
const setVisible = (next) => {
|
|
3505
|
+
visible = next;
|
|
3506
|
+
layer.classList.toggle("ooo-hidden", !next);
|
|
3507
|
+
renderer.setRingsVisible(next);
|
|
3508
|
+
if (!next) {
|
|
3509
|
+
tooltip.hide();
|
|
3510
|
+
}
|
|
3511
|
+
controls.syncVisible(next);
|
|
3512
|
+
patchPanelState({ visible: next });
|
|
3513
|
+
};
|
|
3514
|
+
const setPeek = (next) => {
|
|
3515
|
+
peeking = next;
|
|
3516
|
+
layer.dataset.oooPeek = next ? "on" : "off";
|
|
3517
|
+
if (next) {
|
|
3518
|
+
tooltip.hide();
|
|
3519
|
+
}
|
|
3520
|
+
controls.syncPeek(next);
|
|
3521
|
+
patchPanelState({ peek: next });
|
|
3522
|
+
};
|
|
3523
|
+
const controls = setupControls(layer, {
|
|
3524
|
+
peekKey: options.peekKey ?? "Alt",
|
|
3525
|
+
open: saved.open ?? true,
|
|
3526
|
+
copyFormat: saved.copyFormat ?? "text",
|
|
3527
|
+
onToggleVisible: () => setVisible(!visible),
|
|
3528
|
+
// Peek does nothing with the overlay hidden, so ignore the toggle then.
|
|
3529
|
+
onTogglePeek: () => visible && setPeek(!peeking),
|
|
3530
|
+
onToggleOpen: (open) => patchPanelState({ open }),
|
|
3531
|
+
getReport: (format) => {
|
|
3532
|
+
const violations = audit(options.root ?? document, {
|
|
3533
|
+
...options.audit,
|
|
3534
|
+
format
|
|
3535
|
+
}).violations;
|
|
3536
|
+
return typeof violations === "string" ? violations : JSON.stringify(violations, null, 2);
|
|
3537
|
+
}
|
|
3538
|
+
});
|
|
3539
|
+
if (saved.visible === false) {
|
|
3540
|
+
setVisible(false);
|
|
3541
|
+
}
|
|
3542
|
+
if (saved.peek === true && visible) {
|
|
3543
|
+
setPeek(true);
|
|
3544
|
+
}
|
|
3545
|
+
const handle = {
|
|
3546
|
+
result: null,
|
|
3547
|
+
get visible() {
|
|
3548
|
+
return visible;
|
|
3549
|
+
},
|
|
3550
|
+
setVisible,
|
|
3551
|
+
toggle: () => setVisible(!visible),
|
|
3552
|
+
destroy: () => {
|
|
3553
|
+
if (motion === "auto") {
|
|
3554
|
+
reduceQuery.removeEventListener("change", applyMotion);
|
|
3555
|
+
}
|
|
3556
|
+
controls.teardown();
|
|
3557
|
+
tracker.destroy();
|
|
3558
|
+
mutations.destroy();
|
|
3559
|
+
tooltip.destroy();
|
|
3560
|
+
renderer.clear();
|
|
3561
|
+
layer.remove();
|
|
3562
|
+
}
|
|
3563
|
+
};
|
|
3564
|
+
function build() {
|
|
3565
|
+
const result = audit(options.root ?? document, options.audit, options.rules);
|
|
3566
|
+
handle.result = result;
|
|
3567
|
+
const sig = resultSignature(result);
|
|
3568
|
+
if (sig === lastSig) {
|
|
3569
|
+
return;
|
|
3570
|
+
}
|
|
3571
|
+
lastSig = sig;
|
|
3572
|
+
const sequence = result.sequence;
|
|
3573
|
+
const inSeq = new Set(sequence.map((entry) => entry.element));
|
|
3574
|
+
const issuesByElement = /* @__PURE__ */ new Map();
|
|
3575
|
+
const indexBy = (element, issue) => {
|
|
3576
|
+
const list = issuesByElement.get(element) ?? [];
|
|
3577
|
+
list.push(issue);
|
|
3578
|
+
issuesByElement.set(element, list);
|
|
3579
|
+
};
|
|
3580
|
+
for (const violation of result.violations) {
|
|
3581
|
+
for (const issue of violation.issues) {
|
|
3582
|
+
indexBy(violation.element, issue);
|
|
3583
|
+
for (const related2 of issue.relatedElements ?? []) {
|
|
3584
|
+
indexBy(related2, issue);
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
const stops = sequence.map(
|
|
3589
|
+
(entry, idx) => makeStop(
|
|
3590
|
+
entry.element,
|
|
3591
|
+
idx + 1,
|
|
3592
|
+
entry.selector,
|
|
3593
|
+
entry.tabIndex,
|
|
3594
|
+
issuesByElement.get(entry.element) ?? [],
|
|
3595
|
+
true
|
|
3596
|
+
)
|
|
3597
|
+
);
|
|
3598
|
+
const segs = [];
|
|
3599
|
+
for (let idx = 0; idx < sequence.length - 1; idx++) {
|
|
3600
|
+
const back = (issuesByElement.get(sequence[idx + 1].element) ?? []).some(
|
|
3601
|
+
(issue) => issue.rule === "visual-order-mismatch" && !issue.ignored
|
|
3602
|
+
);
|
|
3603
|
+
segs.push({ back, tip: () => segTip(back, idx + 1, idx + 2) });
|
|
3604
|
+
}
|
|
3605
|
+
const offStops = [];
|
|
3606
|
+
for (const [element, issues] of issuesByElement) {
|
|
3607
|
+
if (inSeq.has(element)) {
|
|
3608
|
+
continue;
|
|
3609
|
+
}
|
|
3610
|
+
offStops.push(makeStop(element, null, selectorFor(element), null, issues, false));
|
|
3611
|
+
}
|
|
3612
|
+
renderer.draw(stops, segs, offStops);
|
|
3613
|
+
renderer.seed();
|
|
3614
|
+
tracker.observe(renderer.elementsToObserve());
|
|
3615
|
+
renderer.setFocused(document.activeElement);
|
|
3616
|
+
}
|
|
3617
|
+
build();
|
|
3618
|
+
tracker.listen();
|
|
3619
|
+
mutations.observe(options.root ?? document);
|
|
3620
|
+
return handle;
|
|
3621
|
+
}
|
|
3622
|
+
var elementIds = /* @__PURE__ */ new WeakMap();
|
|
3623
|
+
var nextElementId = 1;
|
|
3624
|
+
function elementId(element) {
|
|
3625
|
+
let id = elementIds.get(element);
|
|
3626
|
+
if (id === void 0) {
|
|
3627
|
+
id = nextElementId++;
|
|
3628
|
+
elementIds.set(element, id);
|
|
3629
|
+
}
|
|
3630
|
+
return id;
|
|
3631
|
+
}
|
|
3632
|
+
function resultSignature(result) {
|
|
3633
|
+
const order = result.sequence.map((entry) => elementId(entry.element)).join(">");
|
|
3634
|
+
const vios = result.violations.flatMap(
|
|
3635
|
+
(violation) => violation.issues.map(
|
|
3636
|
+
(issue) => `${elementId(violation.element)}:${issue.rule}${issue.ignored ? "!" : ""}`
|
|
3637
|
+
)
|
|
3638
|
+
).sort().join("|");
|
|
3639
|
+
return `${order}#${vios}`;
|
|
3640
|
+
}
|
|
3641
|
+
function makeStop(element, num, selector, tabIndex, issues, inSeq) {
|
|
3642
|
+
const label = num !== null ? String(num) : "\u2298";
|
|
3643
|
+
const autofocus = inSeq && element.hasAttribute("autofocus");
|
|
3644
|
+
return {
|
|
3645
|
+
element,
|
|
3646
|
+
label,
|
|
3647
|
+
severity: worstSeverity(issues),
|
|
3648
|
+
inSeq,
|
|
3649
|
+
autofocus,
|
|
3650
|
+
tip: () => badgeTip({
|
|
3651
|
+
num,
|
|
3652
|
+
selector,
|
|
3653
|
+
tabIndex,
|
|
3654
|
+
issues,
|
|
3655
|
+
name: computeAccessibleName(element).trim(),
|
|
3656
|
+
role: getRole(element) ?? "",
|
|
3657
|
+
description: computeAccessibleDescription(element).trim(),
|
|
3658
|
+
autofocus,
|
|
3659
|
+
srOnly: isScreenReaderOnly(element)
|
|
3660
|
+
})
|
|
3661
|
+
};
|
|
3662
|
+
}
|
|
3663
|
+
function worstSeverity(issues) {
|
|
3664
|
+
const live = issues.filter((issue) => !issue.ignored);
|
|
3665
|
+
if (live.some((issue) => issue.severity === "error")) {
|
|
3666
|
+
return "error";
|
|
3667
|
+
}
|
|
3668
|
+
return live.length ? "warning" : null;
|
|
3669
|
+
}
|
|
3670
|
+
|
|
3671
|
+
// src/inject-overlay.ts
|
|
3672
|
+
var mount = () => void trace();
|
|
3673
|
+
if (document.readyState === "complete") {
|
|
3674
|
+
mount();
|
|
3675
|
+
} else {
|
|
3676
|
+
window.addEventListener("load", mount, { once: true });
|
|
3677
|
+
}
|
|
3678
|
+
})();
|
|
3679
|
+
/*! Bundled license information:
|
|
3680
|
+
|
|
3681
|
+
tabbable/dist/index.esm.js:
|
|
3682
|
+
(*!
|
|
3683
|
+
* tabbable 6.5.0
|
|
3684
|
+
* @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE
|
|
3685
|
+
*)
|
|
3686
|
+
*/
|