/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/common'), require('@angular/core'), require('rxjs'), require('@angular/cdk/keycodes'), require('rxjs/operators'), require('@angular/cdk/platform'), require('@angular/cdk/coercion'), require('@angular/cdk/observers')) : typeof define === 'function' && define.amd ? define('@angular/cdk/a11y', ['exports', '@angular/common', '@angular/core', 'rxjs', '@angular/cdk/keycodes', 'rxjs/operators', '@angular/cdk/platform', '@angular/cdk/coercion', '@angular/cdk/observers'], factory) : (factory((global.ng = global.ng || {}, global.ng.cdk = global.ng.cdk || {}, global.ng.cdk.a11y = {}),global.ng.common,global.ng.core,global.rxjs,global.ng.cdk.keycodes,global.rxjs.operators,global.ng.cdk.platform,global.ng.cdk.coercion,global.ng.cdk.observers)); }(this, (function (exports,common,core,rxjs,keycodes,operators,platform,coercion,observers) { 'use strict'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * IDs are deliminated by an empty space, as per the spec. * @type {?} */ var ID_DELIMINATOR = ' '; /** * Adds the given ID to the specified ARIA attribute on an element. * Used for attributes such as aria-labelledby, aria-owns, etc. * @param {?} el * @param {?} attr * @param {?} id * @return {?} */ function addAriaReferencedId(el, attr, id) { /** @type {?} */ var ids = getAriaReferenceIds(el, attr); if (ids.some((/** * @param {?} existingId * @return {?} */ function (existingId) { return existingId.trim() == id.trim(); }))) { return; } ids.push(id.trim()); el.setAttribute(attr, ids.join(ID_DELIMINATOR)); } /** * Removes the given ID from the specified ARIA attribute on an element. * Used for attributes such as aria-labelledby, aria-owns, etc. * @param {?} el * @param {?} attr * @param {?} id * @return {?} */ function removeAriaReferencedId(el, attr, id) { /** @type {?} */ var ids = getAriaReferenceIds(el, attr); /** @type {?} */ var filteredIds = ids.filter((/** * @param {?} val * @return {?} */ function (val) { return val != id.trim(); })); if (filteredIds.length) { el.setAttribute(attr, filteredIds.join(ID_DELIMINATOR)); } else { el.removeAttribute(attr); } } /** * Gets the list of IDs referenced by the given ARIA attribute on an element. * Used for attributes such as aria-labelledby, aria-owns, etc. * @param {?} el * @param {?} attr * @return {?} */ function getAriaReferenceIds(el, attr) { // Get string array of all individual ids (whitespace deliminated) in the attribute value return (el.getAttribute(attr) || '').match(/\S+/g) || []; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * ID used for the body container where all messages are appended. * @type {?} */ var MESSAGES_CONTAINER_ID = 'cdk-describedby-message-container'; /** * ID prefix used for each created message element. * @type {?} */ var CDK_DESCRIBEDBY_ID_PREFIX = 'cdk-describedby-message'; /** * Attribute given to each host element that is described by a message element. * @type {?} */ var CDK_DESCRIBEDBY_HOST_ATTRIBUTE = 'cdk-describedby-host'; /** * Global incremental identifier for each registered message element. * @type {?} */ var nextId = 0; /** * Global map of all registered message elements that have been placed into the document. * @type {?} */ var messageRegistry = new Map(); /** * Container for all registered messages. * @type {?} */ var messagesContainer = null; /** * Utility that creates visually hidden elements with a message content. Useful for elements that * want to use aria-describedby to further describe themselves without adding additional visual * content. */ var AriaDescriber = /** @class */ (function () { function AriaDescriber(_document) { this._document = _document; } /** * Adds to the host element an aria-describedby reference to a hidden element that contains * the message. If the same message has already been registered, then it will reuse the created * message element. */ /** * Adds to the host element an aria-describedby reference to a hidden element that contains * the message. If the same message has already been registered, then it will reuse the created * message element. * @param {?} hostElement * @param {?} message * @return {?} */ AriaDescriber.prototype.describe = /** * Adds to the host element an aria-describedby reference to a hidden element that contains * the message. If the same message has already been registered, then it will reuse the created * message element. * @param {?} hostElement * @param {?} message * @return {?} */ function (hostElement, message) { if (!this._canBeDescribed(hostElement, message)) { return; } if (typeof message !== 'string') { // We need to ensure that the element has an ID. this._setMessageId(message); messageRegistry.set(message, { messageElement: message, referenceCount: 0 }); } else if (!messageRegistry.has(message)) { this._createMessageElement(message); } if (!this._isElementDescribedByMessage(hostElement, message)) { this._addMessageReference(hostElement, message); } }; /** Removes the host element's aria-describedby reference to the message element. */ /** * Removes the host element's aria-describedby reference to the message element. * @param {?} hostElement * @param {?} message * @return {?} */ AriaDescriber.prototype.removeDescription = /** * Removes the host element's aria-describedby reference to the message element. * @param {?} hostElement * @param {?} message * @return {?} */ function (hostElement, message) { if (!this._isElementNode(hostElement)) { return; } if (this._isElementDescribedByMessage(hostElement, message)) { this._removeMessageReference(hostElement, message); } // If the message is a string, it means that it's one that we created for the // consumer so we can remove it safely, otherwise we should leave it in place. if (typeof message === 'string') { /** @type {?} */ var registeredMessage = messageRegistry.get(message); if (registeredMessage && registeredMessage.referenceCount === 0) { this._deleteMessageElement(message); } } if (messagesContainer && messagesContainer.childNodes.length === 0) { this._deleteMessagesContainer(); } }; /** Unregisters all created message elements and removes the message container. */ /** * Unregisters all created message elements and removes the message container. * @return {?} */ AriaDescriber.prototype.ngOnDestroy = /** * Unregisters all created message elements and removes the message container. * @return {?} */ function () { /** @type {?} */ var describedElements = this._document.querySelectorAll("[" + CDK_DESCRIBEDBY_HOST_ATTRIBUTE + "]"); for (var i = 0; i < describedElements.length; i++) { this._removeCdkDescribedByReferenceIds(describedElements[i]); describedElements[i].removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE); } if (messagesContainer) { this._deleteMessagesContainer(); } messageRegistry.clear(); }; /** * Creates a new element in the visually hidden message container element with the message * as its content and adds it to the message registry. */ /** * Creates a new element in the visually hidden message container element with the message * as its content and adds it to the message registry. * @private * @param {?} message * @return {?} */ AriaDescriber.prototype._createMessageElement = /** * Creates a new element in the visually hidden message container element with the message * as its content and adds it to the message registry. * @private * @param {?} message * @return {?} */ function (message) { /** @type {?} */ var messageElement = this._document.createElement('div'); this._setMessageId(messageElement); messageElement.textContent = message; this._createMessagesContainer(); (/** @type {?} */ (messagesContainer)).appendChild(messageElement); messageRegistry.set(message, { messageElement: messageElement, referenceCount: 0 }); }; /** Assigns a unique ID to an element, if it doesn't have one already. */ /** * Assigns a unique ID to an element, if it doesn't have one already. * @private * @param {?} element * @return {?} */ AriaDescriber.prototype._setMessageId = /** * Assigns a unique ID to an element, if it doesn't have one already. * @private * @param {?} element * @return {?} */ function (element) { if (!element.id) { element.id = CDK_DESCRIBEDBY_ID_PREFIX + "-" + nextId++; } }; /** Deletes the message element from the global messages container. */ /** * Deletes the message element from the global messages container. * @private * @param {?} message * @return {?} */ AriaDescriber.prototype._deleteMessageElement = /** * Deletes the message element from the global messages container. * @private * @param {?} message * @return {?} */ function (message) { /** @type {?} */ var registeredMessage = messageRegistry.get(message); /** @type {?} */ var messageElement = registeredMessage && registeredMessage.messageElement; if (messagesContainer && messageElement) { messagesContainer.removeChild(messageElement); } messageRegistry.delete(message); }; /** Creates the global container for all aria-describedby messages. */ /** * Creates the global container for all aria-describedby messages. * @private * @return {?} */ AriaDescriber.prototype._createMessagesContainer = /** * Creates the global container for all aria-describedby messages. * @private * @return {?} */ function () { if (!messagesContainer) { /** @type {?} */ var preExistingContainer = this._document.getElementById(MESSAGES_CONTAINER_ID); // When going from the server to the client, we may end up in a situation where there's // already a container on the page, but we don't have a reference to it. Clear the // old container so we don't get duplicates. Doing this, instead of emptying the previous // container, should be slightly faster. if (preExistingContainer) { (/** @type {?} */ (preExistingContainer.parentNode)).removeChild(preExistingContainer); } messagesContainer = this._document.createElement('div'); messagesContainer.id = MESSAGES_CONTAINER_ID; messagesContainer.setAttribute('aria-hidden', 'true'); messagesContainer.style.display = 'none'; this._document.body.appendChild(messagesContainer); } }; /** Deletes the global messages container. */ /** * Deletes the global messages container. * @private * @return {?} */ AriaDescriber.prototype._deleteMessagesContainer = /** * Deletes the global messages container. * @private * @return {?} */ function () { if (messagesContainer && messagesContainer.parentNode) { messagesContainer.parentNode.removeChild(messagesContainer); messagesContainer = null; } }; /** Removes all cdk-describedby messages that are hosted through the element. */ /** * Removes all cdk-describedby messages that are hosted through the element. * @private * @param {?} element * @return {?} */ AriaDescriber.prototype._removeCdkDescribedByReferenceIds = /** * Removes all cdk-describedby messages that are hosted through the element. * @private * @param {?} element * @return {?} */ function (element) { // Remove all aria-describedby reference IDs that are prefixed by CDK_DESCRIBEDBY_ID_PREFIX /** @type {?} */ var originalReferenceIds = getAriaReferenceIds(element, 'aria-describedby') .filter((/** * @param {?} id * @return {?} */ function (id) { return id.indexOf(CDK_DESCRIBEDBY_ID_PREFIX) != 0; })); element.setAttribute('aria-describedby', originalReferenceIds.join(' ')); }; /** * Adds a message reference to the element using aria-describedby and increments the registered * message's reference count. */ /** * Adds a message reference to the element using aria-describedby and increments the registered * message's reference count. * @private * @param {?} element * @param {?} message * @return {?} */ AriaDescriber.prototype._addMessageReference = /** * Adds a message reference to the element using aria-describedby and increments the registered * message's reference count. * @private * @param {?} element * @param {?} message * @return {?} */ function (element, message) { /** @type {?} */ var registeredMessage = (/** @type {?} */ (messageRegistry.get(message))); // Add the aria-describedby reference and set the // describedby_host attribute to mark the element. addAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id); element.setAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE, ''); registeredMessage.referenceCount++; }; /** * Removes a message reference from the element using aria-describedby * and decrements the registered message's reference count. */ /** * Removes a message reference from the element using aria-describedby * and decrements the registered message's reference count. * @private * @param {?} element * @param {?} message * @return {?} */ AriaDescriber.prototype._removeMessageReference = /** * Removes a message reference from the element using aria-describedby * and decrements the registered message's reference count. * @private * @param {?} element * @param {?} message * @return {?} */ function (element, message) { /** @type {?} */ var registeredMessage = (/** @type {?} */ (messageRegistry.get(message))); registeredMessage.referenceCount--; removeAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id); element.removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE); }; /** Returns true if the element has been described by the provided message ID. */ /** * Returns true if the element has been described by the provided message ID. * @private * @param {?} element * @param {?} message * @return {?} */ AriaDescriber.prototype._isElementDescribedByMessage = /** * Returns true if the element has been described by the provided message ID. * @private * @param {?} element * @param {?} message * @return {?} */ function (element, message) { /** @type {?} */ var referenceIds = getAriaReferenceIds(element, 'aria-describedby'); /** @type {?} */ var registeredMessage = messageRegistry.get(message); /** @type {?} */ var messageId = registeredMessage && registeredMessage.messageElement.id; return !!messageId && referenceIds.indexOf(messageId) != -1; }; /** Determines whether a message can be described on a particular element. */ /** * Determines whether a message can be described on a particular element. * @private * @param {?} element * @param {?} message * @return {?} */ AriaDescriber.prototype._canBeDescribed = /** * Determines whether a message can be described on a particular element. * @private * @param {?} element * @param {?} message * @return {?} */ function (element, message) { if (!this._isElementNode(element)) { return false; } if (message && typeof message === 'object') { // We'd have to make some assumptions about the description element's text, if the consumer // passed in an element. Assume that if an element is passed in, the consumer has verified // that it can be used as a description. return true; } /** @type {?} */ var trimmedMessage = message == null ? '' : ("" + message).trim(); /** @type {?} */ var ariaLabel = element.getAttribute('aria-label'); // We shouldn't set descriptions if they're exactly the same as the `aria-label` of the // element, because screen readers will end up reading out the same text twice in a row. return trimmedMessage ? (!ariaLabel || ariaLabel.trim() !== trimmedMessage) : false; }; /** Checks whether a node is an Element node. */ /** * Checks whether a node is an Element node. * @private * @param {?} element * @return {?} */ AriaDescriber.prototype._isElementNode = /** * Checks whether a node is an Element node. * @private * @param {?} element * @return {?} */ function (element) { return element.nodeType === this._document.ELEMENT_NODE; }; AriaDescriber.decorators = [ { type: core.Injectable, args: [{ providedIn: 'root' },] }, ]; /** @nocollapse */ AriaDescriber.ctorParameters = function () { return [ { type: undefined, decorators: [{ type: core.Inject, args: [common.DOCUMENT,] }] } ]; }; /** @nocollapse */ AriaDescriber.ngInjectableDef = core.ɵɵdefineInjectable({ factory: function AriaDescriber_Factory() { return new AriaDescriber(core.ɵɵinject(common.DOCUMENT)); }, token: AriaDescriber, providedIn: "root" }); return AriaDescriber; }()); /** * \@docs-private \@deprecated \@breaking-change 8.0.0 * @param {?} parentDispatcher * @param {?} _document * @return {?} */ function ARIA_DESCRIBER_PROVIDER_FACTORY(parentDispatcher, _document) { return parentDispatcher || new AriaDescriber(_document); } /** * \@docs-private \@deprecated \@breaking-change 8.0.0 * @type {?} */ var ARIA_DESCRIBER_PROVIDER = { // If there is already an AriaDescriber available, use that. Otherwise, provide a new one. provide: AriaDescriber, deps: [ [new core.Optional(), new core.SkipSelf(), AriaDescriber], (/** @type {?} */ (common.DOCUMENT)) ], useFactory: ARIA_DESCRIBER_PROVIDER_FACTORY }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * This class manages keyboard events for selectable lists. If you pass it a query list * of items, it will set the active item correctly when arrow events occur. * @template T */ var /** * This class manages keyboard events for selectable lists. If you pass it a query list * of items, it will set the active item correctly when arrow events occur. * @template T */ ListKeyManager = /** @class */ (function () { function ListKeyManager(_items) { var _this = this; this._items = _items; this._activeItemIndex = -1; this._activeItem = null; this._wrap = false; this._letterKeyStream = new rxjs.Subject(); this._typeaheadSubscription = rxjs.Subscription.EMPTY; this._vertical = true; this._allowedModifierKeys = []; /** * Predicate function that can be used to check whether an item should be skipped * by the key manager. By default, disabled items are skipped. */ this._skipPredicateFn = (/** * @param {?} item * @return {?} */ function (item) { return item.disabled; }); // Buffer for the letters that the user has pressed when the typeahead option is turned on. this._pressedLetters = []; /** * Stream that emits any time the TAB key is pressed, so components can react * when focus is shifted off of the list. */ this.tabOut = new rxjs.Subject(); /** * Stream that emits whenever the active item of the list manager changes. */ this.change = new rxjs.Subject(); // We allow for the items to be an array because, in some cases, the consumer may // not have access to a QueryList of the items they want to manage (e.g. when the // items aren't being collected via `ViewChildren` or `ContentChildren`). if (_items instanceof core.QueryList) { _items.changes.subscribe((/** * @param {?} newItems * @return {?} */ function (newItems) { if (_this._activeItem) { /** @type {?} */ var itemArray = newItems.toArray(); /** @type {?} */ var newIndex = itemArray.indexOf(_this._activeItem); if (newIndex > -1 && newIndex !== _this._activeItemIndex) { _this._activeItemIndex = newIndex; } } })); } } /** * Sets the predicate function that determines which items should be skipped by the * list key manager. * @param predicate Function that determines whether the given item should be skipped. */ /** * Sets the predicate function that determines which items should be skipped by the * list key manager. * @template THIS * @this {THIS} * @param {?} predicate Function that determines whether the given item should be skipped. * @return {THIS} */ ListKeyManager.prototype.skipPredicate = /** * Sets the predicate function that determines which items should be skipped by the * list key manager. * @template THIS * @this {THIS} * @param {?} predicate Function that determines whether the given item should be skipped. * @return {THIS} */ function (predicate) { (/** @type {?} */ (this))._skipPredicateFn = predicate; return (/** @type {?} */ (this)); }; /** * Configures wrapping mode, which determines whether the active item will wrap to * the other end of list when there are no more items in the given direction. * @param shouldWrap Whether the list should wrap when reaching the end. */ /** * Configures wrapping mode, which determines whether the active item will wrap to * the other end of list when there are no more items in the given direction. * @template THIS * @this {THIS} * @param {?=} shouldWrap Whether the list should wrap when reaching the end. * @return {THIS} */ ListKeyManager.prototype.withWrap = /** * Configures wrapping mode, which determines whether the active item will wrap to * the other end of list when there are no more items in the given direction. * @template THIS * @this {THIS} * @param {?=} shouldWrap Whether the list should wrap when reaching the end. * @return {THIS} */ function (shouldWrap) { if (shouldWrap === void 0) { shouldWrap = true; } (/** @type {?} */ (this))._wrap = shouldWrap; return (/** @type {?} */ (this)); }; /** * Configures whether the key manager should be able to move the selection vertically. * @param enabled Whether vertical selection should be enabled. */ /** * Configures whether the key manager should be able to move the selection vertically. * @template THIS * @this {THIS} * @param {?=} enabled Whether vertical selection should be enabled. * @return {THIS} */ ListKeyManager.prototype.withVerticalOrientation = /** * Configures whether the key manager should be able to move the selection vertically. * @template THIS * @this {THIS} * @param {?=} enabled Whether vertical selection should be enabled. * @return {THIS} */ function (enabled) { if (enabled === void 0) { enabled = true; } (/** @type {?} */ (this))._vertical = enabled; return (/** @type {?} */ (this)); }; /** * Configures the key manager to move the selection horizontally. * Passing in `null` will disable horizontal movement. * @param direction Direction in which the selection can be moved. */ /** * Configures the key manager to move the selection horizontally. * Passing in `null` will disable horizontal movement. * @template THIS * @this {THIS} * @param {?} direction Direction in which the selection can be moved. * @return {THIS} */ ListKeyManager.prototype.withHorizontalOrientation = /** * Configures the key manager to move the selection horizontally. * Passing in `null` will disable horizontal movement. * @template THIS * @this {THIS} * @param {?} direction Direction in which the selection can be moved. * @return {THIS} */ function (direction) { (/** @type {?} */ (this))._horizontal = direction; return (/** @type {?} */ (this)); }; /** * Modifier keys which are allowed to be held down and whose default actions will be prevented * as the user is pressing the arrow keys. Defaults to not allowing any modifier keys. */ /** * Modifier keys which are allowed to be held down and whose default actions will be prevented * as the user is pressing the arrow keys. Defaults to not allowing any modifier keys. * @template THIS * @this {THIS} * @param {?} keys * @return {THIS} */ ListKeyManager.prototype.withAllowedModifierKeys = /** * Modifier keys which are allowed to be held down and whose default actions will be prevented * as the user is pressing the arrow keys. Defaults to not allowing any modifier keys. * @template THIS * @this {THIS} * @param {?} keys * @return {THIS} */ function (keys) { (/** @type {?} */ (this))._allowedModifierKeys = keys; return (/** @type {?} */ (this)); }; /** * Turns on typeahead mode which allows users to set the active item by typing. * @param debounceInterval Time to wait after the last keystroke before setting the active item. */ /** * Turns on typeahead mode which allows users to set the active item by typing. * @template THIS * @this {THIS} * @param {?=} debounceInterval Time to wait after the last keystroke before setting the active item. * @return {THIS} */ ListKeyManager.prototype.withTypeAhead = /** * Turns on typeahead mode which allows users to set the active item by typing. * @template THIS * @this {THIS} * @param {?=} debounceInterval Time to wait after the last keystroke before setting the active item. * @return {THIS} */ function (debounceInterval) { var _this = this; if (debounceInterval === void 0) { debounceInterval = 200; } if ((/** @type {?} */ (this))._items.length && (/** @type {?} */ (this))._items.some((/** * @param {?} item * @return {?} */ function (item) { return typeof item.getLabel !== 'function'; }))) { throw Error('ListKeyManager items in typeahead mode must implement the `getLabel` method.'); } (/** @type {?} */ (this))._typeaheadSubscription.unsubscribe(); // Debounce the presses of non-navigational keys, collect the ones that correspond to letters // and convert those letters back into a string. Afterwards find the first item that starts // with that string and select it. (/** @type {?} */ (this))._typeaheadSubscription = (/** @type {?} */ (this))._letterKeyStream.pipe(operators.tap((/** * @param {?} keyCode * @return {?} */ function (keyCode) { return (/** @type {?} */ (_this))._pressedLetters.push(keyCode); })), operators.debounceTime(debounceInterval), operators.filter((/** * @return {?} */ function () { return (/** @type {?} */ (_this))._pressedLetters.length > 0; })), operators.map((/** * @return {?} */ function () { return (/** @type {?} */ (_this))._pressedLetters.join(''); }))).subscribe((/** * @param {?} inputString * @return {?} */ function (inputString) { /** @type {?} */ var items = (/** @type {?} */ (_this))._getItemsArray(); // Start at 1 because we want to start searching at the item immediately // following the current active item. for (var i = 1; i < items.length + 1; i++) { /** @type {?} */ var index = ((/** @type {?} */ (_this))._activeItemIndex + i) % items.length; /** @type {?} */ var item = items[index]; if (!(/** @type {?} */ (_this))._skipPredicateFn(item) && (/** @type {?} */ (item.getLabel))().toUpperCase().trim().indexOf(inputString) === 0) { (/** @type {?} */ (_this)).setActiveItem(index); break; } } (/** @type {?} */ (_this))._pressedLetters = []; })); return (/** @type {?} */ (this)); }; /** * @param {?} item * @return {?} */ ListKeyManager.prototype.setActiveItem = /** * @param {?} item * @return {?} */ function (item) { /** @type {?} */ var previousIndex = this._activeItemIndex; this.updateActiveItem(item); if (this._activeItemIndex !== previousIndex) { this.change.next(this._activeItemIndex); } }; /** * Sets the active item depending on the key event passed in. * @param event Keyboard event to be used for determining which element should be active. */ /** * Sets the active item depending on the key event passed in. * @param {?} event Keyboard event to be used for determining which element should be active. * @return {?} */ ListKeyManager.prototype.onKeydown = /** * Sets the active item depending on the key event passed in. * @param {?} event Keyboard event to be used for determining which element should be active. * @return {?} */ function (event) { var _this = this; /** @type {?} */ var keyCode = event.keyCode; /** @type {?} */ var modifiers = ['altKey', 'ctrlKey', 'metaKey', 'shiftKey']; /** @type {?} */ var isModifierAllowed = modifiers.every((/** * @param {?} modifier * @return {?} */ function (modifier) { return !event[modifier] || _this._allowedModifierKeys.indexOf(modifier) > -1; })); switch (keyCode) { case keycodes.TAB: this.tabOut.next(); return; case keycodes.DOWN_ARROW: if (this._vertical && isModifierAllowed) { this.setNextItemActive(); break; } else { return; } case keycodes.UP_ARROW: if (this._vertical && isModifierAllowed) { this.setPreviousItemActive(); break; } else { return; } case keycodes.RIGHT_ARROW: if (this._horizontal && isModifierAllowed) { this._horizontal === 'rtl' ? this.setPreviousItemActive() : this.setNextItemActive(); break; } else { return; } case keycodes.LEFT_ARROW: if (this._horizontal && isModifierAllowed) { this._horizontal === 'rtl' ? this.setNextItemActive() : this.setPreviousItemActive(); break; } else { return; } default: if (isModifierAllowed || keycodes.hasModifierKey(event, 'shiftKey')) { // Attempt to use the `event.key` which also maps it to the user's keyboard language, // otherwise fall back to resolving alphanumeric characters via the keyCode. if (event.key && event.key.length === 1) { this._letterKeyStream.next(event.key.toLocaleUpperCase()); } else if ((keyCode >= keycodes.A && keyCode <= keycodes.Z) || (keyCode >= keycodes.ZERO && keyCode <= keycodes.NINE)) { this._letterKeyStream.next(String.fromCharCode(keyCode)); } } // Note that we return here, in order to avoid preventing // the default action of non-navigational keys. return; } this._pressedLetters = []; event.preventDefault(); }; Object.defineProperty(ListKeyManager.prototype, "activeItemIndex", { /** Index of the currently active item. */ get: /** * Index of the currently active item. * @return {?} */ function () { return this._activeItemIndex; }, enumerable: true, configurable: true }); Object.defineProperty(ListKeyManager.prototype, "activeItem", { /** The active item. */ get: /** * The active item. * @return {?} */ function () { return this._activeItem; }, enumerable: true, configurable: true }); /** Sets the active item to the first enabled item in the list. */ /** * Sets the active item to the first enabled item in the list. * @return {?} */ ListKeyManager.prototype.setFirstItemActive = /** * Sets the active item to the first enabled item in the list. * @return {?} */ function () { this._setActiveItemByIndex(0, 1); }; /** Sets the active item to the last enabled item in the list. */ /** * Sets the active item to the last enabled item in the list. * @return {?} */ ListKeyManager.prototype.setLastItemActive = /** * Sets the active item to the last enabled item in the list. * @return {?} */ function () { this._setActiveItemByIndex(this._items.length - 1, -1); }; /** Sets the active item to the next enabled item in the list. */ /** * Sets the active item to the next enabled item in the list. * @return {?} */ ListKeyManager.prototype.setNextItemActive = /** * Sets the active item to the next enabled item in the list. * @return {?} */ function () { this._activeItemIndex < 0 ? this.setFirstItemActive() : this._setActiveItemByDelta(1); }; /** Sets the active item to a previous enabled item in the list. */ /** * Sets the active item to a previous enabled item in the list. * @return {?} */ ListKeyManager.prototype.setPreviousItemActive = /** * Sets the active item to a previous enabled item in the list. * @return {?} */ function () { this._activeItemIndex < 0 && this._wrap ? this.setLastItemActive() : this._setActiveItemByDelta(-1); }; /** * @param {?} item * @return {?} */ ListKeyManager.prototype.updateActiveItem = /** * @param {?} item * @return {?} */ function (item) { /** @type {?} */ var itemArray = this._getItemsArray(); /** @type {?} */ var index = typeof item === 'number' ? item : itemArray.indexOf(item); /** @type {?} */ var activeItem = itemArray[index]; // Explicitly check for `null` and `undefined` because other falsy values are valid. this._activeItem = activeItem == null ? null : activeItem; this._activeItemIndex = index; }; /** * Allows setting of the activeItemIndex without any other effects. * @param index The new activeItemIndex. * @deprecated Use `updateActiveItem` instead. * @breaking-change 8.0.0 */ /** * Allows setting of the activeItemIndex without any other effects. * @deprecated Use `updateActiveItem` instead. * \@breaking-change 8.0.0 * @param {?} index The new activeItemIndex. * @return {?} */ ListKeyManager.prototype.updateActiveItemIndex = /** * Allows setting of the activeItemIndex without any other effects. * @deprecated Use `updateActiveItem` instead. * \@breaking-change 8.0.0 * @param {?} index The new activeItemIndex. * @return {?} */ function (index) { this.updateActiveItem(index); }; /** * This method sets the active item, given a list of items and the delta between the * currently active item and the new active item. It will calculate differently * depending on whether wrap mode is turned on. */ /** * This method sets the active item, given a list of items and the delta between the * currently active item and the new active item. It will calculate differently * depending on whether wrap mode is turned on. * @private * @param {?} delta * @return {?} */ ListKeyManager.prototype._setActiveItemByDelta = /** * This method sets the active item, given a list of items and the delta between the * currently active item and the new active item. It will calculate differently * depending on whether wrap mode is turned on. * @private * @param {?} delta * @return {?} */ function (delta) { this._wrap ? this._setActiveInWrapMode(delta) : this._setActiveInDefaultMode(delta); }; /** * Sets the active item properly given "wrap" mode. In other words, it will continue to move * down the list until it finds an item that is not disabled, and it will wrap if it * encounters either end of the list. */ /** * Sets the active item properly given "wrap" mode. In other words, it will continue to move * down the list until it finds an item that is not disabled, and it will wrap if it * encounters either end of the list. * @private * @param {?} delta * @return {?} */ ListKeyManager.prototype._setActiveInWrapMode = /** * Sets the active item properly given "wrap" mode. In other words, it will continue to move * down the list until it finds an item that is not disabled, and it will wrap if it * encounters either end of the list. * @private * @param {?} delta * @return {?} */ function (delta) { /** @type {?} */ var items = this._getItemsArray(); for (var i = 1; i <= items.length; i++) { /** @type {?} */ var index = (this._activeItemIndex + (delta * i) + items.length) % items.length; /** @type {?} */ var item = items[index]; if (!this._skipPredicateFn(item)) { this.setActiveItem(index); return; } } }; /** * Sets the active item properly given the default mode. In other words, it will * continue to move down the list until it finds an item that is not disabled. If * it encounters either end of the list, it will stop and not wrap. */ /** * Sets the active item properly given the default mode. In other words, it will * continue to move down the list until it finds an item that is not disabled. If * it encounters either end of the list, it will stop and not wrap. * @private * @param {?} delta * @return {?} */ ListKeyManager.prototype._setActiveInDefaultMode = /** * Sets the active item properly given the default mode. In other words, it will * continue to move down the list until it finds an item that is not disabled. If * it encounters either end of the list, it will stop and not wrap. * @private * @param {?} delta * @return {?} */ function (delta) { this._setActiveItemByIndex(this._activeItemIndex + delta, delta); }; /** * Sets the active item to the first enabled item starting at the index specified. If the * item is disabled, it will move in the fallbackDelta direction until it either * finds an enabled item or encounters the end of the list. */ /** * Sets the active item to the first enabled item starting at the index specified. If the * item is disabled, it will move in the fallbackDelta direction until it either * finds an enabled item or encounters the end of the list. * @private * @param {?} index * @param {?} fallbackDelta * @return {?} */ ListKeyManager.prototype._setActiveItemByIndex = /** * Sets the active item to the first enabled item starting at the index specified. If the * item is disabled, it will move in the fallbackDelta direction until it either * finds an enabled item or encounters the end of the list. * @private * @param {?} index * @param {?} fallbackDelta * @return {?} */ function (index, fallbackDelta) { /** @type {?} */ var items = this._getItemsArray(); if (!items[index]) { return; } while (this._skipPredicateFn(items[index])) { index += fallbackDelta; if (!items[index]) { return; } } this.setActiveItem(index); }; /** Returns the items as an array. */ /** * Returns the items as an array. * @private * @return {?} */ ListKeyManager.prototype._getItemsArray = /** * Returns the items as an array. * @private * @return {?} */ function () { return this._items instanceof core.QueryList ? this._items.toArray() : this._items; }; return ListKeyManager; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @template T */ var /** * @template T */ ActiveDescendantKeyManager = /** @class */ (function (_super) { __extends(ActiveDescendantKeyManager, _super); function ActiveDescendantKeyManager() { return _super !== null && _super.apply(this, arguments) || this; } /** * @param {?} index * @return {?} */ ActiveDescendantKeyManager.prototype.setActiveItem = /** * @param {?} index * @return {?} */ function (index) { if (this.activeItem) { this.activeItem.setInactiveStyles(); } _super.prototype.setActiveItem.call(this, index); if (this.activeItem) { this.activeItem.setActiveStyles(); } }; return ActiveDescendantKeyManager; }(ListKeyManager)); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @template T */ var /** * @template T */ FocusKeyManager = /** @class */ (function (_super) { __extends(FocusKeyManager, _super); function FocusKeyManager() { var _this = _super !== null && _super.apply(this, arguments) || this; _this._origin = 'program'; return _this; } /** * Sets the focus origin that will be passed in to the items for any subsequent `focus` calls. * @param origin Focus origin to be used when focusing items. */ /** * Sets the focus origin that will be passed in to the items for any subsequent `focus` calls. * @template THIS * @this {THIS} * @param {?} origin Focus origin to be used when focusing items. * @return {THIS} */ FocusKeyManager.prototype.setFocusOrigin = /** * Sets the focus origin that will be passed in to the items for any subsequent `focus` calls. * @template THIS * @this {THIS} * @param {?} origin Focus origin to be used when focusing items. * @return {THIS} */ function (origin) { (/** @type {?} */ (this))._origin = origin; return (/** @type {?} */ (this)); }; /** * @param {?} item * @return {?} */ FocusKeyManager.prototype.setActiveItem = /** * @param {?} item * @return {?} */ function (item) { _super.prototype.setActiveItem.call(this, item); if (this.activeItem) { this.activeItem.focus(this._origin); } }; return FocusKeyManager; }(ListKeyManager)); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ // The InteractivityChecker leans heavily on the ally.js accessibility utilities. // Methods like `isTabbable` are only covering specific edge-cases for the browsers which are // supported. /** * Utility for checking the interactivity of an element, such as whether is is focusable or * tabbable. */ var InteractivityChecker = /** @class */ (function () { function InteractivityChecker(_platform) { this._platform = _platform; } /** * Gets whether an element is disabled. * * @param element Element to be checked. * @returns Whether the element is disabled. */ /** * Gets whether an element is disabled. * * @param {?} element Element to be checked. * @return {?} Whether the element is disabled. */ InteractivityChecker.prototype.isDisabled = /** * Gets whether an element is disabled. * * @param {?} element Element to be checked. * @return {?} Whether the element is disabled. */ function (element) { // This does not capture some cases, such as a non-form control with a disabled attribute or // a form control inside of a disabled form, but should capture the most common cases. return element.hasAttribute('disabled'); }; /** * Gets whether an element is visible for the purposes of interactivity. * * This will capture states like `display: none` and `visibility: hidden`, but not things like * being clipped by an `overflow: hidden` parent or being outside the viewport. * * @returns Whether the element is visible. */ /** * Gets whether an element is visible for the purposes of interactivity. * * This will capture states like `display: none` and `visibility: hidden`, but not things like * being clipped by an `overflow: hidden` parent or being outside the viewport. * * @param {?} element * @return {?} Whether the element is visible. */ InteractivityChecker.prototype.isVisible = /** * Gets whether an element is visible for the purposes of interactivity. * * This will capture states like `display: none` and `visibility: hidden`, but not things like * being clipped by an `overflow: hidden` parent or being outside the viewport. * * @param {?} element * @return {?} Whether the element is visible. */ function (element) { return hasGeometry(element) && getComputedStyle(element).visibility === 'visible'; }; /** * Gets whether an element can be reached via Tab key. * Assumes that the element has already been checked with isFocusable. * * @param element Element to be checked. * @returns Whether the element is tabbable. */ /** * Gets whether an element can be reached via Tab key. * Assumes that the element has already been checked with isFocusable. * * @param {?} element Element to be checked. * @return {?} Whether the element is tabbable. */ InteractivityChecker.prototype.isTabbable = /** * Gets whether an element can be reached via Tab key. * Assumes that the element has already been checked with isFocusable. * * @param {?} element Element to be checked. * @return {?} Whether the element is tabbable. */ function (element) { // Nothing is tabbable on the server 😎 if (!this._platform.isBrowser) { return false; } /** @type {?} */ var frameElement = getFrameElement(getWindow(element)); if (frameElement) { /** @type {?} */ var frameType = frameElement && frameElement.nodeName.toLowerCase(); // Frame elements inherit their tabindex onto all child elements. if (getTabIndexValue(frameElement) === -1) { return false; } // Webkit and Blink consider anything inside of an element as non-tabbable. if ((this._platform.BLINK || this._platform.WEBKIT) && frameType === 'object') { return false; } // Webkit and Blink disable tabbing to an element inside of an invisible frame. if ((this._platform.BLINK || this._platform.WEBKIT) && !this.isVisible(frameElement)) { return false; } } /** @type {?} */ var nodeName = element.nodeName.toLowerCase(); /** @type {?} */ var tabIndexValue = getTabIndexValue(element); if (element.hasAttribute('contenteditable')) { return tabIndexValue !== -1; } if (nodeName === 'iframe') { // The frames may be tabbable depending on content, but it's not possibly to reliably // investigate the content of the frames. return false; } if (nodeName === 'audio') { if (!element.hasAttribute('controls')) { // By default an