{"version":3,"file":"a11y.es5.js","sources":["../../packages/cdk/esm5/a11y/list-key-manager.js","../../packages/cdk/esm5/a11y/activedescendant-key-manager.js","../../packages/cdk/esm5/a11y/aria-reference.js","../../packages/cdk/esm5/a11y/aria-describer.js","../../packages/cdk/esm5/a11y/fake-mousedown.js","../../packages/cdk/esm5/a11y/focus-key-manager.js","../../packages/cdk/esm5/a11y/interactivity-checker.js","../../packages/cdk/esm5/a11y/focus-trap.js","../../packages/cdk/esm5/a11y/live-announcer.js","../../packages/cdk/esm5/a11y/focus-monitor.js","../../packages/cdk/esm5/a11y/a11y-module.js","../../packages/cdk/esm5/a11y/index.js"],"sourcesContent":["/**\n * @license\n * Copyright Google Inc. All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport { Subject } from 'rxjs/Subject';\nimport { Subscription } from 'rxjs/Subscription';\nimport { UP_ARROW, DOWN_ARROW, TAB, A, Z, ZERO, NINE } from '@angular/cdk/keycodes';\nimport { RxChain, debounceTime, filter, map, doOperator } from '@angular/cdk/rxjs';\n/**\n * This class manages keyboard events for selectable lists. If you pass it a query list\n * of items, it will set the active item correctly when arrow events occur.\n */\nvar ListKeyManager = (function () {\n /**\n * @param {?} _items\n */\n function ListKeyManager(_items) {\n this._items = _items;\n this._activeItemIndex = -1;\n this._wrap = false;\n this._letterKeyStream = new Subject();\n this._typeaheadSubscription = Subscription.EMPTY;\n this._pressedLetters = [];\n /**\n * Stream that emits any time the TAB key is pressed, so components can react\n * when focus is shifted off of the list.\n */\n this.tabOut = new Subject();\n }\n /**\n * Turns on wrapping mode, which ensures that the active item will wrap to\n * the other end of list when there are no more items in the given direction.\n * @return {?}\n */\n ListKeyManager.prototype.withWrap = function () {\n this._wrap = true;\n return this;\n };\n /**\n * Turns on typeahead mode which allows users to set the active item by typing.\n * @param {?=} debounceInterval Time to wait after the last keystroke before setting the active item.\n * @return {?}\n */\n ListKeyManager.prototype.withTypeAhead = function (debounceInterval) {\n var _this = this;\n if (debounceInterval === void 0) { debounceInterval = 200; }\n if (this._items.length && this._items.some(function (item) { return typeof item.getLabel !== 'function'; })) {\n throw Error('ListKeyManager items in typeahead mode must implement the `getLabel` method.');\n }\n this._typeaheadSubscription.unsubscribe();\n // Debounce the presses of non-navigational keys, collect the ones that correspond to letters\n // and convert those letters back into a string. Afterwards find the first item that starts\n // with that string and select it.\n this._typeaheadSubscription = RxChain.from(this._letterKeyStream)\n .call(doOperator, function (keyCode) { return _this._pressedLetters.push(keyCode); })\n .call(debounceTime, debounceInterval)\n .call(filter, function () { return _this._pressedLetters.length > 0; })\n .call(map, function () { return _this._pressedLetters.join(''); })\n .subscribe(function (inputString) {\n var /** @type {?} */ items = _this._items.toArray();\n // Start at 1 because we want to start searching at the item immediately\n // following the current active item.\n for (var /** @type {?} */ i = 1; i < items.length + 1; i++) {\n var /** @type {?} */ index = (_this._activeItemIndex + i) % items.length;\n var /** @type {?} */ item = items[index];\n if (!item.disabled && ((item.getLabel))().toUpperCase().trim().indexOf(inputString) === 0) {\n _this.setActiveItem(index);\n break;\n }\n }\n _this._pressedLetters = [];\n });\n return this;\n };\n /**\n * Sets the active item to the item at the index specified.\n * @param {?} index The index of the item to be set as active.\n * @return {?}\n */\n ListKeyManager.prototype.setActiveItem = function (index) {\n this._activeItemIndex = index;\n this._activeItem = this._items.toArray()[index];\n };\n /**\n * Sets the active item depending on the key event passed in.\n * @param {?} event Keyboard event to be used for determining which element should be active.\n * @return {?}\n */\n ListKeyManager.prototype.onKeydown = function (event) {\n switch (event.keyCode) {\n case DOWN_ARROW:\n this.setNextItemActive();\n break;\n case UP_ARROW:\n this.setPreviousItemActive();\n break;\n case TAB:\n this.tabOut.next();\n return;\n default:\n var /** @type {?} */ keyCode = event.keyCode;\n // Attempt to use the `event.key` which also maps it to the user's keyboard language,\n // otherwise fall back to resolving alphanumeric characters via the keyCode.\n if (event.key && event.key.length === 1) {\n this._letterKeyStream.next(event.key.toLocaleUpperCase());\n }\n else if ((keyCode >= A && keyCode <= Z) || (keyCode >= ZERO && keyCode <= NINE)) {\n this._letterKeyStream.next(String.fromCharCode(keyCode));\n }\n // Note that we return here, in order to avoid preventing\n // the default action of non-navigational keys.\n return;\n }\n this._pressedLetters = [];\n event.preventDefault();\n };\n Object.defineProperty(ListKeyManager.prototype, \"activeItemIndex\", {\n /**\n * Index of the currently active item.\n * @return {?}\n */\n get: function () {\n return this._activeItemIndex;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(ListKeyManager.prototype, \"activeItem\", {\n /**\n * The active item.\n * @return {?}\n */\n get: function () {\n return this._activeItem;\n },\n enumerable: true,\n configurable: true\n });\n /**\n * Sets the active item to the first enabled item in the list.\n * @return {?}\n */\n ListKeyManager.prototype.setFirstItemActive = function () {\n this._setActiveItemByIndex(0, 1);\n };\n /**\n * Sets the active item to the last enabled item in the list.\n * @return {?}\n */\n ListKeyManager.prototype.setLastItemActive = function () {\n this._setActiveItemByIndex(this._items.length - 1, -1);\n };\n /**\n * Sets the active item to the next enabled item in the list.\n * @return {?}\n */\n ListKeyManager.prototype.setNextItemActive = function () {\n this._activeItemIndex < 0 ? this.setFirstItemActive() : this._setActiveItemByDelta(1);\n };\n /**\n * Sets the active item to a previous enabled item in the list.\n * @return {?}\n */\n ListKeyManager.prototype.setPreviousItemActive = function () {\n this._activeItemIndex < 0 && this._wrap ? this.setLastItemActive()\n : this._setActiveItemByDelta(-1);\n };\n /**\n * Allows setting of the activeItemIndex without any other effects.\n * @param {?} index The new activeItemIndex.\n * @return {?}\n */\n ListKeyManager.prototype.updateActiveItemIndex = function (index) {\n this._activeItemIndex = index;\n };\n /**\n * This method sets the active item, given a list of items and the delta between the\n * currently active item and the new active item. It will calculate differently\n * depending on whether wrap mode is turned on.\n * @param {?} delta\n * @param {?=} items\n * @return {?}\n */\n ListKeyManager.prototype._setActiveItemByDelta = function (delta, items) {\n if (items === void 0) { items = this._items.toArray(); }\n this._wrap ? this._setActiveInWrapMode(delta, items)\n : this._setActiveInDefaultMode(delta, items);\n };\n /**\n * Sets the active item properly given \"wrap\" mode. In other words, it will continue to move\n * down the list until it finds an item that is not disabled, and it will wrap if it\n * encounters either end of the list.\n * @param {?} delta\n * @param {?} items\n * @return {?}\n */\n ListKeyManager.prototype._setActiveInWrapMode = function (delta, items) {\n // when active item would leave menu, wrap to beginning or end\n this._activeItemIndex =\n (this._activeItemIndex + delta + items.length) % items.length;\n // skip all disabled menu items recursively until an enabled one is reached\n if (items[this._activeItemIndex].disabled) {\n this._setActiveInWrapMode(delta, items);\n }\n else {\n this.setActiveItem(this._activeItemIndex);\n }\n };\n /**\n * Sets the active item properly given the default mode. In other words, it will\n * continue to move down the list until it finds an item that is not disabled. If\n * it encounters either end of the list, it will stop and not wrap.\n * @param {?} delta\n * @param {?} items\n * @return {?}\n */\n ListKeyManager.prototype._setActiveInDefaultMode = function (delta, items) {\n this._setActiveItemByIndex(this._activeItemIndex + delta, delta, items);\n };\n /**\n * Sets the active item to the first enabled item starting at the index specified. If the\n * item is disabled, it will move in the fallbackDelta direction until it either\n * finds an enabled item or encounters the end of the list.\n * @param {?} index\n * @param {?} fallbackDelta\n * @param {?=} items\n * @return {?}\n */\n ListKeyManager.prototype._setActiveItemByIndex = function (index, fallbackDelta, items) {\n if (items === void 0) { items = this._items.toArray(); }\n if (!items[index]) {\n return;\n }\n while (items[index].disabled) {\n index += fallbackDelta;\n if (!items[index]) {\n return;\n }\n }\n this.setActiveItem(index);\n };\n return ListKeyManager;\n}());\nexport { ListKeyManager };\nfunction ListKeyManager_tsickle_Closure_declarations() {\n /** @type {?} */\n ListKeyManager.prototype._activeItemIndex;\n /** @type {?} */\n ListKeyManager.prototype._activeItem;\n /** @type {?} */\n ListKeyManager.prototype._wrap;\n /** @type {?} */\n ListKeyManager.prototype._letterKeyStream;\n /** @type {?} */\n ListKeyManager.prototype._typeaheadSubscription;\n /** @type {?} */\n ListKeyManager.prototype._pressedLetters;\n /**\n * Stream that emits any time the TAB key is pressed, so components can react\n * when focus is shifted off of the list.\n * @type {?}\n */\n ListKeyManager.prototype.tabOut;\n /** @type {?} */\n ListKeyManager.prototype._items;\n}\n//# sourceMappingURL=list-key-manager.js.map","/**\n * @license\n * Copyright Google Inc. All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport * as tslib_1 from \"tslib\";\nimport { ListKeyManager } from './list-key-manager';\nvar ActiveDescendantKeyManager = (function (_super) {\n tslib_1.__extends(ActiveDescendantKeyManager, _super);\n function ActiveDescendantKeyManager() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n /**\n * This method sets the active item to the item at the specified index.\n * It also adds active styles to the newly active item and removes active\n * styles from the previously active item.\n * @param {?} index\n * @return {?}\n */\n ActiveDescendantKeyManager.prototype.setActiveItem = function (index) {\n if (this.activeItem) {\n this.activeItem.setInactiveStyles();\n }\n _super.prototype.setActiveItem.call(this, index);\n if (this.activeItem) {\n this.activeItem.setActiveStyles();\n }\n };\n return ActiveDescendantKeyManager;\n}(ListKeyManager));\nexport { ActiveDescendantKeyManager };\n//# sourceMappingURL=activedescendant-key-manager.js.map","/**\n * IDs are deliminated by an empty space, as per the spec.\n */\nvar ID_DELIMINATOR = ' ';\n/**\n * Adds the given ID to the specified ARIA attribute on an element.\n * Used for attributes such as aria-labelledby, aria-owns, etc.\n * @param {?} el\n * @param {?} attr\n * @param {?} id\n * @return {?}\n */\nexport function addAriaReferencedId(el, attr, id) {\n var /** @type {?} */ ids = getAriaReferenceIds(el, attr);\n if (ids.some(function (existingId) { return existingId.trim() == id.trim(); })) {\n return;\n }\n ids.push(id.trim());\n el.setAttribute(attr, ids.join(ID_DELIMINATOR));\n}\n/**\n * Removes the given ID from the specified ARIA attribute on an element.\n * Used for attributes such as aria-labelledby, aria-owns, etc.\n * @param {?} el\n * @param {?} attr\n * @param {?} id\n * @return {?}\n */\nexport function removeAriaReferencedId(el, attr, id) {\n var /** @type {?} */ ids = getAriaReferenceIds(el, attr);\n var /** @type {?} */ filteredIds = ids.filter(function (val) { return val != id.trim(); });\n el.setAttribute(attr, filteredIds.join(ID_DELIMINATOR));\n}\n/**\n * Gets the list of IDs referenced by the given ARIA attribute on an element.\n * Used for attributes such as aria-labelledby, aria-owns, etc.\n * @param {?} el\n * @param {?} attr\n * @return {?}\n */\nexport function getAriaReferenceIds(el, attr) {\n // Get string array of all individual ids (whitespace deliminated) in the attribute value\n return (el.getAttribute(attr) || '').match(/\\S+/g) || [];\n}\n//# sourceMappingURL=aria-reference.js.map","/**\n * @license\n * Copyright Google Inc. All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport { Injectable, Optional, SkipSelf } from '@angular/core';\nimport { Platform } from '@angular/cdk/platform';\nimport { addAriaReferencedId, getAriaReferenceIds, removeAriaReferencedId } from './aria-reference';\n/**\n * ID used for the body container where all messages are appended.\n */\nexport var MESSAGES_CONTAINER_ID = 'cdk-describedby-message-container';\n/**\n * ID prefix used for each created message element.\n */\nexport var CDK_DESCRIBEDBY_ID_PREFIX = 'cdk-describedby-message';\n/**\n * Attribute given to each host element that is described by a message element.\n */\nexport var CDK_DESCRIBEDBY_HOST_ATTRIBUTE = 'cdk-describedby-host';\n/**\n * Global incremental identifier for each registered message element.\n */\nvar nextId = 0;\n/**\n * Global map of all registered message elements that have been placed into the document.\n */\nvar messageRegistry = new Map();\n/**\n * Container for all registered messages.\n */\nvar messagesContainer = null;\n/**\n * Utility that creates visually hidden elements with a message content. Useful for elements that\n * want to use aria-describedby to further describe themselves without adding additional visual\n * content.\n * \\@docs-private\n */\nvar AriaDescriber = (function () {\n /**\n * @param {?} _platform\n */\n function AriaDescriber(_platform) {\n this._platform = _platform;\n }\n /**\n * Adds to the host element an aria-describedby reference to a hidden element that contains\n * the message. If the same message has already been registered, then it will reuse the created\n * message element.\n * @param {?} hostElement\n * @param {?} message\n * @return {?}\n */\n AriaDescriber.prototype.describe = function (hostElement, message) {\n if (!this._platform.isBrowser || !message.trim()) {\n return;\n }\n if (!messageRegistry.has(message)) {\n createMessageElement(message);\n }\n if (!isElementDescribedByMessage(hostElement, message)) {\n addMessageReference(hostElement, message);\n }\n };\n /**\n * Removes the host element's aria-describedby reference to the message element.\n * @param {?} hostElement\n * @param {?} message\n * @return {?}\n */\n AriaDescriber.prototype.removeDescription = function (hostElement, message) {\n if (!this._platform.isBrowser || !message.trim()) {\n return;\n }\n if (isElementDescribedByMessage(hostElement, message)) {\n removeMessageReference(hostElement, message);\n }\n var /** @type {?} */ registeredMessage = messageRegistry.get(message);\n if (registeredMessage && registeredMessage.referenceCount === 0) {\n deleteMessageElement(message);\n }\n if (messagesContainer && messagesContainer.childNodes.length === 0) {\n deleteMessagesContainer();\n }\n };\n /**\n * Unregisters all created message elements and removes the message container.\n * @return {?}\n */\n AriaDescriber.prototype.ngOnDestroy = function () {\n if (!this._platform.isBrowser) {\n return;\n }\n var /** @type {?} */ describedElements = document.querySelectorAll(\"[\" + CDK_DESCRIBEDBY_HOST_ATTRIBUTE + \"]\");\n for (var /** @type {?} */ i = 0; i < describedElements.length; i++) {\n removeCdkDescribedByReferenceIds(describedElements[i]);\n describedElements[i].removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE);\n }\n if (messagesContainer) {\n deleteMessagesContainer();\n }\n messageRegistry.clear();\n };\n AriaDescriber.decorators = [\n { type: Injectable },\n ];\n /**\n * @nocollapse\n */\n AriaDescriber.ctorParameters = function () { return [\n { type: Platform, },\n ]; };\n return AriaDescriber;\n}());\nexport { AriaDescriber };\nfunction AriaDescriber_tsickle_Closure_declarations() {\n /** @type {?} */\n AriaDescriber.decorators;\n /**\n * @nocollapse\n * @type {?}\n */\n AriaDescriber.ctorParameters;\n /** @type {?} */\n AriaDescriber.prototype._platform;\n}\n/**\n * Creates a new element in the visually hidden message container element with the message\n * as its content and adds it to the message registry.\n * @param {?} message\n * @return {?}\n */\nfunction createMessageElement(message) {\n var /** @type {?} */ messageElement = document.createElement('div');\n messageElement.setAttribute('id', CDK_DESCRIBEDBY_ID_PREFIX + \"-\" + nextId++);\n messageElement.appendChild(/** @type {?} */ ((document.createTextNode(message))));\n if (!messagesContainer) {\n createMessagesContainer();\n } /** @type {?} */\n ((messagesContainer)).appendChild(messageElement);\n messageRegistry.set(message, { messageElement: messageElement, referenceCount: 0 });\n}\n/**\n * Deletes the message element from the global messages container.\n * @param {?} message\n * @return {?}\n */\nfunction deleteMessageElement(message) {\n var /** @type {?} */ registeredMessage = messageRegistry.get(message);\n var /** @type {?} */ messageElement = registeredMessage && registeredMessage.messageElement;\n if (messagesContainer && messageElement) {\n messagesContainer.removeChild(messageElement);\n }\n messageRegistry.delete(message);\n}\n/**\n * Creates the global container for all aria-describedby messages.\n * @return {?}\n */\nfunction createMessagesContainer() {\n messagesContainer = document.createElement('div');\n messagesContainer.setAttribute('id', MESSAGES_CONTAINER_ID);\n messagesContainer.setAttribute('aria-hidden', 'true');\n messagesContainer.style.display = 'none';\n document.body.appendChild(messagesContainer);\n}\n/**\n * Deletes the global messages container.\n * @return {?}\n */\nfunction deleteMessagesContainer() {\n document.body.removeChild(/** @type {?} */ ((messagesContainer)));\n messagesContainer = null;\n}\n/**\n * Removes all cdk-describedby messages that are hosted through the element.\n * @param {?} element\n * @return {?}\n */\nfunction removeCdkDescribedByReferenceIds(element) {\n // Remove all aria-describedby reference IDs that are prefixed by CDK_DESCRIBEDBY_ID_PREFIX\n var /** @type {?} */ originalReferenceIds = getAriaReferenceIds(element, 'aria-describedby')\n .filter(function (id) { return id.indexOf(CDK_DESCRIBEDBY_ID_PREFIX) != 0; });\n element.setAttribute('aria-describedby', originalReferenceIds.join(' '));\n}\n/**\n * Adds a message reference to the element using aria-describedby and increments the registered\n * message's reference count.\n * @param {?} element\n * @param {?} message\n * @return {?}\n */\nfunction addMessageReference(element, message) {\n var /** @type {?} */ registeredMessage = ((messageRegistry.get(message)));\n // Add the aria-describedby reference and set the describedby_host attribute to mark the element.\n addAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id);\n element.setAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE, '');\n registeredMessage.referenceCount++;\n}\n/**\n * Removes a message reference from the element using aria-describedby and decrements the registered\n * message's reference count.\n * @param {?} element\n * @param {?} message\n * @return {?}\n */\nfunction removeMessageReference(element, message) {\n var /** @type {?} */ registeredMessage = ((messageRegistry.get(message)));\n registeredMessage.referenceCount--;\n removeAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id);\n element.removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE);\n}\n/**\n * Returns true if the element has been described by the provided message ID.\n * @param {?} element\n * @param {?} message\n * @return {?}\n */\nfunction isElementDescribedByMessage(element, message) {\n var /** @type {?} */ referenceIds = getAriaReferenceIds(element, 'aria-describedby');\n var /** @type {?} */ registeredMessage = messageRegistry.get(message);\n var /** @type {?} */ messageId = registeredMessage && registeredMessage.messageElement.id;\n return !!messageId && referenceIds.indexOf(messageId) != -1;\n}\n/**\n * \\@docs-private\n * @param {?} parentDispatcher\n * @param {?} platform\n * @return {?}\n */\nexport function ARIA_DESCRIBER_PROVIDER_FACTORY(parentDispatcher, platform) {\n return parentDispatcher || new AriaDescriber(platform);\n}\n/**\n * \\@docs-private\n */\nexport var ARIA_DESCRIBER_PROVIDER = {\n // If there is already an AriaDescriber available, use that. Otherwise, provide a new one.\n provide: AriaDescriber,\n deps: [\n [new Optional(), new SkipSelf(), AriaDescriber],\n Platform\n ],\n useFactory: ARIA_DESCRIBER_PROVIDER_FACTORY\n};\n//# sourceMappingURL=aria-describer.js.map","/**\n * Screenreaders will often fire fake mousedown events when a focusable element\n * is activated using the keyboard. We can typically distinguish between these faked\n * mousedown events and real mousedown events using the \"buttons\" property. While\n * real mousedowns will indicate the mouse button that was pressed (e.g. \"1\" for\n * the left mouse button), faked mousedowns will usually set the property value to 0.\n * @param {?} event\n * @return {?}\n */\nexport function isFakeMousedownFromScreenReader(event) {\n return event.buttons === 0;\n}\n//# sourceMappingURL=fake-mousedown.js.map","/**\n * @license\n * Copyright Google Inc. All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport * as tslib_1 from \"tslib\";\nimport { ListKeyManager } from './list-key-manager';\nvar FocusKeyManager = (function (_super) {\n tslib_1.__extends(FocusKeyManager, _super);\n function FocusKeyManager() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n /**\n * This method sets the active item to the item at the specified index.\n * It also adds focuses the newly active item.\n * @param {?} index\n * @return {?}\n */\n FocusKeyManager.prototype.setActiveItem = function (index) {\n _super.prototype.setActiveItem.call(this, index);\n if (this.activeItem) {\n this.activeItem.focus();\n }\n };\n return FocusKeyManager;\n}(ListKeyManager));\nexport { FocusKeyManager };\n//# sourceMappingURL=focus-key-manager.js.map","/**\n * @license\n * Copyright Google Inc. All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport { Injectable } from '@angular/core';\nimport { Platform } from '@angular/cdk/platform';\n/**\n * Utility for checking the interactivity of an element, such as whether is is focusable or\n * tabbable.\n */\nvar InteractivityChecker = (function () {\n /**\n * @param {?} _platform\n */\n function InteractivityChecker(_platform) {\n this._platform = _platform;\n }\n /**\n * Gets whether an element is disabled.\n *\n * @param {?} element Element to be checked.\n * @return {?} Whether the element is disabled.\n */\n InteractivityChecker.prototype.isDisabled = function (element) {\n // This does not capture some cases, such as a non-form control with a disabled attribute or\n // a form control inside of a disabled form, but should capture the most common cases.\n return element.hasAttribute('disabled');\n };\n /**\n * Gets whether an element is visible for the purposes of interactivity.\n *\n * This will capture states like `display: none` and `visibility: hidden`, but not things like\n * being clipped by an `overflow: hidden` parent or being outside the viewport.\n *\n * @param {?} element\n * @return {?} Whether the element is visible.\n */\n InteractivityChecker.prototype.isVisible = function (element) {\n return hasGeometry(element) && getComputedStyle(element).visibility === 'visible';\n };\n /**\n * Gets whether an element can be reached via Tab key.\n * Assumes that the element has already been checked with isFocusable.\n *\n * @param {?} element Element to be checked.\n * @return {?} Whether the element is tabbable.\n */\n InteractivityChecker.prototype.isTabbable = function (element) {\n // Nothing is tabbable on the the server 😎\n if (!this._platform.isBrowser) {\n return false;\n }\n var /** @type {?} */ frameElement = (getWindow(element).frameElement);\n if (frameElement) {\n var /** @type {?} */ frameType = frameElement && frameElement.nodeName.toLowerCase();\n // Frame elements inherit their tabindex onto all child elements.\n if (getTabIndexValue(frameElement) === -1) {\n return false;\n }\n // Webkit and Blink consider anything inside of an element as non-tabbable.\n if ((this._platform.BLINK || this._platform.WEBKIT) && frameType === 'object') {\n return false;\n }\n // Webkit and Blink disable tabbing to an element inside of an invisible frame.\n if ((this._platform.BLINK || this._platform.WEBKIT) && !this.isVisible(frameElement)) {\n return false;\n }\n }\n var /** @type {?} */ nodeName = element.nodeName.toLowerCase();\n var /** @type {?} */ tabIndexValue = getTabIndexValue(element);\n if (element.hasAttribute('contenteditable')) {\n return tabIndexValue !== -1;\n }\n if (nodeName === 'iframe') {\n // The frames may be tabbable depending on content, but it's not possibly to reliably\n // investigate the content of the frames.\n return false;\n }\n if (nodeName === 'audio') {\n if (!element.hasAttribute('controls')) {\n // By default an