| /** |
| * @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 |
| */ |
| import { DOCUMENT, CommonModule } from '@angular/common'; |
| import { Inject, Injectable, Optional, SkipSelf, QueryList, Directive, ElementRef, Input, NgZone, isDevMode, InjectionToken, EventEmitter, Output, NgModule, ɵɵdefineInjectable, ɵɵinject } from '@angular/core'; |
| import { Subject, Subscription, of } from 'rxjs'; |
| import { UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW, TAB, A, Z, ZERO, NINE, hasModifierKey } from '@angular/cdk/keycodes'; |
| import { debounceTime, filter, map, tap, take } from 'rxjs/operators'; |
| import { __extends } from 'tslib'; |
| import { Platform, normalizePassiveListenerOptions, PlatformModule } from '@angular/cdk/platform'; |
| import { coerceBooleanProperty, coerceElement } from '@angular/cdk/coercion'; |
| import { ContentObserver, ObserversModule } from '@angular/cdk/observers'; |
| |
| /** |
| * @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(); })); |
| el.setAttribute(attr, filteredIds.join(ID_DELIMINATOR)); |
| } |
| /** |
| * 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: Injectable, args: [{ providedIn: 'root' },] }, |
| ]; |
| /** @nocollapse */ |
| AriaDescriber.ctorParameters = function () { return [ |
| { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] } |
| ]; }; |
| /** @nocollapse */ AriaDescriber.ngInjectableDef = ɵɵdefineInjectable({ factory: function AriaDescriber_Factory() { return new AriaDescriber(ɵɵinject(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 Optional(), new SkipSelf(), AriaDescriber], |
| (/** @type {?} */ (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 Subject(); |
| this._typeaheadSubscription = 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 Subject(); |
| /** |
| * Stream that emits whenever the active item of the list manager changes. |
| */ |
| this.change = new 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 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(tap((/** |
| * @param {?} keyCode |
| * @return {?} |
| */ |
| function (keyCode) { return (/** @type {?} */ (_this))._pressedLetters.push(keyCode); })), debounceTime(debounceInterval), filter((/** |
| * @return {?} |
| */ |
| function () { return (/** @type {?} */ (_this))._pressedLetters.length > 0; })), 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 TAB: |
| this.tabOut.next(); |
| return; |
| case DOWN_ARROW: |
| if (this._vertical && isModifierAllowed) { |
| this.setNextItemActive(); |
| break; |
| } |
| else { |
| return; |
| } |
| case UP_ARROW: |
| if (this._vertical && isModifierAllowed) { |
| this.setPreviousItemActive(); |
| break; |
| } |
| else { |
| return; |
| } |
| case RIGHT_ARROW: |
| if (this._horizontal && isModifierAllowed) { |
| this._horizontal === 'rtl' ? this.setPreviousItemActive() : this.setNextItemActive(); |
| break; |
| } |
| else { |
| return; |
| } |
| case LEFT_ARROW: |
| if (this._horizontal && isModifierAllowed) { |
| this._horizontal === 'rtl' ? this.setNextItemActive() : this.setPreviousItemActive(); |
| break; |
| } |
| else { |
| return; |
| } |
| default: |
| if (isModifierAllowed || 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 >= A && keyCode <= Z) || (keyCode >= ZERO && keyCode <= 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 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 <object> 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 <audio> element without the controls enabled is not tabbable. |
| return false; |
| } |
| else if (this._platform.BLINK) { |
| // In Blink <audio controls> elements are always tabbable. |
| return true; |
| } |
| } |
| if (nodeName === 'video') { |
| if (!element.hasAttribute('controls') && this._platform.TRIDENT) { |
| // In Trident a <video> element without the controls enabled is not tabbable. |
| return false; |
| } |
| else if (this._platform.BLINK || this._platform.FIREFOX) { |
| // In Chrome and Firefox <video controls> elements are always tabbable. |
| return true; |
| } |
| } |
| if (nodeName === 'object' && (this._platform.BLINK || this._platform.WEBKIT)) { |
| // In all Blink and WebKit based browsers <object> elements are never tabbable. |
| return false; |
| } |
| // In iOS the browser only considers some specific elements as tabbable. |
| if (this._platform.WEBKIT && this._platform.IOS && !isPotentiallyTabbableIOS(element)) { |
| return false; |
| } |
| return element.tabIndex >= 0; |
| }; |
| /** |
| * Gets whether an element can be focused by the user. |
| * |
| * @param element Element to be checked. |
| * @returns Whether the element is focusable. |
| */ |
| /** |
| * Gets whether an element can be focused by the user. |
| * |
| * @param {?} element Element to be checked. |
| * @return {?} Whether the element is focusable. |
| */ |
| InteractivityChecker.prototype.isFocusable = /** |
| * Gets whether an element can be focused by the user. |
| * |
| * @param {?} element Element to be checked. |
| * @return {?} Whether the element is focusable. |
| */ |
| function (element) { |
| // Perform checks in order of left to most expensive. |
| // Again, naive approach that does not capture many edge cases and browser quirks. |
| return isPotentiallyFocusable(element) && !this.isDisabled(element) && this.isVisible(element); |
| }; |
| InteractivityChecker.decorators = [ |
| { type: Injectable, args: [{ providedIn: 'root' },] }, |
| ]; |
| /** @nocollapse */ |
| InteractivityChecker.ctorParameters = function () { return [ |
| { type: Platform } |
| ]; }; |
| /** @nocollapse */ InteractivityChecker.ngInjectableDef = ɵɵdefineInjectable({ factory: function InteractivityChecker_Factory() { return new InteractivityChecker(ɵɵinject(Platform)); }, token: InteractivityChecker, providedIn: "root" }); |
| return InteractivityChecker; |
| }()); |
| /** |
| * Returns the frame element from a window object. Since browsers like MS Edge throw errors if |
| * the frameElement property is being accessed from a different host address, this property |
| * should be accessed carefully. |
| * @param {?} window |
| * @return {?} |
| */ |
| function getFrameElement(window) { |
| try { |
| return (/** @type {?} */ (window.frameElement)); |
| } |
| catch (_a) { |
| return null; |
| } |
| } |
| /** |
| * Checks whether the specified element has any geometry / rectangles. |
| * @param {?} element |
| * @return {?} |
| */ |
| function hasGeometry(element) { |
| // Use logic from jQuery to check for an invisible element. |
| // See https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js#L12 |
| return !!(element.offsetWidth || element.offsetHeight || |
| (typeof element.getClientRects === 'function' && element.getClientRects().length)); |
| } |
| /** |
| * Gets whether an element's |
| * @param {?} element |
| * @return {?} |
| */ |
| function isNativeFormElement(element) { |
| /** @type {?} */ |
| var nodeName = element.nodeName.toLowerCase(); |
| return nodeName === 'input' || |
| nodeName === 'select' || |
| nodeName === 'button' || |
| nodeName === 'textarea'; |
| } |
| /** |
| * Gets whether an element is an `<input type="hidden">`. |
| * @param {?} element |
| * @return {?} |
| */ |
| function isHiddenInput(element) { |
| return isInputElement(element) && element.type == 'hidden'; |
| } |
| /** |
| * Gets whether an element is an anchor that has an href attribute. |
| * @param {?} element |
| * @return {?} |
| */ |
| function isAnchorWithHref(element) { |
| return isAnchorElement(element) && element.hasAttribute('href'); |
| } |
| /** |
| * Gets whether an element is an input element. |
| * @param {?} element |
| * @return {?} |
| */ |
| function isInputElement(element) { |
| return element.nodeName.toLowerCase() == 'input'; |
| } |
| /** |
| * Gets whether an element is an anchor element. |
| * @param {?} element |
| * @return {?} |
| */ |
| function isAnchorElement(element) { |
| return element.nodeName.toLowerCase() == 'a'; |
| } |
| /** |
| * Gets whether an element has a valid tabindex. |
| * @param {?} element |
| * @return {?} |
| */ |
| function hasValidTabIndex(element) { |
| if (!element.hasAttribute('tabindex') || element.tabIndex === undefined) { |
| return false; |
| } |
| /** @type {?} */ |
| var tabIndex = element.getAttribute('tabindex'); |
| // IE11 parses tabindex="" as the value "-32768" |
| if (tabIndex == '-32768') { |
| return false; |
| } |
| return !!(tabIndex && !isNaN(parseInt(tabIndex, 10))); |
| } |
| /** |
| * Returns the parsed tabindex from the element attributes instead of returning the |
| * evaluated tabindex from the browsers defaults. |
| * @param {?} element |
| * @return {?} |
| */ |
| function getTabIndexValue(element) { |
| if (!hasValidTabIndex(element)) { |
| return null; |
| } |
| // See browser issue in Gecko https://bugzilla.mozilla.org/show_bug.cgi?id=1128054 |
| /** @type {?} */ |
| var tabIndex = parseInt(element.getAttribute('tabindex') || '', 10); |
| return isNaN(tabIndex) ? -1 : tabIndex; |
| } |
| /** |
| * Checks whether the specified element is potentially tabbable on iOS |
| * @param {?} element |
| * @return {?} |
| */ |
| function isPotentiallyTabbableIOS(element) { |
| /** @type {?} */ |
| var nodeName = element.nodeName.toLowerCase(); |
| /** @type {?} */ |
| var inputType = nodeName === 'input' && ((/** @type {?} */ (element))).type; |
| return inputType === 'text' |
| || inputType === 'password' |
| || nodeName === 'select' |
| || nodeName === 'textarea'; |
| } |
| /** |
| * Gets whether an element is potentially focusable without taking current visible/disabled state |
| * into account. |
| * @param {?} element |
| * @return {?} |
| */ |
| function isPotentiallyFocusable(element) { |
| // Inputs are potentially focusable *unless* they're type="hidden". |
| if (isHiddenInput(element)) { |
| return false; |
| } |
| return isNativeFormElement(element) || |
| isAnchorWithHref(element) || |
| element.hasAttribute('contenteditable') || |
| hasValidTabIndex(element); |
| } |
| /** |
| * Gets the parent window of a DOM node with regards of being inside of an iframe. |
| * @param {?} node |
| * @return {?} |
| */ |
| function getWindow(node) { |
| // ownerDocument is null if `node` itself *is* a document. |
| return node.ownerDocument && node.ownerDocument.defaultView || window; |
| } |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| /** |
| * Class that allows for trapping focus within a DOM element. |
| * |
| * This class currently uses a relatively simple approach to focus trapping. |
| * It assumes that the tab order is the same as DOM order, which is not necessarily true. |
| * Things like `tabIndex > 0`, flex `order`, and shadow roots can cause to two to misalign. |
| */ |
| var /** |
| * Class that allows for trapping focus within a DOM element. |
| * |
| * This class currently uses a relatively simple approach to focus trapping. |
| * It assumes that the tab order is the same as DOM order, which is not necessarily true. |
| * Things like `tabIndex > 0`, flex `order`, and shadow roots can cause to two to misalign. |
| */ |
| FocusTrap = /** @class */ (function () { |
| function FocusTrap(_element, _checker, _ngZone, _document, deferAnchors) { |
| var _this = this; |
| if (deferAnchors === void 0) { deferAnchors = false; } |
| this._element = _element; |
| this._checker = _checker; |
| this._ngZone = _ngZone; |
| this._document = _document; |
| this._hasAttached = false; |
| // Event listeners for the anchors. Need to be regular functions so that we can unbind them later. |
| this.startAnchorListener = (/** |
| * @return {?} |
| */ |
| function () { return _this.focusLastTabbableElement(); }); |
| this.endAnchorListener = (/** |
| * @return {?} |
| */ |
| function () { return _this.focusFirstTabbableElement(); }); |
| this._enabled = true; |
| if (!deferAnchors) { |
| this.attachAnchors(); |
| } |
| } |
| Object.defineProperty(FocusTrap.prototype, "enabled", { |
| /** Whether the focus trap is active. */ |
| get: /** |
| * Whether the focus trap is active. |
| * @return {?} |
| */ |
| function () { return this._enabled; }, |
| set: /** |
| * @param {?} value |
| * @return {?} |
| */ |
| function (value) { |
| this._enabled = value; |
| if (this._startAnchor && this._endAnchor) { |
| this._toggleAnchorTabIndex(value, this._startAnchor); |
| this._toggleAnchorTabIndex(value, this._endAnchor); |
| } |
| }, |
| enumerable: true, |
| configurable: true |
| }); |
| /** Destroys the focus trap by cleaning up the anchors. */ |
| /** |
| * Destroys the focus trap by cleaning up the anchors. |
| * @return {?} |
| */ |
| FocusTrap.prototype.destroy = /** |
| * Destroys the focus trap by cleaning up the anchors. |
| * @return {?} |
| */ |
| function () { |
| /** @type {?} */ |
| var startAnchor = this._startAnchor; |
| /** @type {?} */ |
| var endAnchor = this._endAnchor; |
| if (startAnchor) { |
| startAnchor.removeEventListener('focus', this.startAnchorListener); |
| if (startAnchor.parentNode) { |
| startAnchor.parentNode.removeChild(startAnchor); |
| } |
| } |
| if (endAnchor) { |
| endAnchor.removeEventListener('focus', this.endAnchorListener); |
| if (endAnchor.parentNode) { |
| endAnchor.parentNode.removeChild(endAnchor); |
| } |
| } |
| this._startAnchor = this._endAnchor = null; |
| }; |
| /** |
| * Inserts the anchors into the DOM. This is usually done automatically |
| * in the constructor, but can be deferred for cases like directives with `*ngIf`. |
| * @returns Whether the focus trap managed to attach successfuly. This may not be the case |
| * if the target element isn't currently in the DOM. |
| */ |
| /** |
| * Inserts the anchors into the DOM. This is usually done automatically |
| * in the constructor, but can be deferred for cases like directives with `*ngIf`. |
| * @return {?} Whether the focus trap managed to attach successfuly. This may not be the case |
| * if the target element isn't currently in the DOM. |
| */ |
| FocusTrap.prototype.attachAnchors = /** |
| * Inserts the anchors into the DOM. This is usually done automatically |
| * in the constructor, but can be deferred for cases like directives with `*ngIf`. |
| * @return {?} Whether the focus trap managed to attach successfuly. This may not be the case |
| * if the target element isn't currently in the DOM. |
| */ |
| function () { |
| var _this = this; |
| // If we're not on the browser, there can be no focus to trap. |
| if (this._hasAttached) { |
| return true; |
| } |
| this._ngZone.runOutsideAngular((/** |
| * @return {?} |
| */ |
| function () { |
| if (!_this._startAnchor) { |
| _this._startAnchor = _this._createAnchor(); |
| (/** @type {?} */ (_this._startAnchor)).addEventListener('focus', _this.startAnchorListener); |
| } |
| if (!_this._endAnchor) { |
| _this._endAnchor = _this._createAnchor(); |
| (/** @type {?} */ (_this._endAnchor)).addEventListener('focus', _this.endAnchorListener); |
| } |
| })); |
| if (this._element.parentNode) { |
| this._element.parentNode.insertBefore((/** @type {?} */ (this._startAnchor)), this._element); |
| this._element.parentNode.insertBefore((/** @type {?} */ (this._endAnchor)), this._element.nextSibling); |
| this._hasAttached = true; |
| } |
| return this._hasAttached; |
| }; |
| /** |
| * Waits for the zone to stabilize, then either focuses the first element that the |
| * user specified, or the first tabbable element. |
| * @returns Returns a promise that resolves with a boolean, depending |
| * on whether focus was moved successfuly. |
| */ |
| /** |
| * Waits for the zone to stabilize, then either focuses the first element that the |
| * user specified, or the first tabbable element. |
| * @return {?} Returns a promise that resolves with a boolean, depending |
| * on whether focus was moved successfuly. |
| */ |
| FocusTrap.prototype.focusInitialElementWhenReady = /** |
| * Waits for the zone to stabilize, then either focuses the first element that the |
| * user specified, or the first tabbable element. |
| * @return {?} Returns a promise that resolves with a boolean, depending |
| * on whether focus was moved successfuly. |
| */ |
| function () { |
| var _this = this; |
| return new Promise((/** |
| * @param {?} resolve |
| * @return {?} |
| */ |
| function (resolve) { |
| _this._executeOnStable((/** |
| * @return {?} |
| */ |
| function () { return resolve(_this.focusInitialElement()); })); |
| })); |
| }; |
| /** |
| * Waits for the zone to stabilize, then focuses |
| * the first tabbable element within the focus trap region. |
| * @returns Returns a promise that resolves with a boolean, depending |
| * on whether focus was moved successfuly. |
| */ |
| /** |
| * Waits for the zone to stabilize, then focuses |
| * the first tabbable element within the focus trap region. |
| * @return {?} Returns a promise that resolves with a boolean, depending |
| * on whether focus was moved successfuly. |
| */ |
| FocusTrap.prototype.focusFirstTabbableElementWhenReady = /** |
| * Waits for the zone to stabilize, then focuses |
| * the first tabbable element within the focus trap region. |
| * @return {?} Returns a promise that resolves with a boolean, depending |
| * on whether focus was moved successfuly. |
| */ |
| function () { |
| var _this = this; |
| return new Promise((/** |
| * @param {?} resolve |
| * @return {?} |
| */ |
| function (resolve) { |
| _this._executeOnStable((/** |
| * @return {?} |
| */ |
| function () { return resolve(_this.focusFirstTabbableElement()); })); |
| })); |
| }; |
| /** |
| * Waits for the zone to stabilize, then focuses |
| * the last tabbable element within the focus trap region. |
| * @returns Returns a promise that resolves with a boolean, depending |
| * on whether focus was moved successfuly. |
| */ |
| /** |
| * Waits for the zone to stabilize, then focuses |
| * the last tabbable element within the focus trap region. |
| * @return {?} Returns a promise that resolves with a boolean, depending |
| * on whether focus was moved successfuly. |
| */ |
| FocusTrap.prototype.focusLastTabbableElementWhenReady = /** |
| * Waits for the zone to stabilize, then focuses |
| * the last tabbable element within the focus trap region. |
| * @return {?} Returns a promise that resolves with a boolean, depending |
| * on whether focus was moved successfuly. |
| */ |
| function () { |
| var _this = this; |
| return new Promise((/** |
| * @param {?} resolve |
| * @return {?} |
| */ |
| function (resolve) { |
| _this._executeOnStable((/** |
| * @return {?} |
| */ |
| function () { return resolve(_this.focusLastTabbableElement()); })); |
| })); |
| }; |
| /** |
| * Get the specified boundary element of the trapped region. |
| * @param bound The boundary to get (start or end of trapped region). |
| * @returns The boundary element. |
| */ |
| /** |
| * Get the specified boundary element of the trapped region. |
| * @private |
| * @param {?} bound The boundary to get (start or end of trapped region). |
| * @return {?} The boundary element. |
| */ |
| FocusTrap.prototype._getRegionBoundary = /** |
| * Get the specified boundary element of the trapped region. |
| * @private |
| * @param {?} bound The boundary to get (start or end of trapped region). |
| * @return {?} The boundary element. |
| */ |
| function (bound) { |
| // Contains the deprecated version of selector, for temporary backwards comparability. |
| /** @type {?} */ |
| var markers = (/** @type {?} */ (this._element.querySelectorAll("[cdk-focus-region-" + bound + "], " + |
| ("[cdkFocusRegion" + bound + "], ") + |
| ("[cdk-focus-" + bound + "]")))); |
| for (var i = 0; i < markers.length; i++) { |
| // @breaking-change 8.0.0 |
| if (markers[i].hasAttribute("cdk-focus-" + bound)) { |
| console.warn("Found use of deprecated attribute 'cdk-focus-" + bound + "', " + |
| ("use 'cdkFocusRegion" + bound + "' instead. The deprecated ") + |
| "attribute will be removed in 8.0.0.", markers[i]); |
| } |
| else if (markers[i].hasAttribute("cdk-focus-region-" + bound)) { |
| console.warn("Found use of deprecated attribute 'cdk-focus-region-" + bound + "', " + |
| ("use 'cdkFocusRegion" + bound + "' instead. The deprecated attribute ") + |
| "will be removed in 8.0.0.", markers[i]); |
| } |
| } |
| if (bound == 'start') { |
| return markers.length ? markers[0] : this._getFirstTabbableElement(this._element); |
| } |
| return markers.length ? |
| markers[markers.length - 1] : this._getLastTabbableElement(this._element); |
| }; |
| /** |
| * Focuses the element that should be focused when the focus trap is initialized. |
| * @returns Whether focus was moved successfuly. |
| */ |
| /** |
| * Focuses the element that should be focused when the focus trap is initialized. |
| * @return {?} Whether focus was moved successfuly. |
| */ |
| FocusTrap.prototype.focusInitialElement = /** |
| * Focuses the element that should be focused when the focus trap is initialized. |
| * @return {?} Whether focus was moved successfuly. |
| */ |
| function () { |
| // Contains the deprecated version of selector, for temporary backwards comparability. |
| /** @type {?} */ |
| var redirectToElement = (/** @type {?} */ (this._element.querySelector("[cdk-focus-initial], " + |
| "[cdkFocusInitial]"))); |
| if (redirectToElement) { |
| // @breaking-change 8.0.0 |
| if (redirectToElement.hasAttribute("cdk-focus-initial")) { |
| console.warn("Found use of deprecated attribute 'cdk-focus-initial', " + |
| "use 'cdkFocusInitial' instead. The deprecated attribute " + |
| "will be removed in 8.0.0", redirectToElement); |
| } |
| // Warn the consumer if the element they've pointed to |
| // isn't focusable, when not in production mode. |
| if (isDevMode() && !this._checker.isFocusable(redirectToElement)) { |
| console.warn("Element matching '[cdkFocusInitial]' is not focusable.", redirectToElement); |
| } |
| redirectToElement.focus(); |
| return true; |
| } |
| return this.focusFirstTabbableElement(); |
| }; |
| /** |
| * Focuses the first tabbable element within the focus trap region. |
| * @returns Whether focus was moved successfuly. |
| */ |
| /** |
| * Focuses the first tabbable element within the focus trap region. |
| * @return {?} Whether focus was moved successfuly. |
| */ |
| FocusTrap.prototype.focusFirstTabbableElement = /** |
| * Focuses the first tabbable element within the focus trap region. |
| * @return {?} Whether focus was moved successfuly. |
| */ |
| function () { |
| /** @type {?} */ |
| var redirectToElement = this._getRegionBoundary('start'); |
| if (redirectToElement) { |
| redirectToElement.focus(); |
| } |
| return !!redirectToElement; |
| }; |
| /** |
| * Focuses the last tabbable element within the focus trap region. |
| * @returns Whether focus was moved successfuly. |
| */ |
| /** |
| * Focuses the last tabbable element within the focus trap region. |
| * @return {?} Whether focus was moved successfuly. |
| */ |
| FocusTrap.prototype.focusLastTabbableElement = /** |
| * Focuses the last tabbable element within the focus trap region. |
| * @return {?} Whether focus was moved successfuly. |
| */ |
| function () { |
| /** @type {?} */ |
| var redirectToElement = this._getRegionBoundary('end'); |
| if (redirectToElement) { |
| redirectToElement.focus(); |
| } |
| return !!redirectToElement; |
| }; |
| /** |
| * Checks whether the focus trap has successfuly been attached. |
| */ |
| /** |
| * Checks whether the focus trap has successfuly been attached. |
| * @return {?} |
| */ |
| FocusTrap.prototype.hasAttached = /** |
| * Checks whether the focus trap has successfuly been attached. |
| * @return {?} |
| */ |
| function () { |
| return this._hasAttached; |
| }; |
| /** Get the first tabbable element from a DOM subtree (inclusive). */ |
| /** |
| * Get the first tabbable element from a DOM subtree (inclusive). |
| * @private |
| * @param {?} root |
| * @return {?} |
| */ |
| FocusTrap.prototype._getFirstTabbableElement = /** |
| * Get the first tabbable element from a DOM subtree (inclusive). |
| * @private |
| * @param {?} root |
| * @return {?} |
| */ |
| function (root) { |
| if (this._checker.isFocusable(root) && this._checker.isTabbable(root)) { |
| return root; |
| } |
| // Iterate in DOM order. Note that IE doesn't have `children` for SVG so we fall |
| // back to `childNodes` which includes text nodes, comments etc. |
| /** @type {?} */ |
| var children = root.children || root.childNodes; |
| for (var i = 0; i < children.length; i++) { |
| /** @type {?} */ |
| var tabbableChild = children[i].nodeType === this._document.ELEMENT_NODE ? |
| this._getFirstTabbableElement((/** @type {?} */ (children[i]))) : |
| null; |
| if (tabbableChild) { |
| return tabbableChild; |
| } |
| } |
| return null; |
| }; |
| /** Get the last tabbable element from a DOM subtree (inclusive). */ |
| /** |
| * Get the last tabbable element from a DOM subtree (inclusive). |
| * @private |
| * @param {?} root |
| * @return {?} |
| */ |
| FocusTrap.prototype._getLastTabbableElement = /** |
| * Get the last tabbable element from a DOM subtree (inclusive). |
| * @private |
| * @param {?} root |
| * @return {?} |
| */ |
| function (root) { |
| if (this._checker.isFocusable(root) && this._checker.isTabbable(root)) { |
| return root; |
| } |
| // Iterate in reverse DOM order. |
| /** @type {?} */ |
| var children = root.children || root.childNodes; |
| for (var i = children.length - 1; i >= 0; i--) { |
| /** @type {?} */ |
| var tabbableChild = children[i].nodeType === this._document.ELEMENT_NODE ? |
| this._getLastTabbableElement((/** @type {?} */ (children[i]))) : |
| null; |
| if (tabbableChild) { |
| return tabbableChild; |
| } |
| } |
| return null; |
| }; |
| /** Creates an anchor element. */ |
| /** |
| * Creates an anchor element. |
| * @private |
| * @return {?} |
| */ |
| FocusTrap.prototype._createAnchor = /** |
| * Creates an anchor element. |
| * @private |
| * @return {?} |
| */ |
| function () { |
| /** @type {?} */ |
| var anchor = this._document.createElement('div'); |
| this._toggleAnchorTabIndex(this._enabled, anchor); |
| anchor.classList.add('cdk-visually-hidden'); |
| anchor.classList.add('cdk-focus-trap-anchor'); |
| anchor.setAttribute('aria-hidden', 'true'); |
| return anchor; |
| }; |
| /** |
| * Toggles the `tabindex` of an anchor, based on the enabled state of the focus trap. |
| * @param isEnabled Whether the focus trap is enabled. |
| * @param anchor Anchor on which to toggle the tabindex. |
| */ |
| /** |
| * Toggles the `tabindex` of an anchor, based on the enabled state of the focus trap. |
| * @private |
| * @param {?} isEnabled Whether the focus trap is enabled. |
| * @param {?} anchor Anchor on which to toggle the tabindex. |
| * @return {?} |
| */ |
| FocusTrap.prototype._toggleAnchorTabIndex = /** |
| * Toggles the `tabindex` of an anchor, based on the enabled state of the focus trap. |
| * @private |
| * @param {?} isEnabled Whether the focus trap is enabled. |
| * @param {?} anchor Anchor on which to toggle the tabindex. |
| * @return {?} |
| */ |
| function (isEnabled, anchor) { |
| // Remove the tabindex completely, rather than setting it to -1, because if the |
| // element has a tabindex, the user might still hit it when navigating with the arrow keys. |
| isEnabled ? anchor.setAttribute('tabindex', '0') : anchor.removeAttribute('tabindex'); |
| }; |
| /** Executes a function when the zone is stable. */ |
| /** |
| * Executes a function when the zone is stable. |
| * @private |
| * @param {?} fn |
| * @return {?} |
| */ |
| FocusTrap.prototype._executeOnStable = /** |
| * Executes a function when the zone is stable. |
| * @private |
| * @param {?} fn |
| * @return {?} |
| */ |
| function (fn) { |
| if (this._ngZone.isStable) { |
| fn(); |
| } |
| else { |
| this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(fn); |
| } |
| }; |
| return FocusTrap; |
| }()); |
| /** |
| * Factory that allows easy instantiation of focus traps. |
| */ |
| var FocusTrapFactory = /** @class */ (function () { |
| function FocusTrapFactory(_checker, _ngZone, _document) { |
| this._checker = _checker; |
| this._ngZone = _ngZone; |
| this._document = _document; |
| } |
| /** |
| * Creates a focus-trapped region around the given element. |
| * @param element The element around which focus will be trapped. |
| * @param deferCaptureElements Defers the creation of focus-capturing elements to be done |
| * manually by the user. |
| * @returns The created focus trap instance. |
| */ |
| /** |
| * Creates a focus-trapped region around the given element. |
| * @param {?} element The element around which focus will be trapped. |
| * @param {?=} deferCaptureElements Defers the creation of focus-capturing elements to be done |
| * manually by the user. |
| * @return {?} The created focus trap instance. |
| */ |
| FocusTrapFactory.prototype.create = /** |
| * Creates a focus-trapped region around the given element. |
| * @param {?} element The element around which focus will be trapped. |
| * @param {?=} deferCaptureElements Defers the creation of focus-capturing elements to be done |
| * manually by the user. |
| * @return {?} The created focus trap instance. |
| */ |
| function (element, deferCaptureElements) { |
| if (deferCaptureElements === void 0) { deferCaptureElements = false; } |
| return new FocusTrap(element, this._checker, this._ngZone, this._document, deferCaptureElements); |
| }; |
| FocusTrapFactory.decorators = [ |
| { type: Injectable, args: [{ providedIn: 'root' },] }, |
| ]; |
| /** @nocollapse */ |
| FocusTrapFactory.ctorParameters = function () { return [ |
| { type: InteractivityChecker }, |
| { type: NgZone }, |
| { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] } |
| ]; }; |
| /** @nocollapse */ FocusTrapFactory.ngInjectableDef = ɵɵdefineInjectable({ factory: function FocusTrapFactory_Factory() { return new FocusTrapFactory(ɵɵinject(InteractivityChecker), ɵɵinject(NgZone), ɵɵinject(DOCUMENT)); }, token: FocusTrapFactory, providedIn: "root" }); |
| return FocusTrapFactory; |
| }()); |
| /** |
| * Directive for trapping focus within a region. |
| */ |
| var CdkTrapFocus = /** @class */ (function () { |
| function CdkTrapFocus(_elementRef, _focusTrapFactory, _document) { |
| this._elementRef = _elementRef; |
| this._focusTrapFactory = _focusTrapFactory; |
| /** |
| * Previously focused element to restore focus to upon destroy when using autoCapture. |
| */ |
| this._previouslyFocusedElement = null; |
| this._document = _document; |
| this.focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement, true); |
| } |
| Object.defineProperty(CdkTrapFocus.prototype, "enabled", { |
| /** Whether the focus trap is active. */ |
| get: /** |
| * Whether the focus trap is active. |
| * @return {?} |
| */ |
| function () { return this.focusTrap.enabled; }, |
| set: /** |
| * @param {?} value |
| * @return {?} |
| */ |
| function (value) { this.focusTrap.enabled = coerceBooleanProperty(value); }, |
| enumerable: true, |
| configurable: true |
| }); |
| Object.defineProperty(CdkTrapFocus.prototype, "autoCapture", { |
| /** |
| * Whether the directive should automatially move focus into the trapped region upon |
| * initialization and return focus to the previous activeElement upon destruction. |
| */ |
| get: /** |
| * Whether the directive should automatially move focus into the trapped region upon |
| * initialization and return focus to the previous activeElement upon destruction. |
| * @return {?} |
| */ |
| function () { return this._autoCapture; }, |
| set: /** |
| * @param {?} value |
| * @return {?} |
| */ |
| function (value) { this._autoCapture = coerceBooleanProperty(value); }, |
| enumerable: true, |
| configurable: true |
| }); |
| /** |
| * @return {?} |
| */ |
| CdkTrapFocus.prototype.ngOnDestroy = /** |
| * @return {?} |
| */ |
| function () { |
| this.focusTrap.destroy(); |
| // If we stored a previously focused element when using autoCapture, return focus to that |
| // element now that the trapped region is being destroyed. |
| if (this._previouslyFocusedElement) { |
| this._previouslyFocusedElement.focus(); |
| this._previouslyFocusedElement = null; |
| } |
| }; |
| /** |
| * @return {?} |
| */ |
| CdkTrapFocus.prototype.ngAfterContentInit = /** |
| * @return {?} |
| */ |
| function () { |
| this.focusTrap.attachAnchors(); |
| if (this.autoCapture) { |
| this._previouslyFocusedElement = (/** @type {?} */ (this._document.activeElement)); |
| this.focusTrap.focusInitialElementWhenReady(); |
| } |
| }; |
| /** |
| * @return {?} |
| */ |
| CdkTrapFocus.prototype.ngDoCheck = /** |
| * @return {?} |
| */ |
| function () { |
| if (!this.focusTrap.hasAttached()) { |
| this.focusTrap.attachAnchors(); |
| } |
| }; |
| CdkTrapFocus.decorators = [ |
| { type: Directive, args: [{ |
| selector: '[cdkTrapFocus]', |
| exportAs: 'cdkTrapFocus', |
| },] }, |
| ]; |
| /** @nocollapse */ |
| CdkTrapFocus.ctorParameters = function () { return [ |
| { type: ElementRef }, |
| { type: FocusTrapFactory }, |
| { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] } |
| ]; }; |
| CdkTrapFocus.propDecorators = { |
| enabled: [{ type: Input, args: ['cdkTrapFocus',] }], |
| autoCapture: [{ type: Input, args: ['cdkTrapFocusAutoCapture',] }] |
| }; |
| return CdkTrapFocus; |
| }()); |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| /** @type {?} */ |
| var LIVE_ANNOUNCER_ELEMENT_TOKEN = new InjectionToken('liveAnnouncerElement', { |
| providedIn: 'root', |
| factory: LIVE_ANNOUNCER_ELEMENT_TOKEN_FACTORY, |
| }); |
| /** |
| * \@docs-private |
| * @return {?} |
| */ |
| function LIVE_ANNOUNCER_ELEMENT_TOKEN_FACTORY() { |
| return null; |
| } |
| /** |
| * Injection token that can be used to configure the default options for the LiveAnnouncer. |
| * @type {?} |
| */ |
| var LIVE_ANNOUNCER_DEFAULT_OPTIONS = new InjectionToken('LIVE_ANNOUNCER_DEFAULT_OPTIONS'); |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| var LiveAnnouncer = /** @class */ (function () { |
| function LiveAnnouncer(elementToken, _ngZone, _document, _defaultOptions) { |
| this._ngZone = _ngZone; |
| this._defaultOptions = _defaultOptions; |
| // We inject the live element and document as `any` because the constructor signature cannot |
| // reference browser globals (HTMLElement, Document) on non-browser environments, since having |
| // a class decorator causes TypeScript to preserve the constructor signature types. |
| this._document = _document; |
| this._liveElement = elementToken || this._createLiveElement(); |
| } |
| /** |
| * @param {?} message |
| * @param {...?} args |
| * @return {?} |
| */ |
| LiveAnnouncer.prototype.announce = /** |
| * @param {?} message |
| * @param {...?} args |
| * @return {?} |
| */ |
| function (message) { |
| var _this = this; |
| var args = []; |
| for (var _i = 1; _i < arguments.length; _i++) { |
| args[_i - 1] = arguments[_i]; |
| } |
| /** @type {?} */ |
| var defaultOptions = this._defaultOptions; |
| /** @type {?} */ |
| var politeness; |
| /** @type {?} */ |
| var duration; |
| if (args.length === 1 && typeof args[0] === 'number') { |
| duration = args[0]; |
| } |
| else { |
| politeness = args[0], duration = args[1]; |
| } |
| this.clear(); |
| clearTimeout(this._previousTimeout); |
| if (!politeness) { |
| politeness = |
| (defaultOptions && defaultOptions.politeness) ? defaultOptions.politeness : 'polite'; |
| } |
| if (duration == null && defaultOptions) { |
| duration = defaultOptions.duration; |
| } |
| // TODO: ensure changing the politeness works on all environments we support. |
| this._liveElement.setAttribute('aria-live', politeness); |
| // This 100ms timeout is necessary for some browser + screen-reader combinations: |
| // - Both JAWS and NVDA over IE11 will not announce anything without a non-zero timeout. |
| // - With Chrome and IE11 with NVDA or JAWS, a repeated (identical) message won't be read a |
| // second time without clearing and then using a non-zero delay. |
| // (using JAWS 17 at time of this writing). |
| return this._ngZone.runOutsideAngular((/** |
| * @return {?} |
| */ |
| function () { |
| return new Promise((/** |
| * @param {?} resolve |
| * @return {?} |
| */ |
| function (resolve) { |
| clearTimeout(_this._previousTimeout); |
| _this._previousTimeout = setTimeout((/** |
| * @return {?} |
| */ |
| function () { |
| _this._liveElement.textContent = message; |
| resolve(); |
| if (typeof duration === 'number') { |
| _this._previousTimeout = setTimeout((/** |
| * @return {?} |
| */ |
| function () { return _this.clear(); }), duration); |
| } |
| }), 100); |
| })); |
| })); |
| }; |
| /** |
| * Clears the current text from the announcer element. Can be used to prevent |
| * screen readers from reading the text out again while the user is going |
| * through the page landmarks. |
| */ |
| /** |
| * Clears the current text from the announcer element. Can be used to prevent |
| * screen readers from reading the text out again while the user is going |
| * through the page landmarks. |
| * @return {?} |
| */ |
| LiveAnnouncer.prototype.clear = /** |
| * Clears the current text from the announcer element. Can be used to prevent |
| * screen readers from reading the text out again while the user is going |
| * through the page landmarks. |
| * @return {?} |
| */ |
| function () { |
| if (this._liveElement) { |
| this._liveElement.textContent = ''; |
| } |
| }; |
| /** |
| * @return {?} |
| */ |
| LiveAnnouncer.prototype.ngOnDestroy = /** |
| * @return {?} |
| */ |
| function () { |
| clearTimeout(this._previousTimeout); |
| if (this._liveElement && this._liveElement.parentNode) { |
| this._liveElement.parentNode.removeChild(this._liveElement); |
| this._liveElement = (/** @type {?} */ (null)); |
| } |
| }; |
| /** |
| * @private |
| * @return {?} |
| */ |
| LiveAnnouncer.prototype._createLiveElement = /** |
| * @private |
| * @return {?} |
| */ |
| function () { |
| /** @type {?} */ |
| var elementClass = 'cdk-live-announcer-element'; |
| /** @type {?} */ |
| var previousElements = this._document.getElementsByClassName(elementClass); |
| /** @type {?} */ |
| var liveEl = this._document.createElement('div'); |
| // Remove any old containers. This can happen when coming in from a server-side-rendered page. |
| for (var i = 0; i < previousElements.length; i++) { |
| (/** @type {?} */ (previousElements[i].parentNode)).removeChild(previousElements[i]); |
| } |
| liveEl.classList.add(elementClass); |
| liveEl.classList.add('cdk-visually-hidden'); |
| liveEl.setAttribute('aria-atomic', 'true'); |
| liveEl.setAttribute('aria-live', 'polite'); |
| this._document.body.appendChild(liveEl); |
| return liveEl; |
| }; |
| LiveAnnouncer.decorators = [ |
| { type: Injectable, args: [{ providedIn: 'root' },] }, |
| ]; |
| /** @nocollapse */ |
| LiveAnnouncer.ctorParameters = function () { return [ |
| { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [LIVE_ANNOUNCER_ELEMENT_TOKEN,] }] }, |
| { type: NgZone }, |
| { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }, |
| { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [LIVE_ANNOUNCER_DEFAULT_OPTIONS,] }] } |
| ]; }; |
| /** @nocollapse */ LiveAnnouncer.ngInjectableDef = ɵɵdefineInjectable({ factory: function LiveAnnouncer_Factory() { return new LiveAnnouncer(ɵɵinject(LIVE_ANNOUNCER_ELEMENT_TOKEN, 8), ɵɵinject(NgZone), ɵɵinject(DOCUMENT), ɵɵinject(LIVE_ANNOUNCER_DEFAULT_OPTIONS, 8)); }, token: LiveAnnouncer, providedIn: "root" }); |
| return LiveAnnouncer; |
| }()); |
| /** |
| * A directive that works similarly to aria-live, but uses the LiveAnnouncer to ensure compatibility |
| * with a wider range of browsers and screen readers. |
| */ |
| var CdkAriaLive = /** @class */ (function () { |
| function CdkAriaLive(_elementRef, _liveAnnouncer, _contentObserver, _ngZone) { |
| this._elementRef = _elementRef; |
| this._liveAnnouncer = _liveAnnouncer; |
| this._contentObserver = _contentObserver; |
| this._ngZone = _ngZone; |
| this._politeness = 'off'; |
| } |
| Object.defineProperty(CdkAriaLive.prototype, "politeness", { |
| /** The aria-live politeness level to use when announcing messages. */ |
| get: /** |
| * The aria-live politeness level to use when announcing messages. |
| * @return {?} |
| */ |
| function () { return this._politeness; }, |
| set: /** |
| * @param {?} value |
| * @return {?} |
| */ |
| function (value) { |
| var _this = this; |
| this._politeness = value === 'polite' || value === 'assertive' ? value : 'off'; |
| if (this._politeness === 'off') { |
| if (this._subscription) { |
| this._subscription.unsubscribe(); |
| this._subscription = null; |
| } |
| } |
| else if (!this._subscription) { |
| this._subscription = this._ngZone.runOutsideAngular((/** |
| * @return {?} |
| */ |
| function () { |
| return _this._contentObserver |
| .observe(_this._elementRef) |
| .subscribe((/** |
| * @return {?} |
| */ |
| function () { |
| // Note that we use textContent here, rather than innerText, in order to avoid a reflow. |
| /** @type {?} */ |
| var elementText = _this._elementRef.nativeElement.textContent; |
| // The `MutationObserver` fires also for attribute |
| // changes which we don't want to announce. |
| if (elementText !== _this._previousAnnouncedText) { |
| _this._liveAnnouncer.announce(elementText, _this._politeness); |
| _this._previousAnnouncedText = elementText; |
| } |
| })); |
| })); |
| } |
| }, |
| enumerable: true, |
| configurable: true |
| }); |
| /** |
| * @return {?} |
| */ |
| CdkAriaLive.prototype.ngOnDestroy = /** |
| * @return {?} |
| */ |
| function () { |
| if (this._subscription) { |
| this._subscription.unsubscribe(); |
| } |
| }; |
| CdkAriaLive.decorators = [ |
| { type: Directive, args: [{ |
| selector: '[cdkAriaLive]', |
| exportAs: 'cdkAriaLive', |
| },] }, |
| ]; |
| /** @nocollapse */ |
| CdkAriaLive.ctorParameters = function () { return [ |
| { type: ElementRef }, |
| { type: LiveAnnouncer }, |
| { type: ContentObserver }, |
| { type: NgZone } |
| ]; }; |
| CdkAriaLive.propDecorators = { |
| politeness: [{ type: Input, args: ['cdkAriaLive',] }] |
| }; |
| return CdkAriaLive; |
| }()); |
| /** |
| * \@docs-private \@deprecated \@breaking-change 8.0.0 |
| * @param {?} parentAnnouncer |
| * @param {?} liveElement |
| * @param {?} _document |
| * @param {?} ngZone |
| * @return {?} |
| */ |
| function LIVE_ANNOUNCER_PROVIDER_FACTORY(parentAnnouncer, liveElement, _document, ngZone) { |
| return parentAnnouncer || new LiveAnnouncer(liveElement, ngZone, _document); |
| } |
| /** |
| * \@docs-private \@deprecated \@breaking-change 8.0.0 |
| * @type {?} |
| */ |
| var LIVE_ANNOUNCER_PROVIDER = { |
| // If there is already a LiveAnnouncer available, use that. Otherwise, provide a new one. |
| provide: LiveAnnouncer, |
| deps: [ |
| [new Optional(), new SkipSelf(), LiveAnnouncer], |
| [new Optional(), new Inject(LIVE_ANNOUNCER_ELEMENT_TOKEN)], |
| DOCUMENT, |
| NgZone, |
| ], |
| useFactory: LIVE_ANNOUNCER_PROVIDER_FACTORY |
| }; |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| // This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found |
| // that a value of around 650ms seems appropriate. |
| /** @type {?} */ |
| var TOUCH_BUFFER_MS = 650; |
| /** |
| * Event listener options that enable capturing and also |
| * mark the listener as passive if the browser supports it. |
| * @type {?} |
| */ |
| var captureEventListenerOptions = normalizePassiveListenerOptions({ |
| passive: true, |
| capture: true |
| }); |
| /** |
| * Monitors mouse and keyboard events to determine the cause of focus events. |
| */ |
| var FocusMonitor = /** @class */ (function () { |
| function FocusMonitor(_ngZone, _platform) { |
| var _this = this; |
| this._ngZone = _ngZone; |
| this._platform = _platform; |
| /** |
| * The focus origin that the next focus event is a result of. |
| */ |
| this._origin = null; |
| /** |
| * Whether the window has just been focused. |
| */ |
| this._windowFocused = false; |
| /** |
| * Map of elements being monitored to their info. |
| */ |
| this._elementInfo = new Map(); |
| /** |
| * The number of elements currently being monitored. |
| */ |
| this._monitoredElementCount = 0; |
| /** |
| * Event listener for `keydown` events on the document. |
| * Needs to be an arrow function in order to preserve the context when it gets bound. |
| */ |
| this._documentKeydownListener = (/** |
| * @return {?} |
| */ |
| function () { |
| // On keydown record the origin and clear any touch event that may be in progress. |
| _this._lastTouchTarget = null; |
| _this._setOriginForCurrentEventQueue('keyboard'); |
| }); |
| /** |
| * Event listener for `mousedown` events on the document. |
| * Needs to be an arrow function in order to preserve the context when it gets bound. |
| */ |
| this._documentMousedownListener = (/** |
| * @return {?} |
| */ |
| function () { |
| // On mousedown record the origin only if there is not touch |
| // target, since a mousedown can happen as a result of a touch event. |
| if (!_this._lastTouchTarget) { |
| _this._setOriginForCurrentEventQueue('mouse'); |
| } |
| }); |
| /** |
| * Event listener for `touchstart` events on the document. |
| * Needs to be an arrow function in order to preserve the context when it gets bound. |
| */ |
| this._documentTouchstartListener = (/** |
| * @param {?} event |
| * @return {?} |
| */ |
| function (event) { |
| // When the touchstart event fires the focus event is not yet in the event queue. This means |
| // we can't rely on the trick used above (setting timeout of 1ms). Instead we wait 650ms to |
| // see if a focus happens. |
| if (_this._touchTimeoutId != null) { |
| clearTimeout(_this._touchTimeoutId); |
| } |
| _this._lastTouchTarget = event.target; |
| _this._touchTimeoutId = setTimeout((/** |
| * @return {?} |
| */ |
| function () { return _this._lastTouchTarget = null; }), TOUCH_BUFFER_MS); |
| }); |
| /** |
| * Event listener for `focus` events on the window. |
| * Needs to be an arrow function in order to preserve the context when it gets bound. |
| */ |
| this._windowFocusListener = (/** |
| * @return {?} |
| */ |
| function () { |
| // Make a note of when the window regains focus, so we can |
| // restore the origin info for the focused element. |
| _this._windowFocused = true; |
| _this._windowFocusTimeoutId = setTimeout((/** |
| * @return {?} |
| */ |
| function () { return _this._windowFocused = false; })); |
| }); |
| } |
| /** |
| * @param {?} element |
| * @param {?=} checkChildren |
| * @return {?} |
| */ |
| FocusMonitor.prototype.monitor = /** |
| * @param {?} element |
| * @param {?=} checkChildren |
| * @return {?} |
| */ |
| function (element, checkChildren) { |
| var _this = this; |
| if (checkChildren === void 0) { checkChildren = false; } |
| // Do nothing if we're not on the browser platform. |
| if (!this._platform.isBrowser) { |
| return of(null); |
| } |
| /** @type {?} */ |
| var nativeElement = coerceElement(element); |
| // Check if we're already monitoring this element. |
| if (this._elementInfo.has(nativeElement)) { |
| /** @type {?} */ |
| var cachedInfo = this._elementInfo.get(nativeElement); |
| (/** @type {?} */ (cachedInfo)).checkChildren = checkChildren; |
| return (/** @type {?} */ (cachedInfo)).subject.asObservable(); |
| } |
| // Create monitored element info. |
| /** @type {?} */ |
| var info = { |
| unlisten: (/** |
| * @return {?} |
| */ |
| function () { }), |
| checkChildren: checkChildren, |
| subject: new Subject() |
| }; |
| this._elementInfo.set(nativeElement, info); |
| this._incrementMonitoredElementCount(); |
| // Start listening. We need to listen in capture phase since focus events don't bubble. |
| /** @type {?} */ |
| var focusListener = (/** |
| * @param {?} event |
| * @return {?} |
| */ |
| function (event) { return _this._onFocus(event, nativeElement); }); |
| /** @type {?} */ |
| var blurListener = (/** |
| * @param {?} event |
| * @return {?} |
| */ |
| function (event) { return _this._onBlur(event, nativeElement); }); |
| this._ngZone.runOutsideAngular((/** |
| * @return {?} |
| */ |
| function () { |
| nativeElement.addEventListener('focus', focusListener, true); |
| nativeElement.addEventListener('blur', blurListener, true); |
| })); |
| // Create an unlisten function for later. |
| info.unlisten = (/** |
| * @return {?} |
| */ |
| function () { |
| nativeElement.removeEventListener('focus', focusListener, true); |
| nativeElement.removeEventListener('blur', blurListener, true); |
| }); |
| return info.subject.asObservable(); |
| }; |
| /** |
| * @param {?} element |
| * @return {?} |
| */ |
| FocusMonitor.prototype.stopMonitoring = /** |
| * @param {?} element |
| * @return {?} |
| */ |
| function (element) { |
| /** @type {?} */ |
| var nativeElement = coerceElement(element); |
| /** @type {?} */ |
| var elementInfo = this._elementInfo.get(nativeElement); |
| if (elementInfo) { |
| elementInfo.unlisten(); |
| elementInfo.subject.complete(); |
| this._setClasses(nativeElement); |
| this._elementInfo.delete(nativeElement); |
| this._decrementMonitoredElementCount(); |
| } |
| }; |
| /** |
| * @param {?} element |
| * @param {?} origin |
| * @param {?=} options |
| * @return {?} |
| */ |
| FocusMonitor.prototype.focusVia = /** |
| * @param {?} element |
| * @param {?} origin |
| * @param {?=} options |
| * @return {?} |
| */ |
| function (element, origin, options) { |
| /** @type {?} */ |
| var nativeElement = coerceElement(element); |
| this._setOriginForCurrentEventQueue(origin); |
| // `focus` isn't available on the server |
| if (typeof nativeElement.focus === 'function') { |
| // Cast the element to `any`, because the TS typings don't have the `options` parameter yet. |
| ((/** @type {?} */ (nativeElement))).focus(options); |
| } |
| }; |
| /** |
| * @return {?} |
| */ |
| FocusMonitor.prototype.ngOnDestroy = /** |
| * @return {?} |
| */ |
| function () { |
| var _this = this; |
| this._elementInfo.forEach((/** |
| * @param {?} _info |
| * @param {?} element |
| * @return {?} |
| */ |
| function (_info, element) { return _this.stopMonitoring(element); })); |
| }; |
| /** |
| * @private |
| * @param {?} element |
| * @param {?} className |
| * @param {?} shouldSet |
| * @return {?} |
| */ |
| FocusMonitor.prototype._toggleClass = /** |
| * @private |
| * @param {?} element |
| * @param {?} className |
| * @param {?} shouldSet |
| * @return {?} |
| */ |
| function (element, className, shouldSet) { |
| if (shouldSet) { |
| element.classList.add(className); |
| } |
| else { |
| element.classList.remove(className); |
| } |
| }; |
| /** |
| * Sets the focus classes on the element based on the given focus origin. |
| * @param element The element to update the classes on. |
| * @param origin The focus origin. |
| */ |
| /** |
| * Sets the focus classes on the element based on the given focus origin. |
| * @private |
| * @param {?} element The element to update the classes on. |
| * @param {?=} origin The focus origin. |
| * @return {?} |
| */ |
| FocusMonitor.prototype._setClasses = /** |
| * Sets the focus classes on the element based on the given focus origin. |
| * @private |
| * @param {?} element The element to update the classes on. |
| * @param {?=} origin The focus origin. |
| * @return {?} |
| */ |
| function (element, origin) { |
| /** @type {?} */ |
| var elementInfo = this._elementInfo.get(element); |
| if (elementInfo) { |
| this._toggleClass(element, 'cdk-focused', !!origin); |
| this._toggleClass(element, 'cdk-touch-focused', origin === 'touch'); |
| this._toggleClass(element, 'cdk-keyboard-focused', origin === 'keyboard'); |
| this._toggleClass(element, 'cdk-mouse-focused', origin === 'mouse'); |
| this._toggleClass(element, 'cdk-program-focused', origin === 'program'); |
| } |
| }; |
| /** |
| * Sets the origin and schedules an async function to clear it at the end of the event queue. |
| * @param origin The origin to set. |
| */ |
| /** |
| * Sets the origin and schedules an async function to clear it at the end of the event queue. |
| * @private |
| * @param {?} origin The origin to set. |
| * @return {?} |
| */ |
| FocusMonitor.prototype._setOriginForCurrentEventQueue = /** |
| * Sets the origin and schedules an async function to clear it at the end of the event queue. |
| * @private |
| * @param {?} origin The origin to set. |
| * @return {?} |
| */ |
| function (origin) { |
| var _this = this; |
| this._ngZone.runOutsideAngular((/** |
| * @return {?} |
| */ |
| function () { |
| _this._origin = origin; |
| // Sometimes the focus origin won't be valid in Firefox because Firefox seems to focus *one* |
| // tick after the interaction event fired. To ensure the focus origin is always correct, |
| // the focus origin will be determined at the beginning of the next tick. |
| _this._originTimeoutId = setTimeout((/** |
| * @return {?} |
| */ |
| function () { return _this._origin = null; }), 1); |
| })); |
| }; |
| /** |
| * Checks whether the given focus event was caused by a touchstart event. |
| * @param event The focus event to check. |
| * @returns Whether the event was caused by a touch. |
| */ |
| /** |
| * Checks whether the given focus event was caused by a touchstart event. |
| * @private |
| * @param {?} event The focus event to check. |
| * @return {?} Whether the event was caused by a touch. |
| */ |
| FocusMonitor.prototype._wasCausedByTouch = /** |
| * Checks whether the given focus event was caused by a touchstart event. |
| * @private |
| * @param {?} event The focus event to check. |
| * @return {?} Whether the event was caused by a touch. |
| */ |
| function (event) { |
| // Note(mmalerba): This implementation is not quite perfect, there is a small edge case. |
| // Consider the following dom structure: |
| // |
| // <div #parent tabindex="0" cdkFocusClasses> |
| // <div #child (click)="#parent.focus()"></div> |
| // </div> |
| // |
| // If the user touches the #child element and the #parent is programmatically focused as a |
| // result, this code will still consider it to have been caused by the touch event and will |
| // apply the cdk-touch-focused class rather than the cdk-program-focused class. This is a |
| // relatively small edge-case that can be worked around by using |
| // focusVia(parentEl, 'program') to focus the parent element. |
| // |
| // If we decide that we absolutely must handle this case correctly, we can do so by listening |
| // for the first focus event after the touchstart, and then the first blur event after that |
| // focus event. When that blur event fires we know that whatever follows is not a result of the |
| // touchstart. |
| /** @type {?} */ |
| var focusTarget = event.target; |
| return this._lastTouchTarget instanceof Node && focusTarget instanceof Node && |
| (focusTarget === this._lastTouchTarget || focusTarget.contains(this._lastTouchTarget)); |
| }; |
| /** |
| * Handles focus events on a registered element. |
| * @param event The focus event. |
| * @param element The monitored element. |
| */ |
| /** |
| * Handles focus events on a registered element. |
| * @private |
| * @param {?} event The focus event. |
| * @param {?} element The monitored element. |
| * @return {?} |
| */ |
| FocusMonitor.prototype._onFocus = /** |
| * Handles focus events on a registered element. |
| * @private |
| * @param {?} event The focus event. |
| * @param {?} element The monitored element. |
| * @return {?} |
| */ |
| function (event, element) { |
| // NOTE(mmalerba): We currently set the classes based on the focus origin of the most recent |
| // focus event affecting the monitored element. If we want to use the origin of the first event |
| // instead we should check for the cdk-focused class here and return if the element already has |
| // it. (This only matters for elements that have includesChildren = true). |
| // NOTE(mmalerba): We currently set the classes based on the focus origin of the most recent |
| // focus event affecting the monitored element. If we want to use the origin of the first event |
| // instead we should check for the cdk-focused class here and return if the element already has |
| // it. (This only matters for elements that have includesChildren = true). |
| // If we are not counting child-element-focus as focused, make sure that the event target is the |
| // monitored element itself. |
| /** @type {?} */ |
| var elementInfo = this._elementInfo.get(element); |
| if (!elementInfo || (!elementInfo.checkChildren && element !== event.target)) { |
| return; |
| } |
| // If we couldn't detect a cause for the focus event, it's due to one of three reasons: |
| // 1) The window has just regained focus, in which case we want to restore the focused state of |
| // the element from before the window blurred. |
| // 2) It was caused by a touch event, in which case we mark the origin as 'touch'. |
| // 3) The element was programmatically focused, in which case we should mark the origin as |
| // 'program'. |
| /** @type {?} */ |
| var origin = this._origin; |
| if (!origin) { |
| if (this._windowFocused && this._lastFocusOrigin) { |
| origin = this._lastFocusOrigin; |
| } |
| else if (this._wasCausedByTouch(event)) { |
| origin = 'touch'; |
| } |
| else { |
| origin = 'program'; |
| } |
| } |
| this._setClasses(element, origin); |
| this._emitOrigin(elementInfo.subject, origin); |
| this._lastFocusOrigin = origin; |
| }; |
| /** |
| * Handles blur events on a registered element. |
| * @param event The blur event. |
| * @param element The monitored element. |
| */ |
| /** |
| * Handles blur events on a registered element. |
| * @param {?} event The blur event. |
| * @param {?} element The monitored element. |
| * @return {?} |
| */ |
| FocusMonitor.prototype._onBlur = /** |
| * Handles blur events on a registered element. |
| * @param {?} event The blur event. |
| * @param {?} element The monitored element. |
| * @return {?} |
| */ |
| function (event, element) { |
| // If we are counting child-element-focus as focused, make sure that we aren't just blurring in |
| // order to focus another child of the monitored element. |
| /** @type {?} */ |
| var elementInfo = this._elementInfo.get(element); |
| if (!elementInfo || (elementInfo.checkChildren && event.relatedTarget instanceof Node && |
| element.contains(event.relatedTarget))) { |
| return; |
| } |
| this._setClasses(element); |
| this._emitOrigin(elementInfo.subject, null); |
| }; |
| /** |
| * @private |
| * @param {?} subject |
| * @param {?} origin |
| * @return {?} |
| */ |
| FocusMonitor.prototype._emitOrigin = /** |
| * @private |
| * @param {?} subject |
| * @param {?} origin |
| * @return {?} |
| */ |
| function (subject, origin) { |
| this._ngZone.run((/** |
| * @return {?} |
| */ |
| function () { return subject.next(origin); })); |
| }; |
| /** |
| * @private |
| * @return {?} |
| */ |
| FocusMonitor.prototype._incrementMonitoredElementCount = /** |
| * @private |
| * @return {?} |
| */ |
| function () { |
| var _this = this; |
| // Register global listeners when first element is monitored. |
| if (++this._monitoredElementCount == 1 && this._platform.isBrowser) { |
| // Note: we listen to events in the capture phase so we |
| // can detect them even if the user stops propagation. |
| this._ngZone.runOutsideAngular((/** |
| * @return {?} |
| */ |
| function () { |
| document.addEventListener('keydown', _this._documentKeydownListener, captureEventListenerOptions); |
| document.addEventListener('mousedown', _this._documentMousedownListener, captureEventListenerOptions); |
| document.addEventListener('touchstart', _this._documentTouchstartListener, captureEventListenerOptions); |
| window.addEventListener('focus', _this._windowFocusListener); |
| })); |
| } |
| }; |
| /** |
| * @private |
| * @return {?} |
| */ |
| FocusMonitor.prototype._decrementMonitoredElementCount = /** |
| * @private |
| * @return {?} |
| */ |
| function () { |
| // Unregister global listeners when last element is unmonitored. |
| if (!--this._monitoredElementCount) { |
| document.removeEventListener('keydown', this._documentKeydownListener, captureEventListenerOptions); |
| document.removeEventListener('mousedown', this._documentMousedownListener, captureEventListenerOptions); |
| document.removeEventListener('touchstart', this._documentTouchstartListener, captureEventListenerOptions); |
| window.removeEventListener('focus', this._windowFocusListener); |
| // Clear timeouts for all potentially pending timeouts to prevent the leaks. |
| clearTimeout(this._windowFocusTimeoutId); |
| clearTimeout(this._touchTimeoutId); |
| clearTimeout(this._originTimeoutId); |
| } |
| }; |
| FocusMonitor.decorators = [ |
| { type: Injectable, args: [{ providedIn: 'root' },] }, |
| ]; |
| /** @nocollapse */ |
| FocusMonitor.ctorParameters = function () { return [ |
| { type: NgZone }, |
| { type: Platform } |
| ]; }; |
| /** @nocollapse */ FocusMonitor.ngInjectableDef = ɵɵdefineInjectable({ factory: function FocusMonitor_Factory() { return new FocusMonitor(ɵɵinject(NgZone), ɵɵinject(Platform)); }, token: FocusMonitor, providedIn: "root" }); |
| return FocusMonitor; |
| }()); |
| /** |
| * Directive that determines how a particular element was focused (via keyboard, mouse, touch, or |
| * programmatically) and adds corresponding classes to the element. |
| * |
| * There are two variants of this directive: |
| * 1) cdkMonitorElementFocus: does not consider an element to be focused if one of its children is |
| * focused. |
| * 2) cdkMonitorSubtreeFocus: considers an element focused if it or any of its children are focused. |
| */ |
| var CdkMonitorFocus = /** @class */ (function () { |
| function CdkMonitorFocus(_elementRef, _focusMonitor) { |
| var _this = this; |
| this._elementRef = _elementRef; |
| this._focusMonitor = _focusMonitor; |
| this.cdkFocusChange = new EventEmitter(); |
| this._monitorSubscription = this._focusMonitor.monitor(this._elementRef, this._elementRef.nativeElement.hasAttribute('cdkMonitorSubtreeFocus')) |
| .subscribe((/** |
| * @param {?} origin |
| * @return {?} |
| */ |
| function (origin) { return _this.cdkFocusChange.emit(origin); })); |
| } |
| /** |
| * @return {?} |
| */ |
| CdkMonitorFocus.prototype.ngOnDestroy = /** |
| * @return {?} |
| */ |
| function () { |
| this._focusMonitor.stopMonitoring(this._elementRef); |
| this._monitorSubscription.unsubscribe(); |
| }; |
| CdkMonitorFocus.decorators = [ |
| { type: Directive, args: [{ |
| selector: '[cdkMonitorElementFocus], [cdkMonitorSubtreeFocus]', |
| },] }, |
| ]; |
| /** @nocollapse */ |
| CdkMonitorFocus.ctorParameters = function () { return [ |
| { type: ElementRef }, |
| { type: FocusMonitor } |
| ]; }; |
| CdkMonitorFocus.propDecorators = { |
| cdkFocusChange: [{ type: Output }] |
| }; |
| return CdkMonitorFocus; |
| }()); |
| /** |
| * \@docs-private \@deprecated \@breaking-change 8.0.0 |
| * @param {?} parentDispatcher |
| * @param {?} ngZone |
| * @param {?} platform |
| * @return {?} |
| */ |
| function FOCUS_MONITOR_PROVIDER_FACTORY(parentDispatcher, ngZone, platform) { |
| return parentDispatcher || new FocusMonitor(ngZone, platform); |
| } |
| /** |
| * \@docs-private \@deprecated \@breaking-change 8.0.0 |
| * @type {?} |
| */ |
| var FOCUS_MONITOR_PROVIDER = { |
| // If there is already a FocusMonitor available, use that. Otherwise, provide a new one. |
| provide: FocusMonitor, |
| deps: [[new Optional(), new SkipSelf(), FocusMonitor], NgZone, Platform], |
| useFactory: FOCUS_MONITOR_PROVIDER_FACTORY |
| }; |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| |
| /** |
| * Screenreaders will often fire fake mousedown events when a focusable element |
| * is activated using the keyboard. We can typically distinguish between these faked |
| * mousedown events and real mousedown events using the "buttons" property. While |
| * real mousedowns will indicate the mouse button that was pressed (e.g. "1" for |
| * the left mouse button), faked mousedowns will usually set the property value to 0. |
| * @param {?} event |
| * @return {?} |
| */ |
| function isFakeMousedownFromScreenReader(event) { |
| return event.buttons === 0; |
| } |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| var A11yModule = /** @class */ (function () { |
| function A11yModule() { |
| } |
| A11yModule.decorators = [ |
| { type: NgModule, args: [{ |
| imports: [CommonModule, PlatformModule, ObserversModule], |
| declarations: [CdkAriaLive, CdkTrapFocus, CdkMonitorFocus], |
| exports: [CdkAriaLive, CdkTrapFocus, CdkMonitorFocus], |
| },] }, |
| ]; |
| return A11yModule; |
| }()); |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| |
| export { ARIA_DESCRIBER_PROVIDER_FACTORY, MESSAGES_CONTAINER_ID, CDK_DESCRIBEDBY_ID_PREFIX, CDK_DESCRIBEDBY_HOST_ATTRIBUTE, AriaDescriber, ARIA_DESCRIBER_PROVIDER, ActiveDescendantKeyManager, FocusKeyManager, ListKeyManager, FocusTrap, FocusTrapFactory, CdkTrapFocus, InteractivityChecker, LIVE_ANNOUNCER_PROVIDER_FACTORY, LiveAnnouncer, CdkAriaLive, LIVE_ANNOUNCER_PROVIDER, LIVE_ANNOUNCER_ELEMENT_TOKEN_FACTORY, LIVE_ANNOUNCER_ELEMENT_TOKEN, LIVE_ANNOUNCER_DEFAULT_OPTIONS, FOCUS_MONITOR_PROVIDER_FACTORY, TOUCH_BUFFER_MS, FocusMonitor, CdkMonitorFocus, FOCUS_MONITOR_PROVIDER, isFakeMousedownFromScreenReader, A11yModule }; |
| //# sourceMappingURL=a11y.es5.js.map |