Spaces:
Running
Running
(function (global, factory) { | |
typeof exports === 'object' && typeof module !== 'undefined' ? factory() : | |
typeof define === 'function' && define.amd ? define('inert', factory) : | |
(factory()); | |
}(this, (function () { 'use strict'; | |
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; }; }(); | |
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | |
/** | |
* This work is licensed under the W3C Software and Document License | |
* (http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). | |
*/ | |
(function () { | |
// Return early if we're not running inside of the browser. | |
if (typeof window === 'undefined') { | |
return; | |
} | |
// Convenience function for converting NodeLists. | |
/** @type {typeof Array.prototype.slice} */ | |
var slice = Array.prototype.slice; | |
/** | |
* IE has a non-standard name for "matches". | |
* @type {typeof Element.prototype.matches} | |
*/ | |
var matches = Element.prototype.matches || Element.prototype.msMatchesSelector; | |
/** @type {string} */ | |
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(','); | |
/** | |
* `InertRoot` manages a single inert subtree, i.e. a DOM subtree whose root element has an `inert` | |
* attribute. | |
* | |
* Its main functions are: | |
* | |
* - to create and maintain a set of managed `InertNode`s, including when mutations occur in the | |
* subtree. The `makeSubtreeUnfocusable()` method handles collecting `InertNode`s via registering | |
* each focusable node in the subtree with the singleton `InertManager` which manages all known | |
* focusable nodes within inert subtrees. `InertManager` ensures that a single `InertNode` | |
* instance exists for each focusable node which has at least one inert root as an ancestor. | |
* | |
* - to notify all managed `InertNode`s when this subtree stops being inert (i.e. when the `inert` | |
* attribute is removed from the root node). This is handled in the destructor, which calls the | |
* `deregister` method on `InertManager` for each managed inert node. | |
*/ | |
var InertRoot = function () { | |
/** | |
* @param {!HTMLElement} rootElement The HTMLElement at the root of the inert subtree. | |
* @param {!InertManager} inertManager The global singleton InertManager object. | |
*/ | |
function InertRoot(rootElement, inertManager) { | |
_classCallCheck(this, InertRoot); | |
/** @type {!InertManager} */ | |
this._inertManager = inertManager; | |
/** @type {!HTMLElement} */ | |
this._rootElement = rootElement; | |
/** | |
* @type {!Set<!InertNode>} | |
* All managed focusable nodes in this InertRoot's subtree. | |
*/ | |
this._managedNodes = new Set(); | |
// Make the subtree hidden from assistive technology | |
if (this._rootElement.hasAttribute('aria-hidden')) { | |
/** @type {?string} */ | |
this._savedAriaHidden = this._rootElement.getAttribute('aria-hidden'); | |
} else { | |
this._savedAriaHidden = null; | |
} | |
this._rootElement.setAttribute('aria-hidden', 'true'); | |
// Make all focusable elements in the subtree unfocusable and add them to _managedNodes | |
this._makeSubtreeUnfocusable(this._rootElement); | |
// Watch for: | |
// - any additions in the subtree: make them unfocusable too | |
// - any removals from the subtree: remove them from this inert root's managed nodes | |
// - attribute changes: if `tabindex` is added, or removed from an intrinsically focusable | |
// element, make that node a managed node. | |
this._observer = new MutationObserver(this._onMutation.bind(this)); | |
this._observer.observe(this._rootElement, { attributes: true, childList: true, subtree: true }); | |
} | |
/** | |
* Call this whenever this object is about to become obsolete. This unwinds all of the state | |
* stored in this object and updates the state of all of the managed nodes. | |
*/ | |
_createClass(InertRoot, [{ | |
key: 'destructor', | |
value: function destructor() { | |
this._observer.disconnect(); | |
if (this._rootElement) { | |
if (this._savedAriaHidden !== null) { | |
this._rootElement.setAttribute('aria-hidden', this._savedAriaHidden); | |
} else { | |
this._rootElement.removeAttribute('aria-hidden'); | |
} | |
} | |
this._managedNodes.forEach(function (inertNode) { | |
this._unmanageNode(inertNode.node); | |
}, this); | |
// Note we cast the nulls to the ANY type here because: | |
// 1) We want the class properties to be declared as non-null, or else we | |
// need even more casts throughout this code. All bets are off if an | |
// instance has been destroyed and a method is called. | |
// 2) We don't want to cast "this", because we want type-aware optimizations | |
// to know which properties we're setting. | |
this._observer = /** @type {?} */null; | |
this._rootElement = /** @type {?} */null; | |
this._managedNodes = /** @type {?} */null; | |
this._inertManager = /** @type {?} */null; | |
} | |
/** | |
* @return {!Set<!InertNode>} A copy of this InertRoot's managed nodes set. | |
*/ | |
}, { | |
key: '_makeSubtreeUnfocusable', | |
/** | |
* @param {!Node} startNode | |
*/ | |
value: function _makeSubtreeUnfocusable(startNode) { | |
var _this2 = this; | |
composedTreeWalk(startNode, function (node) { | |
return _this2._visitNode(node); | |
}); | |
var activeElement = document.activeElement; | |
if (!document.body.contains(startNode)) { | |
// startNode may be in shadow DOM, so find its nearest shadowRoot to get the activeElement. | |
var node = startNode; | |
/** @type {!ShadowRoot|undefined} */ | |
var root = undefined; | |
while (node) { | |
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { | |
root = /** @type {!ShadowRoot} */node; | |
break; | |
} | |
node = node.parentNode; | |
} | |
if (root) { | |
activeElement = root.activeElement; | |
} | |
} | |
if (startNode.contains(activeElement)) { | |
activeElement.blur(); | |
// In IE11, if an element is already focused, and then set to tabindex=-1 | |
// calling blur() will not actually move the focus. | |
// To work around this we call focus() on the body instead. | |
if (activeElement === document.activeElement) { | |
document.body.focus(); | |
} | |
} | |
} | |
/** | |
* @param {!Node} node | |
*/ | |
}, { | |
key: '_visitNode', | |
value: function _visitNode(node) { | |
if (node.nodeType !== Node.ELEMENT_NODE) { | |
return; | |
} | |
var element = /** @type {!HTMLElement} */node; | |
// If a descendant inert root becomes un-inert, its descendants will still be inert because of | |
// this inert root, so all of its managed nodes need to be adopted by this InertRoot. | |
if (element !== this._rootElement && element.hasAttribute('inert')) { | |
this._adoptInertRoot(element); | |
} | |
if (matches.call(element, _focusableElementsString) || element.hasAttribute('tabindex')) { | |
this._manageNode(element); | |
} | |
} | |
/** | |
* Register the given node with this InertRoot and with InertManager. | |
* @param {!Node} node | |
*/ | |
}, { | |
key: '_manageNode', | |
value: function _manageNode(node) { | |
var inertNode = this._inertManager.register(node, this); | |
this._managedNodes.add(inertNode); | |
} | |
/** | |
* Unregister the given node with this InertRoot and with InertManager. | |
* @param {!Node} node | |
*/ | |
}, { | |
key: '_unmanageNode', | |
value: function _unmanageNode(node) { | |
var inertNode = this._inertManager.deregister(node, this); | |
if (inertNode) { | |
this._managedNodes['delete'](inertNode); | |
} | |
} | |
/** | |
* Unregister the entire subtree starting at `startNode`. | |
* @param {!Node} startNode | |
*/ | |
}, { | |
key: '_unmanageSubtree', | |
value: function _unmanageSubtree(startNode) { | |
var _this3 = this; | |
composedTreeWalk(startNode, function (node) { | |
return _this3._unmanageNode(node); | |
}); | |
} | |
/** | |
* If a descendant node is found with an `inert` attribute, adopt its managed nodes. | |
* @param {!HTMLElement} node | |
*/ | |
}, { | |
key: '_adoptInertRoot', | |
value: function _adoptInertRoot(node) { | |
var inertSubroot = this._inertManager.getInertRoot(node); | |
// During initialisation this inert root may not have been registered yet, | |
// so register it now if need be. | |
if (!inertSubroot) { | |
this._inertManager.setInert(node, true); | |
inertSubroot = this._inertManager.getInertRoot(node); | |
} | |
inertSubroot.managedNodes.forEach(function (savedInertNode) { | |
this._manageNode(savedInertNode.node); | |
}, this); | |
} | |
/** | |
* Callback used when mutation observer detects subtree additions, removals, or attribute changes. | |
* @param {!Array<!MutationRecord>} records | |
* @param {!MutationObserver} self | |
*/ | |
}, { | |
key: '_onMutation', | |
value: function _onMutation(records, self) { | |
records.forEach(function (record) { | |
var target = /** @type {!HTMLElement} */record.target; | |
if (record.type === 'childList') { | |
// Manage added nodes | |
slice.call(record.addedNodes).forEach(function (node) { | |
this._makeSubtreeUnfocusable(node); | |
}, this); | |
// Un-manage removed nodes | |
slice.call(record.removedNodes).forEach(function (node) { | |
this._unmanageSubtree(node); | |
}, this); | |
} else if (record.type === 'attributes') { | |
if (record.attributeName === 'tabindex') { | |
// Re-initialise inert node if tabindex changes | |
this._manageNode(target); | |
} else if (target !== this._rootElement && record.attributeName === 'inert' && target.hasAttribute('inert')) { | |
// If a new inert root is added, adopt its managed nodes and make sure it knows about the | |
// already managed nodes from this inert subroot. | |
this._adoptInertRoot(target); | |
var inertSubroot = this._inertManager.getInertRoot(target); | |
this._managedNodes.forEach(function (managedNode) { | |
if (target.contains(managedNode.node)) { | |
inertSubroot._manageNode(managedNode.node); | |
} | |
}); | |
} | |
} | |
}, this); | |
} | |
}, { | |
key: 'managedNodes', | |
get: function get() { | |
return new Set(this._managedNodes); | |
} | |
/** @return {boolean} */ | |
}, { | |
key: 'hasSavedAriaHidden', | |
get: function get() { | |
return this._savedAriaHidden !== null; | |
} | |
/** @param {?string} ariaHidden */ | |
}, { | |
key: 'savedAriaHidden', | |
set: function set(ariaHidden) { | |
this._savedAriaHidden = ariaHidden; | |
} | |
/** @return {?string} */ | |
, | |
get: function get() { | |
return this._savedAriaHidden; | |
} | |
}]); | |
return InertRoot; | |
}(); | |
/** | |
* `InertNode` initialises and manages a single inert node. | |
* A node is inert if it is a descendant of one or more inert root elements. | |
* | |
* On construction, `InertNode` saves the existing `tabindex` value for the node, if any, and | |
* either removes the `tabindex` attribute or sets it to `-1`, depending on whether the element | |
* is intrinsically focusable or not. | |
* | |
* `InertNode` maintains a set of `InertRoot`s which are descendants of this `InertNode`. When an | |
* `InertRoot` is destroyed, and calls `InertManager.deregister()`, the `InertManager` notifies the | |
* `InertNode` via `removeInertRoot()`, which in turn destroys the `InertNode` if no `InertRoot`s | |
* remain in the set. On destruction, `InertNode` reinstates the stored `tabindex` if one exists, | |
* or removes the `tabindex` attribute if the element is intrinsically focusable. | |
*/ | |
var InertNode = function () { | |
/** | |
* @param {!Node} node A focusable element to be made inert. | |
* @param {!InertRoot} inertRoot The inert root element associated with this inert node. | |
*/ | |
function InertNode(node, inertRoot) { | |
_classCallCheck(this, InertNode); | |
/** @type {!Node} */ | |
this._node = node; | |
/** @type {boolean} */ | |
this._overrodeFocusMethod = false; | |
/** | |
* @type {!Set<!InertRoot>} The set of descendant inert roots. | |
* If and only if this set becomes empty, this node is no longer inert. | |
*/ | |
this._inertRoots = new Set([inertRoot]); | |
/** @type {?number} */ | |
this._savedTabIndex = null; | |
/** @type {boolean} */ | |
this._destroyed = false; | |
// Save any prior tabindex info and make this node untabbable | |
this.ensureUntabbable(); | |
} | |
/** | |
* Call this whenever this object is about to become obsolete. | |
* This makes the managed node focusable again and deletes all of the previously stored state. | |
*/ | |
_createClass(InertNode, [{ | |
key: 'destructor', | |
value: function destructor() { | |
this._throwIfDestroyed(); | |
if (this._node && this._node.nodeType === Node.ELEMENT_NODE) { | |
var element = /** @type {!HTMLElement} */this._node; | |
if (this._savedTabIndex !== null) { | |
element.setAttribute('tabindex', this._savedTabIndex); | |
} else { | |
element.removeAttribute('tabindex'); | |
} | |
// Use `delete` to restore native focus method. | |
if (this._overrodeFocusMethod) { | |
delete element.focus; | |
} | |
} | |
// See note in InertRoot.destructor for why we cast these nulls to ANY. | |
this._node = /** @type {?} */null; | |
this._inertRoots = /** @type {?} */null; | |
this._destroyed = true; | |
} | |
/** | |
* @type {boolean} Whether this object is obsolete because the managed node is no longer inert. | |
* If the object has been destroyed, any attempt to access it will cause an exception. | |
*/ | |
}, { | |
key: '_throwIfDestroyed', | |
/** | |
* Throw if user tries to access destroyed InertNode. | |
*/ | |
value: function _throwIfDestroyed() { | |
if (this.destroyed) { | |
throw new Error('Trying to access destroyed InertNode'); | |
} | |
} | |
/** @return {boolean} */ | |
}, { | |
key: 'ensureUntabbable', | |
/** Save the existing tabindex value and make the node untabbable and unfocusable */ | |
value: function ensureUntabbable() { | |
if (this.node.nodeType !== Node.ELEMENT_NODE) { | |
return; | |
} | |
var element = /** @type {!HTMLElement} */this.node; | |
if (matches.call(element, _focusableElementsString)) { | |
if ( /** @type {!HTMLElement} */element.tabIndex === -1 && this.hasSavedTabIndex) { | |
return; | |
} | |
if (element.hasAttribute('tabindex')) { | |
this._savedTabIndex = /** @type {!HTMLElement} */element.tabIndex; | |
} | |
element.setAttribute('tabindex', '-1'); | |
if (element.nodeType === Node.ELEMENT_NODE) { | |
element.focus = function () {}; | |
this._overrodeFocusMethod = true; | |
} | |
} else if (element.hasAttribute('tabindex')) { | |
this._savedTabIndex = /** @type {!HTMLElement} */element.tabIndex; | |
element.removeAttribute('tabindex'); | |
} | |
} | |
/** | |
* Add another inert root to this inert node's set of managing inert roots. | |
* @param {!InertRoot} inertRoot | |
*/ | |
}, { | |
key: 'addInertRoot', | |
value: function addInertRoot(inertRoot) { | |
this._throwIfDestroyed(); | |
this._inertRoots.add(inertRoot); | |
} | |
/** | |
* Remove the given inert root from this inert node's set of managing inert roots. | |
* If the set of managing inert roots becomes empty, this node is no longer inert, | |
* so the object should be destroyed. | |
* @param {!InertRoot} inertRoot | |
*/ | |
}, { | |
key: 'removeInertRoot', | |
value: function removeInertRoot(inertRoot) { | |
this._throwIfDestroyed(); | |
this._inertRoots['delete'](inertRoot); | |
if (this._inertRoots.size === 0) { | |
this.destructor(); | |
} | |
} | |
}, { | |
key: 'destroyed', | |
get: function get() { | |
return (/** @type {!InertNode} */this._destroyed | |
); | |
} | |
}, { | |
key: 'hasSavedTabIndex', | |
get: function get() { | |
return this._savedTabIndex !== null; | |
} | |
/** @return {!Node} */ | |
}, { | |
key: 'node', | |
get: function get() { | |
this._throwIfDestroyed(); | |
return this._node; | |
} | |
/** @param {?number} tabIndex */ | |
}, { | |
key: 'savedTabIndex', | |
set: function set(tabIndex) { | |
this._throwIfDestroyed(); | |
this._savedTabIndex = tabIndex; | |
} | |
/** @return {?number} */ | |
, | |
get: function get() { | |
this._throwIfDestroyed(); | |
return this._savedTabIndex; | |
} | |
}]); | |
return InertNode; | |
}(); | |
/** | |
* InertManager is a per-document singleton object which manages all inert roots and nodes. | |
* | |
* When an element becomes an inert root by having an `inert` attribute set and/or its `inert` | |
* property set to `true`, the `setInert` method creates an `InertRoot` object for the element. | |
* The `InertRoot` in turn registers itself as managing all of the element's focusable descendant | |
* nodes via the `register()` method. The `InertManager` ensures that a single `InertNode` instance | |
* is created for each such node, via the `_managedNodes` map. | |
*/ | |
var InertManager = function () { | |
/** | |
* @param {!Document} document | |
*/ | |
function InertManager(document) { | |
_classCallCheck(this, InertManager); | |
if (!document) { | |
throw new Error('Missing required argument; InertManager needs to wrap a document.'); | |
} | |
/** @type {!Document} */ | |
this._document = document; | |
/** | |
* All managed nodes known to this InertManager. In a map to allow looking up by Node. | |
* @type {!Map<!Node, !InertNode>} | |
*/ | |
this._managedNodes = new Map(); | |
/** | |
* All inert roots known to this InertManager. In a map to allow looking up by Node. | |
* @type {!Map<!Node, !InertRoot>} | |
*/ | |
this._inertRoots = new Map(); | |
/** | |
* Observer for mutations on `document.body`. | |
* @type {!MutationObserver} | |
*/ | |
this._observer = new MutationObserver(this._watchForInert.bind(this)); | |
// Add inert style. | |
addInertStyle(document.head || document.body || document.documentElement); | |
// Wait for document to be loaded. | |
if (document.readyState === 'loading') { | |
document.addEventListener('DOMContentLoaded', this._onDocumentLoaded.bind(this)); | |
} else { | |
this._onDocumentLoaded(); | |
} | |
} | |
/** | |
* Set whether the given element should be an inert root or not. | |
* @param {!HTMLElement} root | |
* @param {boolean} inert | |
*/ | |
_createClass(InertManager, [{ | |
key: 'setInert', | |
value: function setInert(root, inert) { | |
if (inert) { | |
if (this._inertRoots.has(root)) { | |
// element is already inert | |
return; | |
} | |
var inertRoot = new InertRoot(root, this); | |
root.setAttribute('inert', ''); | |
this._inertRoots.set(root, inertRoot); | |
// If not contained in the document, it must be in a shadowRoot. | |
// Ensure inert styles are added there. | |
if (!this._document.body.contains(root)) { | |
var parent = root.parentNode; | |
while (parent) { | |
if (parent.nodeType === 11) { | |
addInertStyle(parent); | |
} | |
parent = parent.parentNode; | |
} | |
} | |
} else { | |
if (!this._inertRoots.has(root)) { | |
// element is already non-inert | |
return; | |
} | |
var _inertRoot = this._inertRoots.get(root); | |
_inertRoot.destructor(); | |
this._inertRoots['delete'](root); | |
root.removeAttribute('inert'); | |
} | |
} | |
/** | |
* Get the InertRoot object corresponding to the given inert root element, if any. | |
* @param {!Node} element | |
* @return {!InertRoot|undefined} | |
*/ | |
}, { | |
key: 'getInertRoot', | |
value: function getInertRoot(element) { | |
return this._inertRoots.get(element); | |
} | |
/** | |
* Register the given InertRoot as managing the given node. | |
* In the case where the node has a previously existing inert root, this inert root will | |
* be added to its set of inert roots. | |
* @param {!Node} node | |
* @param {!InertRoot} inertRoot | |
* @return {!InertNode} inertNode | |
*/ | |
}, { | |
key: 'register', | |
value: function register(node, inertRoot) { | |
var inertNode = this._managedNodes.get(node); | |
if (inertNode !== undefined) { | |
// node was already in an inert subtree | |
inertNode.addInertRoot(inertRoot); | |
} else { | |
inertNode = new InertNode(node, inertRoot); | |
} | |
this._managedNodes.set(node, inertNode); | |
return inertNode; | |
} | |
/** | |
* De-register the given InertRoot as managing the given inert node. | |
* Removes the inert root from the InertNode's set of managing inert roots, and remove the inert | |
* node from the InertManager's set of managed nodes if it is destroyed. | |
* If the node is not currently managed, this is essentially a no-op. | |
* @param {!Node} node | |
* @param {!InertRoot} inertRoot | |
* @return {?InertNode} The potentially destroyed InertNode associated with this node, if any. | |
*/ | |
}, { | |
key: 'deregister', | |
value: function deregister(node, inertRoot) { | |
var inertNode = this._managedNodes.get(node); | |
if (!inertNode) { | |
return null; | |
} | |
inertNode.removeInertRoot(inertRoot); | |
if (inertNode.destroyed) { | |
this._managedNodes['delete'](node); | |
} | |
return inertNode; | |
} | |
/** | |
* Callback used when document has finished loading. | |
*/ | |
}, { | |
key: '_onDocumentLoaded', | |
value: function _onDocumentLoaded() { | |
// Find all inert roots in document and make them actually inert. | |
var inertElements = slice.call(this._document.querySelectorAll('[inert]')); | |
inertElements.forEach(function (inertElement) { | |
this.setInert(inertElement, true); | |
}, this); | |
// Comment this out to use programmatic API only. | |
this._observer.observe(this._document.body || this._document.documentElement, { attributes: true, subtree: true, childList: true }); | |
} | |
/** | |
* Callback used when mutation observer detects attribute changes. | |
* @param {!Array<!MutationRecord>} records | |
* @param {!MutationObserver} self | |
*/ | |
}, { | |
key: '_watchForInert', | |
value: function _watchForInert(records, self) { | |
var _this = this; | |
records.forEach(function (record) { | |
switch (record.type) { | |
case 'childList': | |
slice.call(record.addedNodes).forEach(function (node) { | |
if (node.nodeType !== Node.ELEMENT_NODE) { | |
return; | |
} | |
var inertElements = slice.call(node.querySelectorAll('[inert]')); | |
if (matches.call(node, '[inert]')) { | |
inertElements.unshift(node); | |
} | |
inertElements.forEach(function (inertElement) { | |
this.setInert(inertElement, true); | |
}, _this); | |
}, _this); | |
break; | |
case 'attributes': | |
if (record.attributeName !== 'inert') { | |
return; | |
} | |
var target = /** @type {!HTMLElement} */record.target; | |
var inert = target.hasAttribute('inert'); | |
_this.setInert(target, inert); | |
break; | |
} | |
}, this); | |
} | |
}]); | |
return InertManager; | |
}(); | |
/** | |
* Recursively walk the composed tree from |node|. | |
* @param {!Node} node | |
* @param {(function (!HTMLElement))=} callback Callback to be called for each element traversed, | |
* before descending into child nodes. | |
* @param {?ShadowRoot=} shadowRootAncestor The nearest ShadowRoot ancestor, if any. | |
*/ | |
function composedTreeWalk(node, callback, shadowRootAncestor) { | |
if (node.nodeType == Node.ELEMENT_NODE) { | |
var element = /** @type {!HTMLElement} */node; | |
if (callback) { | |
callback(element); | |
} | |
// Descend into node: | |
// If it has a ShadowRoot, ignore all child elements - these will be picked | |
// up by the <content> or <shadow> elements. Descend straight into the | |
// ShadowRoot. | |
var shadowRoot = /** @type {!HTMLElement} */element.shadowRoot; | |
if (shadowRoot) { | |
composedTreeWalk(shadowRoot, callback, shadowRoot); | |
return; | |
} | |
// If it is a <content> element, descend into distributed elements - these | |
// are elements from outside the shadow root which are rendered inside the | |
// shadow DOM. | |
if (element.localName == 'content') { | |
var content = /** @type {!HTMLContentElement} */element; | |
// Verifies if ShadowDom v0 is supported. | |
var distributedNodes = content.getDistributedNodes ? content.getDistributedNodes() : []; | |
for (var i = 0; i < distributedNodes.length; i++) { | |
composedTreeWalk(distributedNodes[i], callback, shadowRootAncestor); | |
} | |
return; | |
} | |
// If it is a <slot> element, descend into assigned nodes - these | |
// are elements from outside the shadow root which are rendered inside the | |
// shadow DOM. | |
if (element.localName == 'slot') { | |
var slot = /** @type {!HTMLSlotElement} */element; | |
// Verify if ShadowDom v1 is supported. | |
var _distributedNodes = slot.assignedNodes ? slot.assignedNodes({ flatten: true }) : []; | |
for (var _i = 0; _i < _distributedNodes.length; _i++) { | |
composedTreeWalk(_distributedNodes[_i], callback, shadowRootAncestor); | |
} | |
return; | |
} | |
} | |
// If it is neither the parent of a ShadowRoot, a <content> element, a <slot> | |
// element, nor a <shadow> element recurse normally. | |
var child = node.firstChild; | |
while (child != null) { | |
composedTreeWalk(child, callback, shadowRootAncestor); | |
child = child.nextSibling; | |
} | |
} | |
/** | |
* Adds a style element to the node containing the inert specific styles | |
* @param {!Node} node | |
*/ | |
function addInertStyle(node) { | |
if (node.querySelector('style#inert-style, link#inert-style')) { | |
return; | |
} | |
var style = document.createElement('style'); | |
style.setAttribute('id', 'inert-style'); | |
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'; | |
node.appendChild(style); | |
} | |
if (!HTMLElement.prototype.hasOwnProperty('inert')) { | |
/** @type {!InertManager} */ | |
var inertManager = new InertManager(document); | |
Object.defineProperty(HTMLElement.prototype, 'inert', { | |
enumerable: true, | |
/** @this {!HTMLElement} */ | |
get: function get() { | |
return this.hasAttribute('inert'); | |
}, | |
/** @this {!HTMLElement} */ | |
set: function set(inert) { | |
inertManager.setInert(this, inert); | |
} | |
}); | |
} | |
})(); | |
}))); | |