wicg-inert 3.0.1 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/dist/inert.esm.js +832 -0
- package/dist/inert.js +682 -675
- package/dist/inert.min.js +1 -1
- package/dist/inert.min.js.map +1 -1
- package/package.json +2 -2
- package/rollup.config.js +12 -0
- package/src/inert.js +627 -620
- package/w3c.json +1 -1
package/dist/inert.js
CHANGED
@@ -13,821 +13,828 @@
|
|
13
13
|
* (http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document).
|
14
14
|
*/
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
* IE has a non-standard name for "matches".
|
22
|
-
* @type {typeof Element.prototype.matches}
|
23
|
-
*/
|
24
|
-
var matches = Element.prototype.matches || Element.prototype.msMatchesSelector;
|
25
|
-
|
26
|
-
/** @type {string} */
|
27
|
-
var _focusableElementsString = ['a[href]', 'area[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', 'details', 'summary', 'iframe', 'object', 'embed', '[contenteditable]'].join(',');
|
16
|
+
(function () {
|
17
|
+
// Return early if we're not running inside of the browser.
|
18
|
+
if (typeof window === 'undefined') {
|
19
|
+
return;
|
20
|
+
}
|
28
21
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
*
|
33
|
-
* Its main functions are:
|
34
|
-
*
|
35
|
-
* - to create and maintain a set of managed `InertNode`s, including when mutations occur in the
|
36
|
-
* subtree. The `makeSubtreeUnfocusable()` method handles collecting `InertNode`s via registering
|
37
|
-
* each focusable node in the subtree with the singleton `InertManager` which manages all known
|
38
|
-
* focusable nodes within inert subtrees. `InertManager` ensures that a single `InertNode`
|
39
|
-
* instance exists for each focusable node which has at least one inert root as an ancestor.
|
40
|
-
*
|
41
|
-
* - to notify all managed `InertNode`s when this subtree stops being inert (i.e. when the `inert`
|
42
|
-
* attribute is removed from the root node). This is handled in the destructor, which calls the
|
43
|
-
* `deregister` method on `InertManager` for each managed inert node.
|
44
|
-
*/
|
22
|
+
// Convenience function for converting NodeLists.
|
23
|
+
/** @type {typeof Array.prototype.slice} */
|
24
|
+
var slice = Array.prototype.slice;
|
45
25
|
|
46
|
-
var InertRoot = function () {
|
47
26
|
/**
|
48
|
-
*
|
49
|
-
* @
|
27
|
+
* IE has a non-standard name for "matches".
|
28
|
+
* @type {typeof Element.prototype.matches}
|
50
29
|
*/
|
51
|
-
|
52
|
-
_classCallCheck(this, InertRoot);
|
30
|
+
var matches = Element.prototype.matches || Element.prototype.msMatchesSelector;
|
53
31
|
|
54
|
-
|
55
|
-
|
32
|
+
/** @type {string} */
|
33
|
+
var _focusableElementsString = ['a[href]', 'area[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', 'details', 'summary', 'iframe', 'object', 'embed', '[contenteditable]'].join(',');
|
56
34
|
|
57
|
-
|
58
|
-
|
35
|
+
/**
|
36
|
+
* `InertRoot` manages a single inert subtree, i.e. a DOM subtree whose root element has an `inert`
|
37
|
+
* attribute.
|
38
|
+
*
|
39
|
+
* Its main functions are:
|
40
|
+
*
|
41
|
+
* - to create and maintain a set of managed `InertNode`s, including when mutations occur in the
|
42
|
+
* subtree. The `makeSubtreeUnfocusable()` method handles collecting `InertNode`s via registering
|
43
|
+
* each focusable node in the subtree with the singleton `InertManager` which manages all known
|
44
|
+
* focusable nodes within inert subtrees. `InertManager` ensures that a single `InertNode`
|
45
|
+
* instance exists for each focusable node which has at least one inert root as an ancestor.
|
46
|
+
*
|
47
|
+
* - to notify all managed `InertNode`s when this subtree stops being inert (i.e. when the `inert`
|
48
|
+
* attribute is removed from the root node). This is handled in the destructor, which calls the
|
49
|
+
* `deregister` method on `InertManager` for each managed inert node.
|
50
|
+
*/
|
59
51
|
|
52
|
+
var InertRoot = function () {
|
60
53
|
/**
|
61
|
-
* @
|
62
|
-
*
|
54
|
+
* @param {!Element} rootElement The Element at the root of the inert subtree.
|
55
|
+
* @param {!InertManager} inertManager The global singleton InertManager object.
|
63
56
|
*/
|
64
|
-
|
65
|
-
|
66
|
-
// Make the subtree hidden from assistive technology
|
67
|
-
if (this._rootElement.hasAttribute('aria-hidden')) {
|
68
|
-
/** @type {?string} */
|
69
|
-
this._savedAriaHidden = this._rootElement.getAttribute('aria-hidden');
|
70
|
-
} else {
|
71
|
-
this._savedAriaHidden = null;
|
72
|
-
}
|
73
|
-
this._rootElement.setAttribute('aria-hidden', 'true');
|
74
|
-
|
75
|
-
// Make all focusable elements in the subtree unfocusable and add them to _managedNodes
|
76
|
-
this._makeSubtreeUnfocusable(this._rootElement);
|
77
|
-
|
78
|
-
// Watch for:
|
79
|
-
// - any additions in the subtree: make them unfocusable too
|
80
|
-
// - any removals from the subtree: remove them from this inert root's managed nodes
|
81
|
-
// - attribute changes: if `tabindex` is added, or removed from an intrinsically focusable
|
82
|
-
// element, make that node a managed node.
|
83
|
-
this._observer = new MutationObserver(this._onMutation.bind(this));
|
84
|
-
this._observer.observe(this._rootElement, { attributes: true, childList: true, subtree: true });
|
85
|
-
}
|
57
|
+
function InertRoot(rootElement, inertManager) {
|
58
|
+
_classCallCheck(this, InertRoot);
|
86
59
|
|
87
|
-
|
88
|
-
|
89
|
-
* stored in this object and updates the state of all of the managed nodes.
|
90
|
-
*/
|
60
|
+
/** @type {!InertManager} */
|
61
|
+
this._inertManager = inertManager;
|
91
62
|
|
63
|
+
/** @type {!Element} */
|
64
|
+
this._rootElement = rootElement;
|
92
65
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
66
|
+
/**
|
67
|
+
* @type {!Set<!InertNode>}
|
68
|
+
* All managed focusable nodes in this InertRoot's subtree.
|
69
|
+
*/
|
70
|
+
this._managedNodes = new Set();
|
97
71
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
72
|
+
// Make the subtree hidden from assistive technology
|
73
|
+
if (this._rootElement.hasAttribute('aria-hidden')) {
|
74
|
+
/** @type {?string} */
|
75
|
+
this._savedAriaHidden = this._rootElement.getAttribute('aria-hidden');
|
76
|
+
} else {
|
77
|
+
this._savedAriaHidden = null;
|
104
78
|
}
|
79
|
+
this._rootElement.setAttribute('aria-hidden', 'true');
|
80
|
+
|
81
|
+
// Make all focusable elements in the subtree unfocusable and add them to _managedNodes
|
82
|
+
this._makeSubtreeUnfocusable(this._rootElement);
|
105
83
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
//
|
111
|
-
|
112
|
-
|
113
|
-
// instance has been destroyed and a method is called.
|
114
|
-
// 2) We don't want to cast "this", because we want type-aware optimizations
|
115
|
-
// to know which properties we're setting.
|
116
|
-
this._observer = /** @type {?} */null;
|
117
|
-
this._rootElement = /** @type {?} */null;
|
118
|
-
this._managedNodes = /** @type {?} */null;
|
119
|
-
this._inertManager = /** @type {?} */null;
|
84
|
+
// Watch for:
|
85
|
+
// - any additions in the subtree: make them unfocusable too
|
86
|
+
// - any removals from the subtree: remove them from this inert root's managed nodes
|
87
|
+
// - attribute changes: if `tabindex` is added, or removed from an intrinsically focusable
|
88
|
+
// element, make that node a managed node.
|
89
|
+
this._observer = new MutationObserver(this._onMutation.bind(this));
|
90
|
+
this._observer.observe(this._rootElement, { attributes: true, childList: true, subtree: true });
|
120
91
|
}
|
121
92
|
|
122
93
|
/**
|
123
|
-
*
|
94
|
+
* Call this whenever this object is about to become obsolete. This unwinds all of the state
|
95
|
+
* stored in this object and updates the state of all of the managed nodes.
|
124
96
|
*/
|
125
97
|
|
126
|
-
}, {
|
127
|
-
key: '_makeSubtreeUnfocusable',
|
128
98
|
|
99
|
+
_createClass(InertRoot, [{
|
100
|
+
key: 'destructor',
|
101
|
+
value: function destructor() {
|
102
|
+
this._observer.disconnect();
|
129
103
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
composedTreeWalk(startNode, function (node) {
|
137
|
-
return _this2._visitNode(node);
|
138
|
-
});
|
139
|
-
|
140
|
-
var activeElement = document.activeElement;
|
141
|
-
|
142
|
-
if (!document.body.contains(startNode)) {
|
143
|
-
// startNode may be in shadow DOM, so find its nearest shadowRoot to get the activeElement.
|
144
|
-
var node = startNode;
|
145
|
-
/** @type {!ShadowRoot|undefined} */
|
146
|
-
var root = undefined;
|
147
|
-
while (node) {
|
148
|
-
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
149
|
-
root = /** @type {!ShadowRoot} */node;
|
150
|
-
break;
|
104
|
+
if (this._rootElement) {
|
105
|
+
if (this._savedAriaHidden !== null) {
|
106
|
+
this._rootElement.setAttribute('aria-hidden', this._savedAriaHidden);
|
107
|
+
} else {
|
108
|
+
this._rootElement.removeAttribute('aria-hidden');
|
151
109
|
}
|
152
|
-
node = node.parentNode;
|
153
|
-
}
|
154
|
-
if (root) {
|
155
|
-
activeElement = root.activeElement;
|
156
110
|
}
|
157
|
-
}
|
158
|
-
if (startNode.contains(activeElement)) {
|
159
|
-
activeElement.blur();
|
160
|
-
// In IE11, if an element is already focused, and then set to tabindex=-1
|
161
|
-
// calling blur() will not actually move the focus.
|
162
|
-
// To work around this we call focus() on the body instead.
|
163
|
-
if (activeElement === document.activeElement) {
|
164
|
-
document.body.focus();
|
165
|
-
}
|
166
|
-
}
|
167
|
-
}
|
168
|
-
|
169
|
-
/**
|
170
|
-
* @param {!Node} node
|
171
|
-
*/
|
172
111
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
112
|
+
this._managedNodes.forEach(function (inertNode) {
|
113
|
+
this._unmanageNode(inertNode.node);
|
114
|
+
}, this);
|
115
|
+
|
116
|
+
// Note we cast the nulls to the ANY type here because:
|
117
|
+
// 1) We want the class properties to be declared as non-null, or else we
|
118
|
+
// need even more casts throughout this code. All bets are off if an
|
119
|
+
// instance has been destroyed and a method is called.
|
120
|
+
// 2) We don't want to cast "this", because we want type-aware optimizations
|
121
|
+
// to know which properties we're setting.
|
122
|
+
this._observer = /** @type {?} */null;
|
123
|
+
this._rootElement = /** @type {?} */null;
|
124
|
+
this._managedNodes = /** @type {?} */null;
|
125
|
+
this._inertManager = /** @type {?} */null;
|
178
126
|
}
|
179
|
-
var element = /** @type {!Element} */node;
|
180
127
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
this._adoptInertRoot(element);
|
185
|
-
}
|
128
|
+
/**
|
129
|
+
* @return {!Set<!InertNode>} A copy of this InertRoot's managed nodes set.
|
130
|
+
*/
|
186
131
|
|
187
|
-
|
188
|
-
|
189
|
-
}
|
190
|
-
}
|
132
|
+
}, {
|
133
|
+
key: '_makeSubtreeUnfocusable',
|
191
134
|
|
192
|
-
/**
|
193
|
-
* Register the given node with this InertRoot and with InertManager.
|
194
|
-
* @param {!Node} node
|
195
|
-
*/
|
196
135
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
}
|
136
|
+
/**
|
137
|
+
* @param {!Node} startNode
|
138
|
+
*/
|
139
|
+
value: function _makeSubtreeUnfocusable(startNode) {
|
140
|
+
var _this2 = this;
|
203
141
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
*/
|
142
|
+
composedTreeWalk(startNode, function (node) {
|
143
|
+
return _this2._visitNode(node);
|
144
|
+
});
|
208
145
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
146
|
+
var activeElement = document.activeElement;
|
147
|
+
|
148
|
+
if (!document.body.contains(startNode)) {
|
149
|
+
// startNode may be in shadow DOM, so find its nearest shadowRoot to get the activeElement.
|
150
|
+
var node = startNode;
|
151
|
+
/** @type {!ShadowRoot|undefined} */
|
152
|
+
var root = undefined;
|
153
|
+
while (node) {
|
154
|
+
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
155
|
+
root = /** @type {!ShadowRoot} */node;
|
156
|
+
break;
|
157
|
+
}
|
158
|
+
node = node.parentNode;
|
159
|
+
}
|
160
|
+
if (root) {
|
161
|
+
activeElement = root.activeElement;
|
162
|
+
}
|
163
|
+
}
|
164
|
+
if (startNode.contains(activeElement)) {
|
165
|
+
activeElement.blur();
|
166
|
+
// In IE11, if an element is already focused, and then set to tabindex=-1
|
167
|
+
// calling blur() will not actually move the focus.
|
168
|
+
// To work around this we call focus() on the body instead.
|
169
|
+
if (activeElement === document.activeElement) {
|
170
|
+
document.body.focus();
|
171
|
+
}
|
172
|
+
}
|
215
173
|
}
|
216
|
-
}
|
217
174
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
*/
|
175
|
+
/**
|
176
|
+
* @param {!Node} node
|
177
|
+
*/
|
222
178
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
179
|
+
}, {
|
180
|
+
key: '_visitNode',
|
181
|
+
value: function _visitNode(node) {
|
182
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
183
|
+
return;
|
184
|
+
}
|
185
|
+
var element = /** @type {!Element} */node;
|
227
186
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
187
|
+
// If a descendant inert root becomes un-inert, its descendants will still be inert because of
|
188
|
+
// this inert root, so all of its managed nodes need to be adopted by this InertRoot.
|
189
|
+
if (element !== this._rootElement && element.hasAttribute('inert')) {
|
190
|
+
this._adoptInertRoot(element);
|
191
|
+
}
|
232
192
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
193
|
+
if (matches.call(element, _focusableElementsString) || element.hasAttribute('tabindex')) {
|
194
|
+
this._manageNode(element);
|
195
|
+
}
|
196
|
+
}
|
237
197
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
198
|
+
/**
|
199
|
+
* Register the given node with this InertRoot and with InertManager.
|
200
|
+
* @param {!Node} node
|
201
|
+
*/
|
242
202
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
this._inertManager.
|
247
|
-
|
203
|
+
}, {
|
204
|
+
key: '_manageNode',
|
205
|
+
value: function _manageNode(node) {
|
206
|
+
var inertNode = this._inertManager.register(node, this);
|
207
|
+
this._managedNodes.add(inertNode);
|
248
208
|
}
|
249
209
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
210
|
+
/**
|
211
|
+
* Unregister the given node with this InertRoot and with InertManager.
|
212
|
+
* @param {!Node} node
|
213
|
+
*/
|
214
|
+
|
215
|
+
}, {
|
216
|
+
key: '_unmanageNode',
|
217
|
+
value: function _unmanageNode(node) {
|
218
|
+
var inertNode = this._inertManager.deregister(node, this);
|
219
|
+
if (inertNode) {
|
220
|
+
this._managedNodes['delete'](inertNode);
|
221
|
+
}
|
222
|
+
}
|
254
223
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
*/
|
224
|
+
/**
|
225
|
+
* Unregister the entire subtree starting at `startNode`.
|
226
|
+
* @param {!Node} startNode
|
227
|
+
*/
|
260
228
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
if (target.contains(managedNode.node)) {
|
287
|
-
inertSubroot._manageNode(managedNode.node);
|
288
|
-
}
|
289
|
-
});
|
290
|
-
}
|
229
|
+
}, {
|
230
|
+
key: '_unmanageSubtree',
|
231
|
+
value: function _unmanageSubtree(startNode) {
|
232
|
+
var _this3 = this;
|
233
|
+
|
234
|
+
composedTreeWalk(startNode, function (node) {
|
235
|
+
return _this3._unmanageNode(node);
|
236
|
+
});
|
237
|
+
}
|
238
|
+
|
239
|
+
/**
|
240
|
+
* If a descendant node is found with an `inert` attribute, adopt its managed nodes.
|
241
|
+
* @param {!Element} node
|
242
|
+
*/
|
243
|
+
|
244
|
+
}, {
|
245
|
+
key: '_adoptInertRoot',
|
246
|
+
value: function _adoptInertRoot(node) {
|
247
|
+
var inertSubroot = this._inertManager.getInertRoot(node);
|
248
|
+
|
249
|
+
// During initialisation this inert root may not have been registered yet,
|
250
|
+
// so register it now if need be.
|
251
|
+
if (!inertSubroot) {
|
252
|
+
this._inertManager.setInert(node, true);
|
253
|
+
inertSubroot = this._inertManager.getInertRoot(node);
|
291
254
|
}
|
292
|
-
}, this);
|
293
|
-
}
|
294
|
-
}, {
|
295
|
-
key: 'managedNodes',
|
296
|
-
get: function get() {
|
297
|
-
return new Set(this._managedNodes);
|
298
|
-
}
|
299
255
|
|
300
|
-
|
256
|
+
inertSubroot.managedNodes.forEach(function (savedInertNode) {
|
257
|
+
this._manageNode(savedInertNode.node);
|
258
|
+
}, this);
|
259
|
+
}
|
301
260
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
261
|
+
/**
|
262
|
+
* Callback used when mutation observer detects subtree additions, removals, or attribute changes.
|
263
|
+
* @param {!Array<!MutationRecord>} records
|
264
|
+
* @param {!MutationObserver} self
|
265
|
+
*/
|
266
|
+
|
267
|
+
}, {
|
268
|
+
key: '_onMutation',
|
269
|
+
value: function _onMutation(records, self) {
|
270
|
+
records.forEach(function (record) {
|
271
|
+
var target = /** @type {!Element} */record.target;
|
272
|
+
if (record.type === 'childList') {
|
273
|
+
// Manage added nodes
|
274
|
+
slice.call(record.addedNodes).forEach(function (node) {
|
275
|
+
this._makeSubtreeUnfocusable(node);
|
276
|
+
}, this);
|
277
|
+
|
278
|
+
// Un-manage removed nodes
|
279
|
+
slice.call(record.removedNodes).forEach(function (node) {
|
280
|
+
this._unmanageSubtree(node);
|
281
|
+
}, this);
|
282
|
+
} else if (record.type === 'attributes') {
|
283
|
+
if (record.attributeName === 'tabindex') {
|
284
|
+
// Re-initialise inert node if tabindex changes
|
285
|
+
this._manageNode(target);
|
286
|
+
} else if (target !== this._rootElement && record.attributeName === 'inert' && target.hasAttribute('inert')) {
|
287
|
+
// If a new inert root is added, adopt its managed nodes and make sure it knows about the
|
288
|
+
// already managed nodes from this inert subroot.
|
289
|
+
this._adoptInertRoot(target);
|
290
|
+
var inertSubroot = this._inertManager.getInertRoot(target);
|
291
|
+
this._managedNodes.forEach(function (managedNode) {
|
292
|
+
if (target.contains(managedNode.node)) {
|
293
|
+
inertSubroot._manageNode(managedNode.node);
|
294
|
+
}
|
295
|
+
});
|
296
|
+
}
|
297
|
+
}
|
298
|
+
}, this);
|
299
|
+
}
|
300
|
+
}, {
|
301
|
+
key: 'managedNodes',
|
302
|
+
get: function get() {
|
303
|
+
return new Set(this._managedNodes);
|
304
|
+
}
|
307
305
|
|
308
|
-
|
306
|
+
/** @return {boolean} */
|
309
307
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
308
|
+
}, {
|
309
|
+
key: 'hasSavedAriaHidden',
|
310
|
+
get: function get() {
|
311
|
+
return this._savedAriaHidden !== null;
|
312
|
+
}
|
315
313
|
|
316
|
-
|
317
|
-
,
|
318
|
-
get: function get() {
|
319
|
-
return this._savedAriaHidden;
|
320
|
-
}
|
321
|
-
}]);
|
314
|
+
/** @param {?string} ariaHidden */
|
322
315
|
|
323
|
-
|
324
|
-
|
316
|
+
}, {
|
317
|
+
key: 'savedAriaHidden',
|
318
|
+
set: function set(ariaHidden) {
|
319
|
+
this._savedAriaHidden = ariaHidden;
|
320
|
+
}
|
325
321
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
* is intrinsically focusable or not.
|
333
|
-
*
|
334
|
-
* `InertNode` maintains a set of `InertRoot`s which are descendants of this `InertNode`. When an
|
335
|
-
* `InertRoot` is destroyed, and calls `InertManager.deregister()`, the `InertManager` notifies the
|
336
|
-
* `InertNode` via `removeInertRoot()`, which in turn destroys the `InertNode` if no `InertRoot`s
|
337
|
-
* remain in the set. On destruction, `InertNode` reinstates the stored `tabindex` if one exists,
|
338
|
-
* or removes the `tabindex` attribute if the element is intrinsically focusable.
|
339
|
-
*/
|
322
|
+
/** @return {?string} */
|
323
|
+
,
|
324
|
+
get: function get() {
|
325
|
+
return this._savedAriaHidden;
|
326
|
+
}
|
327
|
+
}]);
|
340
328
|
|
329
|
+
return InertRoot;
|
330
|
+
}();
|
341
331
|
|
342
|
-
var InertNode = function () {
|
343
332
|
/**
|
344
|
-
*
|
345
|
-
*
|
333
|
+
* `InertNode` initialises and manages a single inert node.
|
334
|
+
* A node is inert if it is a descendant of one or more inert root elements.
|
335
|
+
*
|
336
|
+
* On construction, `InertNode` saves the existing `tabindex` value for the node, if any, and
|
337
|
+
* either removes the `tabindex` attribute or sets it to `-1`, depending on whether the element
|
338
|
+
* is intrinsically focusable or not.
|
339
|
+
*
|
340
|
+
* `InertNode` maintains a set of `InertRoot`s which are descendants of this `InertNode`. When an
|
341
|
+
* `InertRoot` is destroyed, and calls `InertManager.deregister()`, the `InertManager` notifies the
|
342
|
+
* `InertNode` via `removeInertRoot()`, which in turn destroys the `InertNode` if no `InertRoot`s
|
343
|
+
* remain in the set. On destruction, `InertNode` reinstates the stored `tabindex` if one exists,
|
344
|
+
* or removes the `tabindex` attribute if the element is intrinsically focusable.
|
346
345
|
*/
|
347
|
-
function InertNode(node, inertRoot) {
|
348
|
-
_classCallCheck(this, InertNode);
|
349
|
-
|
350
|
-
/** @type {!Node} */
|
351
|
-
this._node = node;
|
352
346
|
|
353
|
-
/** @type {boolean} */
|
354
|
-
this._overrodeFocusMethod = false;
|
355
347
|
|
348
|
+
var InertNode = function () {
|
356
349
|
/**
|
357
|
-
* @
|
358
|
-
*
|
350
|
+
* @param {!Node} node A focusable element to be made inert.
|
351
|
+
* @param {!InertRoot} inertRoot The inert root element associated with this inert node.
|
359
352
|
*/
|
360
|
-
|
361
|
-
|
362
|
-
/** @type {?number} */
|
363
|
-
this._savedTabIndex = null;
|
364
|
-
|
365
|
-
/** @type {boolean} */
|
366
|
-
this._destroyed = false;
|
367
|
-
|
368
|
-
// Save any prior tabindex info and make this node untabbable
|
369
|
-
this.ensureUntabbable();
|
370
|
-
}
|
353
|
+
function InertNode(node, inertRoot) {
|
354
|
+
_classCallCheck(this, InertNode);
|
371
355
|
|
372
|
-
|
373
|
-
|
374
|
-
* This makes the managed node focusable again and deletes all of the previously stored state.
|
375
|
-
*/
|
356
|
+
/** @type {!Node} */
|
357
|
+
this._node = node;
|
376
358
|
|
359
|
+
/** @type {boolean} */
|
360
|
+
this._overrodeFocusMethod = false;
|
377
361
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
362
|
+
/**
|
363
|
+
* @type {!Set<!InertRoot>} The set of descendant inert roots.
|
364
|
+
* If and only if this set becomes empty, this node is no longer inert.
|
365
|
+
*/
|
366
|
+
this._inertRoots = new Set([inertRoot]);
|
382
367
|
|
383
|
-
|
384
|
-
|
385
|
-
if (this._savedTabIndex !== null) {
|
386
|
-
element.setAttribute('tabindex', this._savedTabIndex);
|
387
|
-
} else {
|
388
|
-
element.removeAttribute('tabindex');
|
389
|
-
}
|
368
|
+
/** @type {?number} */
|
369
|
+
this._savedTabIndex = null;
|
390
370
|
|
391
|
-
|
392
|
-
|
393
|
-
delete element.focus;
|
394
|
-
}
|
395
|
-
}
|
371
|
+
/** @type {boolean} */
|
372
|
+
this._destroyed = false;
|
396
373
|
|
397
|
-
//
|
398
|
-
this.
|
399
|
-
this._inertRoots = /** @type {?} */null;
|
400
|
-
this._destroyed = true;
|
374
|
+
// Save any prior tabindex info and make this node untabbable
|
375
|
+
this.ensureUntabbable();
|
401
376
|
}
|
402
377
|
|
403
378
|
/**
|
404
|
-
*
|
405
|
-
*
|
379
|
+
* Call this whenever this object is about to become obsolete.
|
380
|
+
* This makes the managed node focusable again and deletes all of the previously stored state.
|
406
381
|
*/
|
407
382
|
|
408
|
-
}, {
|
409
|
-
key: '_throwIfDestroyed',
|
410
383
|
|
384
|
+
_createClass(InertNode, [{
|
385
|
+
key: 'destructor',
|
386
|
+
value: function destructor() {
|
387
|
+
this._throwIfDestroyed();
|
411
388
|
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
389
|
+
if (this._node && this._node.nodeType === Node.ELEMENT_NODE) {
|
390
|
+
var element = /** @type {!Element} */this._node;
|
391
|
+
if (this._savedTabIndex !== null) {
|
392
|
+
element.setAttribute('tabindex', this._savedTabIndex);
|
393
|
+
} else {
|
394
|
+
element.removeAttribute('tabindex');
|
395
|
+
}
|
396
|
+
|
397
|
+
// Use `delete` to restore native focus method.
|
398
|
+
if (this._overrodeFocusMethod) {
|
399
|
+
delete element.focus;
|
400
|
+
}
|
401
|
+
}
|
402
|
+
|
403
|
+
// See note in InertRoot.destructor for why we cast these nulls to ANY.
|
404
|
+
this._node = /** @type {?} */null;
|
405
|
+
this._inertRoots = /** @type {?} */null;
|
406
|
+
this._destroyed = true;
|
418
407
|
}
|
419
|
-
}
|
420
408
|
|
421
|
-
|
409
|
+
/**
|
410
|
+
* @type {boolean} Whether this object is obsolete because the managed node is no longer inert.
|
411
|
+
* If the object has been destroyed, any attempt to access it will cause an exception.
|
412
|
+
*/
|
422
413
|
|
423
|
-
|
424
|
-
|
414
|
+
}, {
|
415
|
+
key: '_throwIfDestroyed',
|
425
416
|
|
426
417
|
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
418
|
+
/**
|
419
|
+
* Throw if user tries to access destroyed InertNode.
|
420
|
+
*/
|
421
|
+
value: function _throwIfDestroyed() {
|
422
|
+
if (this.destroyed) {
|
423
|
+
throw new Error('Trying to access destroyed InertNode');
|
424
|
+
}
|
431
425
|
}
|
432
|
-
|
433
|
-
|
434
|
-
|
426
|
+
|
427
|
+
/** @return {boolean} */
|
428
|
+
|
429
|
+
}, {
|
430
|
+
key: 'ensureUntabbable',
|
431
|
+
|
432
|
+
|
433
|
+
/** Save the existing tabindex value and make the node untabbable and unfocusable */
|
434
|
+
value: function ensureUntabbable() {
|
435
|
+
if (this.node.nodeType !== Node.ELEMENT_NODE) {
|
435
436
|
return;
|
436
437
|
}
|
438
|
+
var element = /** @type {!Element} */this.node;
|
439
|
+
if (matches.call(element, _focusableElementsString)) {
|
440
|
+
if ( /** @type {!HTMLElement} */element.tabIndex === -1 && this.hasSavedTabIndex) {
|
441
|
+
return;
|
442
|
+
}
|
437
443
|
|
438
|
-
|
444
|
+
if (element.hasAttribute('tabindex')) {
|
445
|
+
this._savedTabIndex = /** @type {!HTMLElement} */element.tabIndex;
|
446
|
+
}
|
447
|
+
element.setAttribute('tabindex', '-1');
|
448
|
+
if (element.nodeType === Node.ELEMENT_NODE) {
|
449
|
+
element.focus = function () {};
|
450
|
+
this._overrodeFocusMethod = true;
|
451
|
+
}
|
452
|
+
} else if (element.hasAttribute('tabindex')) {
|
439
453
|
this._savedTabIndex = /** @type {!HTMLElement} */element.tabIndex;
|
454
|
+
element.removeAttribute('tabindex');
|
440
455
|
}
|
441
|
-
element.setAttribute('tabindex', '-1');
|
442
|
-
if (element.nodeType === Node.ELEMENT_NODE) {
|
443
|
-
element.focus = function () {};
|
444
|
-
this._overrodeFocusMethod = true;
|
445
|
-
}
|
446
|
-
} else if (element.hasAttribute('tabindex')) {
|
447
|
-
this._savedTabIndex = /** @type {!HTMLElement} */element.tabIndex;
|
448
|
-
element.removeAttribute('tabindex');
|
449
456
|
}
|
450
|
-
}
|
451
457
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
}, {
|
458
|
-
key: 'addInertRoot',
|
459
|
-
value: function addInertRoot(inertRoot) {
|
460
|
-
this._throwIfDestroyed();
|
461
|
-
this._inertRoots.add(inertRoot);
|
462
|
-
}
|
463
|
-
|
464
|
-
/**
|
465
|
-
* Remove the given inert root from this inert node's set of managing inert roots.
|
466
|
-
* If the set of managing inert roots becomes empty, this node is no longer inert,
|
467
|
-
* so the object should be destroyed.
|
468
|
-
* @param {!InertRoot} inertRoot
|
469
|
-
*/
|
458
|
+
/**
|
459
|
+
* Add another inert root to this inert node's set of managing inert roots.
|
460
|
+
* @param {!InertRoot} inertRoot
|
461
|
+
*/
|
470
462
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
if (this._inertRoots.size === 0) {
|
477
|
-
this.destructor();
|
463
|
+
}, {
|
464
|
+
key: 'addInertRoot',
|
465
|
+
value: function addInertRoot(inertRoot) {
|
466
|
+
this._throwIfDestroyed();
|
467
|
+
this._inertRoots.add(inertRoot);
|
478
468
|
}
|
479
|
-
}
|
480
|
-
}, {
|
481
|
-
key: 'destroyed',
|
482
|
-
get: function get() {
|
483
|
-
return (/** @type {!InertNode} */this._destroyed
|
484
|
-
);
|
485
|
-
}
|
486
|
-
}, {
|
487
|
-
key: 'hasSavedTabIndex',
|
488
|
-
get: function get() {
|
489
|
-
return this._savedTabIndex !== null;
|
490
|
-
}
|
491
|
-
|
492
|
-
/** @return {!Node} */
|
493
469
|
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
470
|
+
/**
|
471
|
+
* Remove the given inert root from this inert node's set of managing inert roots.
|
472
|
+
* If the set of managing inert roots becomes empty, this node is no longer inert,
|
473
|
+
* so the object should be destroyed.
|
474
|
+
* @param {!InertRoot} inertRoot
|
475
|
+
*/
|
476
|
+
|
477
|
+
}, {
|
478
|
+
key: 'removeInertRoot',
|
479
|
+
value: function removeInertRoot(inertRoot) {
|
480
|
+
this._throwIfDestroyed();
|
481
|
+
this._inertRoots['delete'](inertRoot);
|
482
|
+
if (this._inertRoots.size === 0) {
|
483
|
+
this.destructor();
|
484
|
+
}
|
485
|
+
}
|
486
|
+
}, {
|
487
|
+
key: 'destroyed',
|
488
|
+
get: function get() {
|
489
|
+
return (/** @type {!InertNode} */this._destroyed
|
490
|
+
);
|
491
|
+
}
|
492
|
+
}, {
|
493
|
+
key: 'hasSavedTabIndex',
|
494
|
+
get: function get() {
|
495
|
+
return this._savedTabIndex !== null;
|
496
|
+
}
|
500
497
|
|
501
|
-
|
498
|
+
/** @return {!Node} */
|
502
499
|
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
500
|
+
}, {
|
501
|
+
key: 'node',
|
502
|
+
get: function get() {
|
503
|
+
this._throwIfDestroyed();
|
504
|
+
return this._node;
|
505
|
+
}
|
509
506
|
|
510
|
-
|
511
|
-
,
|
512
|
-
get: function get() {
|
513
|
-
this._throwIfDestroyed();
|
514
|
-
return this._savedTabIndex;
|
515
|
-
}
|
516
|
-
}]);
|
507
|
+
/** @param {?number} tabIndex */
|
517
508
|
|
518
|
-
|
519
|
-
|
509
|
+
}, {
|
510
|
+
key: 'savedTabIndex',
|
511
|
+
set: function set(tabIndex) {
|
512
|
+
this._throwIfDestroyed();
|
513
|
+
this._savedTabIndex = tabIndex;
|
514
|
+
}
|
520
515
|
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
* is created for each such node, via the `_managedNodes` map.
|
529
|
-
*/
|
516
|
+
/** @return {?number} */
|
517
|
+
,
|
518
|
+
get: function get() {
|
519
|
+
this._throwIfDestroyed();
|
520
|
+
return this._savedTabIndex;
|
521
|
+
}
|
522
|
+
}]);
|
530
523
|
|
524
|
+
return InertNode;
|
525
|
+
}();
|
531
526
|
|
532
|
-
var InertManager = function () {
|
533
527
|
/**
|
534
|
-
*
|
528
|
+
* InertManager is a per-document singleton object which manages all inert roots and nodes.
|
529
|
+
*
|
530
|
+
* When an element becomes an inert root by having an `inert` attribute set and/or its `inert`
|
531
|
+
* property set to `true`, the `setInert` method creates an `InertRoot` object for the element.
|
532
|
+
* The `InertRoot` in turn registers itself as managing all of the element's focusable descendant
|
533
|
+
* nodes via the `register()` method. The `InertManager` ensures that a single `InertNode` instance
|
534
|
+
* is created for each such node, via the `_managedNodes` map.
|
535
535
|
*/
|
536
|
-
function InertManager(document) {
|
537
|
-
_classCallCheck(this, InertManager);
|
538
536
|
|
539
|
-
if (!document) {
|
540
|
-
throw new Error('Missing required argument; InertManager needs to wrap a document.');
|
541
|
-
}
|
542
|
-
|
543
|
-
/** @type {!Document} */
|
544
|
-
this._document = document;
|
545
|
-
|
546
|
-
/**
|
547
|
-
* All managed nodes known to this InertManager. In a map to allow looking up by Node.
|
548
|
-
* @type {!Map<!Node, !InertNode>}
|
549
|
-
*/
|
550
|
-
this._managedNodes = new Map();
|
551
|
-
|
552
|
-
/**
|
553
|
-
* All inert roots known to this InertManager. In a map to allow looking up by Node.
|
554
|
-
* @type {!Map<!Node, !InertRoot>}
|
555
|
-
*/
|
556
|
-
this._inertRoots = new Map();
|
557
537
|
|
538
|
+
var InertManager = function () {
|
558
539
|
/**
|
559
|
-
*
|
560
|
-
* @type {!MutationObserver}
|
540
|
+
* @param {!Document} document
|
561
541
|
*/
|
562
|
-
|
563
|
-
|
564
|
-
// Add inert style.
|
565
|
-
addInertStyle(document.head || document.body || document.documentElement);
|
566
|
-
|
567
|
-
// Wait for document to be loaded.
|
568
|
-
if (document.readyState === 'loading') {
|
569
|
-
document.addEventListener('DOMContentLoaded', this._onDocumentLoaded.bind(this));
|
570
|
-
} else {
|
571
|
-
this._onDocumentLoaded();
|
572
|
-
}
|
573
|
-
}
|
574
|
-
|
575
|
-
/**
|
576
|
-
* Set whether the given element should be an inert root or not.
|
577
|
-
* @param {!Element} root
|
578
|
-
* @param {boolean} inert
|
579
|
-
*/
|
542
|
+
function InertManager(document) {
|
543
|
+
_classCallCheck(this, InertManager);
|
580
544
|
|
545
|
+
if (!document) {
|
546
|
+
throw new Error('Missing required argument; InertManager needs to wrap a document.');
|
547
|
+
}
|
581
548
|
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
549
|
+
/** @type {!Document} */
|
550
|
+
this._document = document;
|
551
|
+
|
552
|
+
/**
|
553
|
+
* All managed nodes known to this InertManager. In a map to allow looking up by Node.
|
554
|
+
* @type {!Map<!Node, !InertNode>}
|
555
|
+
*/
|
556
|
+
this._managedNodes = new Map();
|
557
|
+
|
558
|
+
/**
|
559
|
+
* All inert roots known to this InertManager. In a map to allow looking up by Node.
|
560
|
+
* @type {!Map<!Node, !InertRoot>}
|
561
|
+
*/
|
562
|
+
this._inertRoots = new Map();
|
563
|
+
|
564
|
+
/**
|
565
|
+
* Observer for mutations on `document.body`.
|
566
|
+
* @type {!MutationObserver}
|
567
|
+
*/
|
568
|
+
this._observer = new MutationObserver(this._watchForInert.bind(this));
|
569
|
+
|
570
|
+
// Add inert style.
|
571
|
+
addInertStyle(document.head || document.body || document.documentElement);
|
572
|
+
|
573
|
+
// Wait for document to be loaded.
|
574
|
+
if (document.readyState === 'loading') {
|
575
|
+
document.addEventListener('DOMContentLoaded', this._onDocumentLoaded.bind(this));
|
605
576
|
} else {
|
606
|
-
|
607
|
-
// element is already non-inert
|
608
|
-
return;
|
609
|
-
}
|
610
|
-
|
611
|
-
var _inertRoot = this._inertRoots.get(root);
|
612
|
-
_inertRoot.destructor();
|
613
|
-
this._inertRoots['delete'](root);
|
614
|
-
root.removeAttribute('inert');
|
577
|
+
this._onDocumentLoaded();
|
615
578
|
}
|
616
579
|
}
|
617
580
|
|
618
581
|
/**
|
619
|
-
*
|
620
|
-
* @param {!
|
621
|
-
* @
|
582
|
+
* Set whether the given element should be an inert root or not.
|
583
|
+
* @param {!Element} root
|
584
|
+
* @param {boolean} inert
|
622
585
|
*/
|
623
586
|
|
624
|
-
}, {
|
625
|
-
key: 'getInertRoot',
|
626
|
-
value: function getInertRoot(element) {
|
627
|
-
return this._inertRoots.get(element);
|
628
|
-
}
|
629
587
|
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
588
|
+
_createClass(InertManager, [{
|
589
|
+
key: 'setInert',
|
590
|
+
value: function setInert(root, inert) {
|
591
|
+
if (inert) {
|
592
|
+
if (this._inertRoots.has(root)) {
|
593
|
+
// element is already inert
|
594
|
+
return;
|
595
|
+
}
|
638
596
|
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
597
|
+
var inertRoot = new InertRoot(root, this);
|
598
|
+
root.setAttribute('inert', '');
|
599
|
+
this._inertRoots.set(root, inertRoot);
|
600
|
+
// If not contained in the document, it must be in a shadowRoot.
|
601
|
+
// Ensure inert styles are added there.
|
602
|
+
if (!this._document.body.contains(root)) {
|
603
|
+
var parent = root.parentNode;
|
604
|
+
while (parent) {
|
605
|
+
if (parent.nodeType === 11) {
|
606
|
+
addInertStyle(parent);
|
607
|
+
}
|
608
|
+
parent = parent.parentNode;
|
609
|
+
}
|
610
|
+
}
|
611
|
+
} else {
|
612
|
+
if (!this._inertRoots.has(root)) {
|
613
|
+
// element is already non-inert
|
614
|
+
return;
|
615
|
+
}
|
616
|
+
|
617
|
+
var _inertRoot = this._inertRoots.get(root);
|
618
|
+
_inertRoot.destructor();
|
619
|
+
this._inertRoots['delete'](root);
|
620
|
+
root.removeAttribute('inert');
|
621
|
+
}
|
648
622
|
}
|
649
623
|
|
650
|
-
|
624
|
+
/**
|
625
|
+
* Get the InertRoot object corresponding to the given inert root element, if any.
|
626
|
+
* @param {!Node} element
|
627
|
+
* @return {!InertRoot|undefined}
|
628
|
+
*/
|
651
629
|
|
652
|
-
|
653
|
-
|
630
|
+
}, {
|
631
|
+
key: 'getInertRoot',
|
632
|
+
value: function getInertRoot(element) {
|
633
|
+
return this._inertRoots.get(element);
|
634
|
+
}
|
654
635
|
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
636
|
+
/**
|
637
|
+
* Register the given InertRoot as managing the given node.
|
638
|
+
* In the case where the node has a previously existing inert root, this inert root will
|
639
|
+
* be added to its set of inert roots.
|
640
|
+
* @param {!Node} node
|
641
|
+
* @param {!InertRoot} inertRoot
|
642
|
+
* @return {!InertNode} inertNode
|
643
|
+
*/
|
644
|
+
|
645
|
+
}, {
|
646
|
+
key: 'register',
|
647
|
+
value: function register(node, inertRoot) {
|
648
|
+
var inertNode = this._managedNodes.get(node);
|
649
|
+
if (inertNode !== undefined) {
|
650
|
+
// node was already in an inert subtree
|
651
|
+
inertNode.addInertRoot(inertRoot);
|
652
|
+
} else {
|
653
|
+
inertNode = new InertNode(node, inertRoot);
|
654
|
+
}
|
664
655
|
|
665
|
-
|
666
|
-
key: 'deregister',
|
667
|
-
value: function deregister(node, inertRoot) {
|
668
|
-
var inertNode = this._managedNodes.get(node);
|
669
|
-
if (!inertNode) {
|
670
|
-
return null;
|
671
|
-
}
|
656
|
+
this._managedNodes.set(node, inertNode);
|
672
657
|
|
673
|
-
|
674
|
-
if (inertNode.destroyed) {
|
675
|
-
this._managedNodes['delete'](node);
|
658
|
+
return inertNode;
|
676
659
|
}
|
677
660
|
|
678
|
-
|
679
|
-
|
661
|
+
/**
|
662
|
+
* De-register the given InertRoot as managing the given inert node.
|
663
|
+
* Removes the inert root from the InertNode's set of managing inert roots, and remove the inert
|
664
|
+
* node from the InertManager's set of managed nodes if it is destroyed.
|
665
|
+
* If the node is not currently managed, this is essentially a no-op.
|
666
|
+
* @param {!Node} node
|
667
|
+
* @param {!InertRoot} inertRoot
|
668
|
+
* @return {?InertNode} The potentially destroyed InertNode associated with this node, if any.
|
669
|
+
*/
|
670
|
+
|
671
|
+
}, {
|
672
|
+
key: 'deregister',
|
673
|
+
value: function deregister(node, inertRoot) {
|
674
|
+
var inertNode = this._managedNodes.get(node);
|
675
|
+
if (!inertNode) {
|
676
|
+
return null;
|
677
|
+
}
|
680
678
|
|
681
|
-
|
682
|
-
|
683
|
-
|
679
|
+
inertNode.removeInertRoot(inertRoot);
|
680
|
+
if (inertNode.destroyed) {
|
681
|
+
this._managedNodes['delete'](node);
|
682
|
+
}
|
684
683
|
|
685
|
-
|
686
|
-
|
687
|
-
value: function _onDocumentLoaded() {
|
688
|
-
// Find all inert roots in document and make them actually inert.
|
689
|
-
var inertElements = slice.call(this._document.querySelectorAll('[inert]'));
|
690
|
-
inertElements.forEach(function (inertElement) {
|
691
|
-
this.setInert(inertElement, true);
|
692
|
-
}, this);
|
693
|
-
|
694
|
-
// Comment this out to use programmatic API only.
|
695
|
-
this._observer.observe(this._document.body, { attributes: true, subtree: true, childList: true });
|
696
|
-
}
|
684
|
+
return inertNode;
|
685
|
+
}
|
697
686
|
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
687
|
+
/**
|
688
|
+
* Callback used when document has finished loading.
|
689
|
+
*/
|
690
|
+
|
691
|
+
}, {
|
692
|
+
key: '_onDocumentLoaded',
|
693
|
+
value: function _onDocumentLoaded() {
|
694
|
+
// Find all inert roots in document and make them actually inert.
|
695
|
+
var inertElements = slice.call(this._document.querySelectorAll('[inert]'));
|
696
|
+
inertElements.forEach(function (inertElement) {
|
697
|
+
this.setInert(inertElement, true);
|
698
|
+
}, this);
|
699
|
+
|
700
|
+
// Comment this out to use programmatic API only.
|
701
|
+
this._observer.observe(this._document.body || this._document.documentElement, { attributes: true, subtree: true, childList: true });
|
702
|
+
}
|
703
703
|
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
704
|
+
/**
|
705
|
+
* Callback used when mutation observer detects attribute changes.
|
706
|
+
* @param {!Array<!MutationRecord>} records
|
707
|
+
* @param {!MutationObserver} self
|
708
|
+
*/
|
709
|
+
|
710
|
+
}, {
|
711
|
+
key: '_watchForInert',
|
712
|
+
value: function _watchForInert(records, self) {
|
713
|
+
var _this = this;
|
714
|
+
records.forEach(function (record) {
|
715
|
+
switch (record.type) {
|
716
|
+
case 'childList':
|
717
|
+
slice.call(record.addedNodes).forEach(function (node) {
|
718
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
719
|
+
return;
|
720
|
+
}
|
721
|
+
var inertElements = slice.call(node.querySelectorAll('[inert]'));
|
722
|
+
if (matches.call(node, '[inert]')) {
|
723
|
+
inertElements.unshift(node);
|
724
|
+
}
|
725
|
+
inertElements.forEach(function (inertElement) {
|
726
|
+
this.setInert(inertElement, true);
|
727
|
+
}, _this);
|
728
|
+
}, _this);
|
729
|
+
break;
|
730
|
+
case 'attributes':
|
731
|
+
if (record.attributeName !== 'inert') {
|
713
732
|
return;
|
714
733
|
}
|
715
|
-
var
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
break;
|
724
|
-
case 'attributes':
|
725
|
-
if (record.attributeName !== 'inert') {
|
726
|
-
return;
|
727
|
-
}
|
728
|
-
var target = /** @type {!Element} */record.target;
|
729
|
-
var inert = target.hasAttribute('inert');
|
730
|
-
_this.setInert(target, inert);
|
731
|
-
break;
|
732
|
-
}
|
733
|
-
}, this);
|
734
|
-
}
|
735
|
-
}]);
|
734
|
+
var target = /** @type {!Element} */record.target;
|
735
|
+
var inert = target.hasAttribute('inert');
|
736
|
+
_this.setInert(target, inert);
|
737
|
+
break;
|
738
|
+
}
|
739
|
+
}, this);
|
740
|
+
}
|
741
|
+
}]);
|
736
742
|
|
737
|
-
|
738
|
-
|
743
|
+
return InertManager;
|
744
|
+
}();
|
739
745
|
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
746
|
+
/**
|
747
|
+
* Recursively walk the composed tree from |node|.
|
748
|
+
* @param {!Node} node
|
749
|
+
* @param {(function (!Element))=} callback Callback to be called for each element traversed,
|
750
|
+
* before descending into child nodes.
|
751
|
+
* @param {?ShadowRoot=} shadowRootAncestor The nearest ShadowRoot ancestor, if any.
|
752
|
+
*/
|
747
753
|
|
748
754
|
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
+
function composedTreeWalk(node, callback, shadowRootAncestor) {
|
756
|
+
if (node.nodeType == Node.ELEMENT_NODE) {
|
757
|
+
var element = /** @type {!Element} */node;
|
758
|
+
if (callback) {
|
759
|
+
callback(element);
|
760
|
+
}
|
755
761
|
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
762
|
+
// Descend into node:
|
763
|
+
// If it has a ShadowRoot, ignore all child elements - these will be picked
|
764
|
+
// up by the <content> or <shadow> elements. Descend straight into the
|
765
|
+
// ShadowRoot.
|
766
|
+
var shadowRoot = /** @type {!HTMLElement} */element.shadowRoot;
|
767
|
+
if (shadowRoot) {
|
768
|
+
composedTreeWalk(shadowRoot, callback, shadowRoot);
|
769
|
+
return;
|
770
|
+
}
|
765
771
|
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
772
|
+
// If it is a <content> element, descend into distributed elements - these
|
773
|
+
// are elements from outside the shadow root which are rendered inside the
|
774
|
+
// shadow DOM.
|
775
|
+
if (element.localName == 'content') {
|
776
|
+
var content = /** @type {!HTMLContentElement} */element;
|
777
|
+
// Verifies if ShadowDom v0 is supported.
|
778
|
+
var distributedNodes = content.getDistributedNodes ? content.getDistributedNodes() : [];
|
779
|
+
for (var i = 0; i < distributedNodes.length; i++) {
|
780
|
+
composedTreeWalk(distributedNodes[i], callback, shadowRootAncestor);
|
781
|
+
}
|
782
|
+
return;
|
775
783
|
}
|
776
|
-
return;
|
777
|
-
}
|
778
784
|
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
785
|
+
// If it is a <slot> element, descend into assigned nodes - these
|
786
|
+
// are elements from outside the shadow root which are rendered inside the
|
787
|
+
// shadow DOM.
|
788
|
+
if (element.localName == 'slot') {
|
789
|
+
var slot = /** @type {!HTMLSlotElement} */element;
|
790
|
+
// Verify if ShadowDom v1 is supported.
|
791
|
+
var _distributedNodes = slot.assignedNodes ? slot.assignedNodes({ flatten: true }) : [];
|
792
|
+
for (var _i = 0; _i < _distributedNodes.length; _i++) {
|
793
|
+
composedTreeWalk(_distributedNodes[_i], callback, shadowRootAncestor);
|
794
|
+
}
|
795
|
+
return;
|
788
796
|
}
|
789
|
-
|
797
|
+
}
|
798
|
+
|
799
|
+
// If it is neither the parent of a ShadowRoot, a <content> element, a <slot>
|
800
|
+
// element, nor a <shadow> element recurse normally.
|
801
|
+
var child = node.firstChild;
|
802
|
+
while (child != null) {
|
803
|
+
composedTreeWalk(child, callback, shadowRootAncestor);
|
804
|
+
child = child.nextSibling;
|
790
805
|
}
|
791
806
|
}
|
792
807
|
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
808
|
+
/**
|
809
|
+
* Adds a style element to the node containing the inert specific styles
|
810
|
+
* @param {!Node} node
|
811
|
+
*/
|
812
|
+
function addInertStyle(node) {
|
813
|
+
if (node.querySelector('style#inert-style, link#inert-style')) {
|
814
|
+
return;
|
815
|
+
}
|
816
|
+
var style = document.createElement('style');
|
817
|
+
style.setAttribute('id', 'inert-style');
|
818
|
+
style.textContent = '\n' + '[inert] {\n' + ' pointer-events: none;\n' + ' cursor: default;\n' + '}\n' + '\n' + '[inert], [inert] * {\n' + ' -webkit-user-select: none;\n' + ' -moz-user-select: none;\n' + ' -ms-user-select: none;\n' + ' user-select: none;\n' + '}\n';
|
819
|
+
node.appendChild(style);
|
799
820
|
}
|
800
|
-
}
|
801
821
|
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
822
|
+
if (!Element.prototype.hasOwnProperty('inert')) {
|
823
|
+
/** @type {!InertManager} */
|
824
|
+
var inertManager = new InertManager(document);
|
825
|
+
|
826
|
+
Object.defineProperty(Element.prototype, 'inert', {
|
827
|
+
enumerable: true,
|
828
|
+
/** @this {!Element} */
|
829
|
+
get: function get() {
|
830
|
+
return this.hasAttribute('inert');
|
831
|
+
},
|
832
|
+
/** @this {!Element} */
|
833
|
+
set: function set(inert) {
|
834
|
+
inertManager.setInert(this, inert);
|
835
|
+
}
|
836
|
+
});
|
809
837
|
}
|
810
|
-
|
811
|
-
style.setAttribute('id', 'inert-style');
|
812
|
-
style.textContent = '\n' + '[inert] {\n' + ' pointer-events: none;\n' + ' cursor: default;\n' + '}\n' + '\n' + '[inert], [inert] * {\n' + ' user-select: none;\n' + ' -webkit-user-select: none;\n' + ' -moz-user-select: none;\n' + ' -ms-user-select: none;\n' + '}\n';
|
813
|
-
node.appendChild(style);
|
814
|
-
}
|
815
|
-
|
816
|
-
/** @type {!InertManager} */
|
817
|
-
var inertManager = new InertManager(document);
|
818
|
-
|
819
|
-
if (!Element.prototype.hasOwnProperty('inert')) {
|
820
|
-
Object.defineProperty(Element.prototype, 'inert', {
|
821
|
-
enumerable: true,
|
822
|
-
/** @this {!Element} */
|
823
|
-
get: function get() {
|
824
|
-
return this.hasAttribute('inert');
|
825
|
-
},
|
826
|
-
/** @this {!Element} */
|
827
|
-
set: function set(inert) {
|
828
|
-
inertManager.setInert(this, inert);
|
829
|
-
}
|
830
|
-
});
|
831
|
-
}
|
838
|
+
})();
|
832
839
|
|
833
840
|
})));
|