wicg-inert 3.0.3 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,832 @@
1
+ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
2
+
3
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
4
+
5
+ /**
6
+ * This work is licensed under the W3C Software and Document License
7
+ * (http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document).
8
+ */
9
+
10
+ (function () {
11
+ // Return early if we're not running inside of the browser.
12
+ if (typeof window === 'undefined') {
13
+ return;
14
+ }
15
+
16
+ // Convenience function for converting NodeLists.
17
+ /** @type {typeof Array.prototype.slice} */
18
+ var slice = Array.prototype.slice;
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(',');
28
+
29
+ /**
30
+ * `InertRoot` manages a single inert subtree, i.e. a DOM subtree whose root element has an `inert`
31
+ * attribute.
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
+ */
45
+
46
+ var InertRoot = function () {
47
+ /**
48
+ * @param {!HTMLElement} rootElement The HTMLElement at the root of the inert subtree.
49
+ * @param {!InertManager} inertManager The global singleton InertManager object.
50
+ */
51
+ function InertRoot(rootElement, inertManager) {
52
+ _classCallCheck(this, InertRoot);
53
+
54
+ /** @type {!InertManager} */
55
+ this._inertManager = inertManager;
56
+
57
+ /** @type {!HTMLElement} */
58
+ this._rootElement = rootElement;
59
+
60
+ /**
61
+ * @type {!Set<!InertNode>}
62
+ * All managed focusable nodes in this InertRoot's subtree.
63
+ */
64
+ this._managedNodes = new Set();
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
+ }
86
+
87
+ /**
88
+ * Call this whenever this object is about to become obsolete. This unwinds all of the state
89
+ * stored in this object and updates the state of all of the managed nodes.
90
+ */
91
+
92
+
93
+ _createClass(InertRoot, [{
94
+ key: 'destructor',
95
+ value: function destructor() {
96
+ this._observer.disconnect();
97
+
98
+ if (this._rootElement) {
99
+ if (this._savedAriaHidden !== null) {
100
+ this._rootElement.setAttribute('aria-hidden', this._savedAriaHidden);
101
+ } else {
102
+ this._rootElement.removeAttribute('aria-hidden');
103
+ }
104
+ }
105
+
106
+ this._managedNodes.forEach(function (inertNode) {
107
+ this._unmanageNode(inertNode.node);
108
+ }, this);
109
+
110
+ // Note we cast the nulls to the ANY type here because:
111
+ // 1) We want the class properties to be declared as non-null, or else we
112
+ // need even more casts throughout this code. All bets are off if an
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;
120
+ }
121
+
122
+ /**
123
+ * @return {!Set<!InertNode>} A copy of this InertRoot's managed nodes set.
124
+ */
125
+
126
+ }, {
127
+ key: '_makeSubtreeUnfocusable',
128
+
129
+
130
+ /**
131
+ * @param {!Node} startNode
132
+ */
133
+ value: function _makeSubtreeUnfocusable(startNode) {
134
+ var _this2 = this;
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;
151
+ }
152
+ node = node.parentNode;
153
+ }
154
+ if (root) {
155
+ activeElement = root.activeElement;
156
+ }
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
+
173
+ }, {
174
+ key: '_visitNode',
175
+ value: function _visitNode(node) {
176
+ if (node.nodeType !== Node.ELEMENT_NODE) {
177
+ return;
178
+ }
179
+ var element = /** @type {!HTMLElement} */node;
180
+
181
+ // If a descendant inert root becomes un-inert, its descendants will still be inert because of
182
+ // this inert root, so all of its managed nodes need to be adopted by this InertRoot.
183
+ if (element !== this._rootElement && element.hasAttribute('inert')) {
184
+ this._adoptInertRoot(element);
185
+ }
186
+
187
+ if (matches.call(element, _focusableElementsString) || element.hasAttribute('tabindex')) {
188
+ this._manageNode(element);
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Register the given node with this InertRoot and with InertManager.
194
+ * @param {!Node} node
195
+ */
196
+
197
+ }, {
198
+ key: '_manageNode',
199
+ value: function _manageNode(node) {
200
+ var inertNode = this._inertManager.register(node, this);
201
+ this._managedNodes.add(inertNode);
202
+ }
203
+
204
+ /**
205
+ * Unregister the given node with this InertRoot and with InertManager.
206
+ * @param {!Node} node
207
+ */
208
+
209
+ }, {
210
+ key: '_unmanageNode',
211
+ value: function _unmanageNode(node) {
212
+ var inertNode = this._inertManager.deregister(node, this);
213
+ if (inertNode) {
214
+ this._managedNodes['delete'](inertNode);
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Unregister the entire subtree starting at `startNode`.
220
+ * @param {!Node} startNode
221
+ */
222
+
223
+ }, {
224
+ key: '_unmanageSubtree',
225
+ value: function _unmanageSubtree(startNode) {
226
+ var _this3 = this;
227
+
228
+ composedTreeWalk(startNode, function (node) {
229
+ return _this3._unmanageNode(node);
230
+ });
231
+ }
232
+
233
+ /**
234
+ * If a descendant node is found with an `inert` attribute, adopt its managed nodes.
235
+ * @param {!HTMLElement} node
236
+ */
237
+
238
+ }, {
239
+ key: '_adoptInertRoot',
240
+ value: function _adoptInertRoot(node) {
241
+ var inertSubroot = this._inertManager.getInertRoot(node);
242
+
243
+ // During initialisation this inert root may not have been registered yet,
244
+ // so register it now if need be.
245
+ if (!inertSubroot) {
246
+ this._inertManager.setInert(node, true);
247
+ inertSubroot = this._inertManager.getInertRoot(node);
248
+ }
249
+
250
+ inertSubroot.managedNodes.forEach(function (savedInertNode) {
251
+ this._manageNode(savedInertNode.node);
252
+ }, this);
253
+ }
254
+
255
+ /**
256
+ * Callback used when mutation observer detects subtree additions, removals, or attribute changes.
257
+ * @param {!Array<!MutationRecord>} records
258
+ * @param {!MutationObserver} self
259
+ */
260
+
261
+ }, {
262
+ key: '_onMutation',
263
+ value: function _onMutation(records, self) {
264
+ records.forEach(function (record) {
265
+ var target = /** @type {!HTMLElement} */record.target;
266
+ if (record.type === 'childList') {
267
+ // Manage added nodes
268
+ slice.call(record.addedNodes).forEach(function (node) {
269
+ this._makeSubtreeUnfocusable(node);
270
+ }, this);
271
+
272
+ // Un-manage removed nodes
273
+ slice.call(record.removedNodes).forEach(function (node) {
274
+ this._unmanageSubtree(node);
275
+ }, this);
276
+ } else if (record.type === 'attributes') {
277
+ if (record.attributeName === 'tabindex') {
278
+ // Re-initialise inert node if tabindex changes
279
+ this._manageNode(target);
280
+ } else if (target !== this._rootElement && record.attributeName === 'inert' && target.hasAttribute('inert')) {
281
+ // If a new inert root is added, adopt its managed nodes and make sure it knows about the
282
+ // already managed nodes from this inert subroot.
283
+ this._adoptInertRoot(target);
284
+ var inertSubroot = this._inertManager.getInertRoot(target);
285
+ this._managedNodes.forEach(function (managedNode) {
286
+ if (target.contains(managedNode.node)) {
287
+ inertSubroot._manageNode(managedNode.node);
288
+ }
289
+ });
290
+ }
291
+ }
292
+ }, this);
293
+ }
294
+ }, {
295
+ key: 'managedNodes',
296
+ get: function get() {
297
+ return new Set(this._managedNodes);
298
+ }
299
+
300
+ /** @return {boolean} */
301
+
302
+ }, {
303
+ key: 'hasSavedAriaHidden',
304
+ get: function get() {
305
+ return this._savedAriaHidden !== null;
306
+ }
307
+
308
+ /** @param {?string} ariaHidden */
309
+
310
+ }, {
311
+ key: 'savedAriaHidden',
312
+ set: function set(ariaHidden) {
313
+ this._savedAriaHidden = ariaHidden;
314
+ }
315
+
316
+ /** @return {?string} */
317
+ ,
318
+ get: function get() {
319
+ return this._savedAriaHidden;
320
+ }
321
+ }]);
322
+
323
+ return InertRoot;
324
+ }();
325
+
326
+ /**
327
+ * `InertNode` initialises and manages a single inert node.
328
+ * A node is inert if it is a descendant of one or more inert root elements.
329
+ *
330
+ * On construction, `InertNode` saves the existing `tabindex` value for the node, if any, and
331
+ * either removes the `tabindex` attribute or sets it to `-1`, depending on whether the element
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
+ */
340
+
341
+
342
+ var InertNode = function () {
343
+ /**
344
+ * @param {!Node} node A focusable element to be made inert.
345
+ * @param {!InertRoot} inertRoot The inert root element associated with this inert node.
346
+ */
347
+ function InertNode(node, inertRoot) {
348
+ _classCallCheck(this, InertNode);
349
+
350
+ /** @type {!Node} */
351
+ this._node = node;
352
+
353
+ /** @type {boolean} */
354
+ this._overrodeFocusMethod = false;
355
+
356
+ /**
357
+ * @type {!Set<!InertRoot>} The set of descendant inert roots.
358
+ * If and only if this set becomes empty, this node is no longer inert.
359
+ */
360
+ this._inertRoots = new Set([inertRoot]);
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
+ }
371
+
372
+ /**
373
+ * Call this whenever this object is about to become obsolete.
374
+ * This makes the managed node focusable again and deletes all of the previously stored state.
375
+ */
376
+
377
+
378
+ _createClass(InertNode, [{
379
+ key: 'destructor',
380
+ value: function destructor() {
381
+ this._throwIfDestroyed();
382
+
383
+ if (this._node && this._node.nodeType === Node.ELEMENT_NODE) {
384
+ var element = /** @type {!HTMLElement} */this._node;
385
+ if (this._savedTabIndex !== null) {
386
+ element.setAttribute('tabindex', this._savedTabIndex);
387
+ } else {
388
+ element.removeAttribute('tabindex');
389
+ }
390
+
391
+ // Use `delete` to restore native focus method.
392
+ if (this._overrodeFocusMethod) {
393
+ delete element.focus;
394
+ }
395
+ }
396
+
397
+ // See note in InertRoot.destructor for why we cast these nulls to ANY.
398
+ this._node = /** @type {?} */null;
399
+ this._inertRoots = /** @type {?} */null;
400
+ this._destroyed = true;
401
+ }
402
+
403
+ /**
404
+ * @type {boolean} Whether this object is obsolete because the managed node is no longer inert.
405
+ * If the object has been destroyed, any attempt to access it will cause an exception.
406
+ */
407
+
408
+ }, {
409
+ key: '_throwIfDestroyed',
410
+
411
+
412
+ /**
413
+ * Throw if user tries to access destroyed InertNode.
414
+ */
415
+ value: function _throwIfDestroyed() {
416
+ if (this.destroyed) {
417
+ throw new Error('Trying to access destroyed InertNode');
418
+ }
419
+ }
420
+
421
+ /** @return {boolean} */
422
+
423
+ }, {
424
+ key: 'ensureUntabbable',
425
+
426
+
427
+ /** Save the existing tabindex value and make the node untabbable and unfocusable */
428
+ value: function ensureUntabbable() {
429
+ if (this.node.nodeType !== Node.ELEMENT_NODE) {
430
+ return;
431
+ }
432
+ var element = /** @type {!HTMLElement} */this.node;
433
+ if (matches.call(element, _focusableElementsString)) {
434
+ if ( /** @type {!HTMLElement} */element.tabIndex === -1 && this.hasSavedTabIndex) {
435
+ return;
436
+ }
437
+
438
+ if (element.hasAttribute('tabindex')) {
439
+ this._savedTabIndex = /** @type {!HTMLElement} */element.tabIndex;
440
+ }
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
+ }
450
+ }
451
+
452
+ /**
453
+ * Add another inert root to this inert node's set of managing inert roots.
454
+ * @param {!InertRoot} inertRoot
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
+ */
470
+
471
+ }, {
472
+ key: 'removeInertRoot',
473
+ value: function removeInertRoot(inertRoot) {
474
+ this._throwIfDestroyed();
475
+ this._inertRoots['delete'](inertRoot);
476
+ if (this._inertRoots.size === 0) {
477
+ this.destructor();
478
+ }
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
+
494
+ }, {
495
+ key: 'node',
496
+ get: function get() {
497
+ this._throwIfDestroyed();
498
+ return this._node;
499
+ }
500
+
501
+ /** @param {?number} tabIndex */
502
+
503
+ }, {
504
+ key: 'savedTabIndex',
505
+ set: function set(tabIndex) {
506
+ this._throwIfDestroyed();
507
+ this._savedTabIndex = tabIndex;
508
+ }
509
+
510
+ /** @return {?number} */
511
+ ,
512
+ get: function get() {
513
+ this._throwIfDestroyed();
514
+ return this._savedTabIndex;
515
+ }
516
+ }]);
517
+
518
+ return InertNode;
519
+ }();
520
+
521
+ /**
522
+ * InertManager is a per-document singleton object which manages all inert roots and nodes.
523
+ *
524
+ * When an element becomes an inert root by having an `inert` attribute set and/or its `inert`
525
+ * property set to `true`, the `setInert` method creates an `InertRoot` object for the element.
526
+ * The `InertRoot` in turn registers itself as managing all of the element's focusable descendant
527
+ * nodes via the `register()` method. The `InertManager` ensures that a single `InertNode` instance
528
+ * is created for each such node, via the `_managedNodes` map.
529
+ */
530
+
531
+
532
+ var InertManager = function () {
533
+ /**
534
+ * @param {!Document} document
535
+ */
536
+ function InertManager(document) {
537
+ _classCallCheck(this, InertManager);
538
+
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
+
558
+ /**
559
+ * Observer for mutations on `document.body`.
560
+ * @type {!MutationObserver}
561
+ */
562
+ this._observer = new MutationObserver(this._watchForInert.bind(this));
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 {!HTMLElement} root
578
+ * @param {boolean} inert
579
+ */
580
+
581
+
582
+ _createClass(InertManager, [{
583
+ key: 'setInert',
584
+ value: function setInert(root, inert) {
585
+ if (inert) {
586
+ if (this._inertRoots.has(root)) {
587
+ // element is already inert
588
+ return;
589
+ }
590
+
591
+ var inertRoot = new InertRoot(root, this);
592
+ root.setAttribute('inert', '');
593
+ this._inertRoots.set(root, inertRoot);
594
+ // If not contained in the document, it must be in a shadowRoot.
595
+ // Ensure inert styles are added there.
596
+ if (!this._document.body.contains(root)) {
597
+ var parent = root.parentNode;
598
+ while (parent) {
599
+ if (parent.nodeType === 11) {
600
+ addInertStyle(parent);
601
+ }
602
+ parent = parent.parentNode;
603
+ }
604
+ }
605
+ } else {
606
+ if (!this._inertRoots.has(root)) {
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');
615
+ }
616
+ }
617
+
618
+ /**
619
+ * Get the InertRoot object corresponding to the given inert root element, if any.
620
+ * @param {!Node} element
621
+ * @return {!InertRoot|undefined}
622
+ */
623
+
624
+ }, {
625
+ key: 'getInertRoot',
626
+ value: function getInertRoot(element) {
627
+ return this._inertRoots.get(element);
628
+ }
629
+
630
+ /**
631
+ * Register the given InertRoot as managing the given node.
632
+ * In the case where the node has a previously existing inert root, this inert root will
633
+ * be added to its set of inert roots.
634
+ * @param {!Node} node
635
+ * @param {!InertRoot} inertRoot
636
+ * @return {!InertNode} inertNode
637
+ */
638
+
639
+ }, {
640
+ key: 'register',
641
+ value: function register(node, inertRoot) {
642
+ var inertNode = this._managedNodes.get(node);
643
+ if (inertNode !== undefined) {
644
+ // node was already in an inert subtree
645
+ inertNode.addInertRoot(inertRoot);
646
+ } else {
647
+ inertNode = new InertNode(node, inertRoot);
648
+ }
649
+
650
+ this._managedNodes.set(node, inertNode);
651
+
652
+ return inertNode;
653
+ }
654
+
655
+ /**
656
+ * De-register the given InertRoot as managing the given inert node.
657
+ * Removes the inert root from the InertNode's set of managing inert roots, and remove the inert
658
+ * node from the InertManager's set of managed nodes if it is destroyed.
659
+ * If the node is not currently managed, this is essentially a no-op.
660
+ * @param {!Node} node
661
+ * @param {!InertRoot} inertRoot
662
+ * @return {?InertNode} The potentially destroyed InertNode associated with this node, if any.
663
+ */
664
+
665
+ }, {
666
+ key: 'deregister',
667
+ value: function deregister(node, inertRoot) {
668
+ var inertNode = this._managedNodes.get(node);
669
+ if (!inertNode) {
670
+ return null;
671
+ }
672
+
673
+ inertNode.removeInertRoot(inertRoot);
674
+ if (inertNode.destroyed) {
675
+ this._managedNodes['delete'](node);
676
+ }
677
+
678
+ return inertNode;
679
+ }
680
+
681
+ /**
682
+ * Callback used when document has finished loading.
683
+ */
684
+
685
+ }, {
686
+ key: '_onDocumentLoaded',
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 || this._document.documentElement, { attributes: true, subtree: true, childList: true });
696
+ }
697
+
698
+ /**
699
+ * Callback used when mutation observer detects attribute changes.
700
+ * @param {!Array<!MutationRecord>} records
701
+ * @param {!MutationObserver} self
702
+ */
703
+
704
+ }, {
705
+ key: '_watchForInert',
706
+ value: function _watchForInert(records, self) {
707
+ var _this = this;
708
+ records.forEach(function (record) {
709
+ switch (record.type) {
710
+ case 'childList':
711
+ slice.call(record.addedNodes).forEach(function (node) {
712
+ if (node.nodeType !== Node.ELEMENT_NODE) {
713
+ return;
714
+ }
715
+ var inertElements = slice.call(node.querySelectorAll('[inert]'));
716
+ if (matches.call(node, '[inert]')) {
717
+ inertElements.unshift(node);
718
+ }
719
+ inertElements.forEach(function (inertElement) {
720
+ this.setInert(inertElement, true);
721
+ }, _this);
722
+ }, _this);
723
+ break;
724
+ case 'attributes':
725
+ if (record.attributeName !== 'inert') {
726
+ return;
727
+ }
728
+ var target = /** @type {!HTMLElement} */record.target;
729
+ var inert = target.hasAttribute('inert');
730
+ _this.setInert(target, inert);
731
+ break;
732
+ }
733
+ }, this);
734
+ }
735
+ }]);
736
+
737
+ return InertManager;
738
+ }();
739
+
740
+ /**
741
+ * Recursively walk the composed tree from |node|.
742
+ * @param {!Node} node
743
+ * @param {(function (!HTMLElement))=} callback Callback to be called for each element traversed,
744
+ * before descending into child nodes.
745
+ * @param {?ShadowRoot=} shadowRootAncestor The nearest ShadowRoot ancestor, if any.
746
+ */
747
+
748
+
749
+ function composedTreeWalk(node, callback, shadowRootAncestor) {
750
+ if (node.nodeType == Node.ELEMENT_NODE) {
751
+ var element = /** @type {!HTMLElement} */node;
752
+ if (callback) {
753
+ callback(element);
754
+ }
755
+
756
+ // Descend into node:
757
+ // If it has a ShadowRoot, ignore all child elements - these will be picked
758
+ // up by the <content> or <shadow> elements. Descend straight into the
759
+ // ShadowRoot.
760
+ var shadowRoot = /** @type {!HTMLElement} */element.shadowRoot;
761
+ if (shadowRoot) {
762
+ composedTreeWalk(shadowRoot, callback, shadowRoot);
763
+ return;
764
+ }
765
+
766
+ // If it is a <content> element, descend into distributed elements - these
767
+ // are elements from outside the shadow root which are rendered inside the
768
+ // shadow DOM.
769
+ if (element.localName == 'content') {
770
+ var content = /** @type {!HTMLContentElement} */element;
771
+ // Verifies if ShadowDom v0 is supported.
772
+ var distributedNodes = content.getDistributedNodes ? content.getDistributedNodes() : [];
773
+ for (var i = 0; i < distributedNodes.length; i++) {
774
+ composedTreeWalk(distributedNodes[i], callback, shadowRootAncestor);
775
+ }
776
+ return;
777
+ }
778
+
779
+ // If it is a <slot> element, descend into assigned nodes - these
780
+ // are elements from outside the shadow root which are rendered inside the
781
+ // shadow DOM.
782
+ if (element.localName == 'slot') {
783
+ var slot = /** @type {!HTMLSlotElement} */element;
784
+ // Verify if ShadowDom v1 is supported.
785
+ var _distributedNodes = slot.assignedNodes ? slot.assignedNodes({ flatten: true }) : [];
786
+ for (var _i = 0; _i < _distributedNodes.length; _i++) {
787
+ composedTreeWalk(_distributedNodes[_i], callback, shadowRootAncestor);
788
+ }
789
+ return;
790
+ }
791
+ }
792
+
793
+ // If it is neither the parent of a ShadowRoot, a <content> element, a <slot>
794
+ // element, nor a <shadow> element recurse normally.
795
+ var child = node.firstChild;
796
+ while (child != null) {
797
+ composedTreeWalk(child, callback, shadowRootAncestor);
798
+ child = child.nextSibling;
799
+ }
800
+ }
801
+
802
+ /**
803
+ * Adds a style element to the node containing the inert specific styles
804
+ * @param {!Node} node
805
+ */
806
+ function addInertStyle(node) {
807
+ if (node.querySelector('style#inert-style, link#inert-style')) {
808
+ return;
809
+ }
810
+ var style = document.createElement('style');
811
+ style.setAttribute('id', 'inert-style');
812
+ 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';
813
+ node.appendChild(style);
814
+ }
815
+
816
+ if (!HTMLElement.prototype.hasOwnProperty('inert')) {
817
+ /** @type {!InertManager} */
818
+ var inertManager = new InertManager(document);
819
+
820
+ Object.defineProperty(HTMLElement.prototype, 'inert', {
821
+ enumerable: true,
822
+ /** @this {!HTMLElement} */
823
+ get: function get() {
824
+ return this.hasAttribute('inert');
825
+ },
826
+ /** @this {!HTMLElement} */
827
+ set: function set(inert) {
828
+ inertManager.setInert(this, inert);
829
+ }
830
+ });
831
+ }
832
+ })();