http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/985298bd/node_modules/@angular/cdk/bundles/cdk-a11y.umd.js ---------------------------------------------------------------------- diff --git a/node_modules/@angular/cdk/bundles/cdk-a11y.umd.js b/node_modules/@angular/cdk/bundles/cdk-a11y.umd.js index ac4fe98..c5b06f8 100644 --- a/node_modules/@angular/cdk/bundles/cdk-a11y.umd.js +++ b/node_modules/@angular/cdk/bundles/cdk-a11y.umd.js @@ -1,15 +1,15 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rxjs/Subject'), require('rxjs/Subscription'), require('@angular/cdk/keycodes'), require('@angular/cdk/rxjs'), require('@angular/core'), require('@angular/cdk/platform'), require('@angular/cdk/coercion'), require('rxjs/observable/of'), require('@angular/common')) : - typeof define === 'function' && define.amd ? define(['exports', 'rxjs/Subject', 'rxjs/Subscription', '@angular/cdk/keycodes', '@angular/cdk/rxjs', '@angular/core', '@angular/cdk/platform', '@angular/cdk/coercion', 'rxjs/observable/of', '@angular/common'], factory) : - (factory((global.ng = global.ng || {}, global.ng.cdk = global.ng.cdk || {}, global.ng.cdk.a11y = global.ng.cdk.a11y || {}),global.Rx,global.Rx,global.ng.cdk.keycodes,global.ng.cdk.rxjs,global.ng.core,global.ng.cdk.platform,global.ng.cdk.coercion,global.Rx.Observable,global.ng.common)); -}(this, (function (exports,rxjs_Subject,rxjs_Subscription,_angular_cdk_keycodes,_angular_cdk_rxjs,_angular_core,_angular_cdk_platform,_angular_cdk_coercion,rxjs_observable_of,_angular_common) { 'use strict'; + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/cdk/coercion'), require('rxjs/operators/take'), require('@angular/cdk/platform'), require('@angular/common'), require('rxjs/Subject'), require('rxjs/Subscription'), require('@angular/cdk/keycodes'), require('rxjs/operators/debounceTime'), require('rxjs/operators/filter'), require('rxjs/operators/map'), require('rxjs/operators/tap'), require('rxjs/observable/of')) : + typeof define === 'function' && define.amd ? define(['exports', '@angular/core', '@angular/cdk/coercion', 'rxjs/operators/take', '@angular/cdk/platform', '@angular/common', 'rxjs/Subject', 'rxjs/Subscription', '@angular/cdk/keycodes', 'rxjs/operators/debounceTime', 'rxjs/operators/filter', 'rxjs/operators/map', 'rxjs/operators/tap', 'rxjs/observable/of'], factory) : + (factory((global.ng = global.ng || {}, global.ng.cdk = global.ng.cdk || {}, global.ng.cdk.a11y = global.ng.cdk.a11y || {}),global.ng.core,global.ng.cdk.coercion,global.Rx.operators,global.ng.cdk.platform,global.ng.common,global.Rx,global.Rx,global.ng.cdk.keycodes,global.Rx.operators,global.Rx.operators,global.Rx.operators,global.Rx.operators,global.Rx.Observable)); +}(this, (function (exports,_angular_core,_angular_cdk_coercion,rxjs_operators_take,_angular_cdk_platform,_angular_common,rxjs_Subject,rxjs_Subscription,_angular_cdk_keycodes,rxjs_operators_debounceTime,rxjs_operators_filter,rxjs_operators_map,rxjs_operators_tap,rxjs_observable_of) { 'use strict'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. @@ -38,264 +38,874 @@ function __extends(d, b) { } /** - * 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. + * @fileoverview added by tsickle + * @suppress {checkTypes} checked by tsc + */ + +/** + * Utility for checking the interactivity of an element, such as whether is is focusable or + * tabbable. */ -var ListKeyManager = (function () { +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 the server ð + if (!this._platform.isBrowser) { + return false; + } + var /** @type {?} */ frameElement = getFrameElement(getWindow(element)); + if (frameElement) { + var /** @type {?} */ 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; + } + } + var /** @type {?} */ nodeName = element.nodeName.toLowerCase(); + var /** @type {?} */ 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: _angular_core.Injectable }, + ]; + /** @nocollapse */ + InteractivityChecker.ctorParameters = function () { return [ + { type: _angular_cdk_platform.Platform, }, + ]; }; + 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 (/** @type {?} */ e) { + 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) { + var /** @type {?} */ 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; + } + var /** @type {?} */ 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 + var /** @type {?} */ 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) { + var /** @type {?} */ nodeName = element.nodeName.toLowerCase(); + var /** @type {?} */ 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) { + return node.ownerDocument.defaultView || window; +} + +/** + * @fileoverview added by tsickle + * @suppress {checkTypes} 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 FocusTrap = /** @class */ (function () { + function FocusTrap(_element, _checker, _ngZone, _document, deferAnchors) { + if (deferAnchors === void 0) { deferAnchors = false; } + this._element = _element; + this._checker = _checker; + this._ngZone = _ngZone; + this._document = _document; + 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 {?} val + * @return {?} + */ + function (val) { + this._enabled = val; + if (this._startAnchor && this._endAnchor) { + this._startAnchor.tabIndex = this._endAnchor.tabIndex = this._enabled ? 0 : -1; + } + }, + 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 () { + if (this._startAnchor && this._startAnchor.parentNode) { + this._startAnchor.parentNode.removeChild(this._startAnchor); + } + if (this._endAnchor && this._endAnchor.parentNode) { + this._endAnchor.parentNode.removeChild(this._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`. + */ + /** + * 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 {?} + */ + 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 {?} + */ + function () { + var _this = this; + if (!this._startAnchor) { + this._startAnchor = this._createAnchor(); + } + if (!this._endAnchor) { + this._endAnchor = this._createAnchor(); + } + this._ngZone.runOutsideAngular(function () { + /** @type {?} */ ((_this._startAnchor)).addEventListener('focus', function () { + _this.focusLastTabbableElement(); + }); /** @type {?} */ + ((_this._endAnchor)).addEventListener('focus', function () { + _this.focusFirstTabbableElement(); + }); + if (_this._element.parentNode) { + _this._element.parentNode.insertBefore(/** @type {?} */ ((_this._startAnchor)), _this._element); + _this._element.parentNode.insertBefore(/** @type {?} */ ((_this._endAnchor)), _this._element.nextSibling); + } + }); + }; + /** + * 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(function (resolve) { + _this._executeOnStable(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(function (resolve) { + _this._executeOnStable(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(function (resolve) { + _this._executeOnStable(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). + * @return {?} The boundary element. + */ + FocusTrap.prototype._getRegionBoundary = /** + * Get the specified boundary element of the trapped region. + * @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. + var /** @type {?} */ markers = /** @type {?} */ (this._element.querySelectorAll("[cdk-focus-region-" + bound + "], " + + ("[cdkFocusRegion" + bound + "], ") + + ("[cdk-focus-" + bound + "]"))); + for (var /** @type {?} */ i = 0; i < markers.length; i++) { + if (markers[i].hasAttribute("cdk-focus-" + bound)) { + console.warn("Found use of deprecated attribute 'cdk-focus-" + bound + "'," + + (" use 'cdkFocusRegion" + bound + "' instead."), 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."), 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); + }; /** - * @param {?} _items + * Focuses the element that should be focused when the focus trap is initialized. + * @returns Whether focus was moved successfuly. */ - function ListKeyManager(_items) { - this._items = _items; - this._activeItemIndex = -1; - this._wrap = false; - this._letterKeyStream = new rxjs_Subject.Subject(); - this._typeaheadSubscription = rxjs_Subscription.Subscription.EMPTY; - this._pressedLetters = []; - /** - * Stream that emits any time the TAB key is pressed, so components can react - * when focus is shifted off of the list. - */ - this.tabOut = new rxjs_Subject.Subject(); - } /** - * Turns on wrapping mode, which ensures that the active item will wrap to - * the other end of list when there are no more items in the given direction. + * 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. + var /** @type {?} */ redirectToElement = /** @type {?} */ (this._element.querySelector("[cdk-focus-initial], " + + "[cdkFocusInitial]")); + if (this._element.hasAttribute("cdk-focus-initial")) { + console.warn("Found use of deprecated attribute 'cdk-focus-initial'," + + " use 'cdkFocusInitial' instead.", this._element); + } + if (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 () { + var /** @type {?} */ 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 () { + var /** @type {?} */ redirectToElement = this._getRegionBoundary('end'); + if (redirectToElement) { + redirectToElement.focus(); + } + return !!redirectToElement; + }; + /** + * Get the first tabbable element from a DOM subtree (inclusive). + * @param {?} root * @return {?} */ - ListKeyManager.prototype.withWrap = function () { - this._wrap = true; - return this; + FocusTrap.prototype._getFirstTabbableElement = /** + * Get the first tabbable element from a DOM subtree (inclusive). + * @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. + var /** @type {?} */ children = root.children || root.childNodes; + for (var /** @type {?} */ i = 0; i < children.length; i++) { + var /** @type {?} */ tabbableChild = children[i].nodeType === this._document.ELEMENT_NODE ? + this._getFirstTabbableElement(/** @type {?} */ (children[i])) : + null; + if (tabbableChild) { + return tabbableChild; + } + } + return null; }; /** - * 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. + * Get the last tabbable element from a DOM subtree (inclusive). + * @param {?} root * @return {?} */ - ListKeyManager.prototype.withTypeAhead = function (debounceInterval) { - var _this = this; - if (debounceInterval === void 0) { debounceInterval = 200; } - if (this._items.length && this._items.some(function (item) { return typeof item.getLabel !== 'function'; })) { - throw Error('ListKeyManager items in typeahead mode must implement the `getLabel` method.'); + FocusTrap.prototype._getLastTabbableElement = /** + * Get the last tabbable element from a DOM subtree (inclusive). + * @param {?} root + * @return {?} + */ + function (root) { + if (this._checker.isFocusable(root) && this._checker.isTabbable(root)) { + return root; } - 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. - this._typeaheadSubscription = _angular_cdk_rxjs.RxChain.from(this._letterKeyStream) - .call(_angular_cdk_rxjs.doOperator, function (keyCode) { return _this._pressedLetters.push(keyCode); }) - .call(_angular_cdk_rxjs.debounceTime, debounceInterval) - .call(_angular_cdk_rxjs.filter, function () { return _this._pressedLetters.length > 0; }) - .call(_angular_cdk_rxjs.map, function () { return _this._pressedLetters.join(''); }) - .subscribe(function (inputString) { - var /** @type {?} */ items = _this._items.toArray(); - // Start at 1 because we want to start searching at the item immediately - // following the current active item. - for (var /** @type {?} */ i = 1; i < items.length + 1; i++) { - var /** @type {?} */ index = (_this._activeItemIndex + i) % items.length; - var /** @type {?} */ item = items[index]; - if (!item.disabled && ((item.getLabel))().toUpperCase().trim().indexOf(inputString) === 0) { - _this.setActiveItem(index); - break; - } + // Iterate in reverse DOM order. + var /** @type {?} */ children = root.children || root.childNodes; + for (var /** @type {?} */ i = children.length - 1; i >= 0; i--) { + var /** @type {?} */ tabbableChild = children[i].nodeType === this._document.ELEMENT_NODE ? + this._getLastTabbableElement(/** @type {?} */ (children[i])) : + null; + if (tabbableChild) { + return tabbableChild; } - _this._pressedLetters = []; - }); - return this; + } + return null; }; /** - * Sets the active item to the item at the index specified. - * @param {?} index The index of the item to be set as active. + * Creates an anchor element. * @return {?} */ - ListKeyManager.prototype.setActiveItem = function (index) { - this._activeItemIndex = index; - this._activeItem = this._items.toArray()[index]; + FocusTrap.prototype._createAnchor = /** + * Creates an anchor element. + * @return {?} + */ + function () { + var /** @type {?} */ anchor = this._document.createElement('div'); + anchor.tabIndex = this._enabled ? 0 : -1; + anchor.classList.add('cdk-visually-hidden'); + anchor.classList.add('cdk-focus-trap-anchor'); + return anchor; }; /** - * 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. + * Executes a function when the zone is stable. + * @param {?} fn * @return {?} */ - ListKeyManager.prototype.onKeydown = function (event) { - switch (event.keyCode) { - case _angular_cdk_keycodes.DOWN_ARROW: - this.setNextItemActive(); - break; - case _angular_cdk_keycodes.UP_ARROW: - this.setPreviousItemActive(); - break; - case _angular_cdk_keycodes.TAB: - this.tabOut.next(); - return; - default: - var /** @type {?} */ keyCode = event.keyCode; - // 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 >= _angular_cdk_keycodes.A && keyCode <= _angular_cdk_keycodes.Z) || (keyCode >= _angular_cdk_keycodes.ZERO && keyCode <= _angular_cdk_keycodes.NINE)) { - this._letterKeyStream.next(String.fromCharCode(keyCode)); - } - // Note that we return here, in order to avoid preventing - // the default action of non-navigational keys. - return; + FocusTrap.prototype._executeOnStable = /** + * Executes a function when the zone is stable. + * @param {?} fn + * @return {?} + */ + function (fn) { + if (this._ngZone.isStable) { + fn(); + } + else { + this._ngZone.onStable.asObservable().pipe(rxjs_operators_take.take(1)).subscribe(fn); } - this._pressedLetters = []; - event.preventDefault(); }; - Object.defineProperty(ListKeyManager.prototype, "activeItemIndex", { + 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: _angular_core.Injectable }, + ]; + /** @nocollapse */ + FocusTrapFactory.ctorParameters = function () { return [ + { type: InteractivityChecker, }, + { type: _angular_core.NgZone, }, + { type: undefined, decorators: [{ type: _angular_core.Inject, args: [_angular_common.DOCUMENT,] },] }, + ]; }; + return FocusTrapFactory; +}()); +/** + * Directive for trapping focus within a region. + * \@docs-private + * @deprecated + * \@deletion-target 6.0.0 + */ +var FocusTrapDeprecatedDirective = /** @class */ (function () { + function FocusTrapDeprecatedDirective(_elementRef, _focusTrapFactory) { + this._elementRef = _elementRef; + this._focusTrapFactory = _focusTrapFactory; + this.focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement, true); + } + Object.defineProperty(FocusTrapDeprecatedDirective.prototype, "disabled", { + get: /** + * Whether the focus trap is active. + * @return {?} + */ + function () { return !this.focusTrap.enabled; }, + set: /** + * @param {?} val + * @return {?} + */ + function (val) { + this.focusTrap.enabled = !_angular_cdk_coercion.coerceBooleanProperty(val); + }, + enumerable: true, + configurable: true + }); + /** + * @return {?} + */ + FocusTrapDeprecatedDirective.prototype.ngOnDestroy = /** + * @return {?} + */ + function () { + this.focusTrap.destroy(); + }; + /** + * @return {?} + */ + FocusTrapDeprecatedDirective.prototype.ngAfterContentInit = /** + * @return {?} + */ + function () { + this.focusTrap.attachAnchors(); + }; + FocusTrapDeprecatedDirective.decorators = [ + { type: _angular_core.Directive, args: [{ + selector: 'cdk-focus-trap', + },] }, + ]; + /** @nocollapse */ + FocusTrapDeprecatedDirective.ctorParameters = function () { return [ + { type: _angular_core.ElementRef, }, + { type: FocusTrapFactory, }, + ]; }; + FocusTrapDeprecatedDirective.propDecorators = { + "disabled": [{ type: _angular_core.Input },], + }; + return FocusTrapDeprecatedDirective; +}()); +/** + * Directive for trapping focus within a region. + */ +var CdkTrapFocus = /** @class */ (function () { + function CdkTrapFocus(_elementRef, _focusTrapFactory, _document) { + this._elementRef = _elementRef; + this._focusTrapFactory = _focusTrapFactory; /** - * Index of the currently active item. + * 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", { + get: /** + * Whether the focus trap is active. * @return {?} */ - get: function () { - return this._activeItemIndex; - }, + function () { return this.focusTrap.enabled; }, + set: /** + * @param {?} value + * @return {?} + */ + function (value) { this.focusTrap.enabled = _angular_cdk_coercion.coerceBooleanProperty(value); }, enumerable: true, configurable: true }); - Object.defineProperty(ListKeyManager.prototype, "activeItem", { - /** - * The active item. + Object.defineProperty(CdkTrapFocus.prototype, "autoCapture", { + get: /** + * Whether the directive should automatially move focus into the trapped region upon + * initialization and return focus to the previous activeElement upon destruction. * @return {?} */ - get: function () { - return this._activeItem; - }, + function () { return this._autoCapture; }, + set: /** + * @param {?} value + * @return {?} + */ + function (value) { this._autoCapture = _angular_cdk_coercion.coerceBooleanProperty(value); }, enumerable: true, configurable: true }); /** - * Sets the active item to the first enabled item in the list. - * @return {?} - */ - ListKeyManager.prototype.setFirstItemActive = function () { - this._setActiveItemByIndex(0, 1); - }; - /** - * Sets the active item to the last enabled item in the list. - * @return {?} - */ - ListKeyManager.prototype.setLastItemActive = function () { - this._setActiveItemByIndex(this._items.length - 1, -1); - }; - /** - * Sets the active item to the next enabled item in the list. - * @return {?} - */ - ListKeyManager.prototype.setNextItemActive = function () { - this._activeItemIndex < 0 ? this.setFirstItemActive() : this._setActiveItemByDelta(1); - }; - /** - * Sets the active item to a previous enabled item in the list. - * @return {?} - */ - ListKeyManager.prototype.setPreviousItemActive = function () { - this._activeItemIndex < 0 && this._wrap ? this.setLastItemActive() - : this._setActiveItemByDelta(-1); - }; - /** - * Allows setting of the activeItemIndex without any other effects. - * @param {?} index The new activeItemIndex. - * @return {?} - */ - ListKeyManager.prototype.updateActiveItemIndex = function (index) { - this._activeItemIndex = 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. - * @param {?} delta - * @param {?=} items * @return {?} */ - ListKeyManager.prototype._setActiveItemByDelta = function (delta, items) { - if (items === void 0) { items = this._items.toArray(); } - this._wrap ? this._setActiveInWrapMode(delta, items) - : this._setActiveInDefaultMode(delta, items); - }; - /** - * 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. - * @param {?} delta - * @param {?} items + CdkTrapFocus.prototype.ngOnDestroy = /** * @return {?} */ - ListKeyManager.prototype._setActiveInWrapMode = function (delta, items) { - // when active item would leave menu, wrap to beginning or end - this._activeItemIndex = - (this._activeItemIndex + delta + items.length) % items.length; - // skip all disabled menu items recursively until an enabled one is reached - if (items[this._activeItemIndex].disabled) { - this._setActiveInWrapMode(delta, items); - } - else { - this.setActiveItem(this._activeItemIndex); + 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; } }; /** - * 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. - * @param {?} delta - * @param {?} items * @return {?} */ - ListKeyManager.prototype._setActiveInDefaultMode = function (delta, items) { - this._setActiveItemByIndex(this._activeItemIndex + delta, delta, items); - }; - /** - * 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. - * @param {?} index - * @param {?} fallbackDelta - * @param {?=} items + CdkTrapFocus.prototype.ngAfterContentInit = /** * @return {?} */ - ListKeyManager.prototype._setActiveItemByIndex = function (index, fallbackDelta, items) { - if (items === void 0) { items = this._items.toArray(); } - if (!items[index]) { - return; - } - while (items[index].disabled) { - index += fallbackDelta; - if (!items[index]) { - return; - } + function () { + this.focusTrap.attachAnchors(); + if (this.autoCapture) { + this._previouslyFocusedElement = /** @type {?} */ (this._document.activeElement); + this.focusTrap.focusInitialElementWhenReady(); } - this.setActiveItem(index); }; - return ListKeyManager; + CdkTrapFocus.decorators = [ + { type: _angular_core.Directive, args: [{ + selector: '[cdkTrapFocus]', + exportAs: 'cdkTrapFocus', + },] }, + ]; + /** @nocollapse */ + CdkTrapFocus.ctorParameters = function () { return [ + { type: _angular_core.ElementRef, }, + { type: FocusTrapFactory, }, + { type: undefined, decorators: [{ type: _angular_core.Inject, args: [_angular_common.DOCUMENT,] },] }, + ]; }; + CdkTrapFocus.propDecorators = { + "enabled": [{ type: _angular_core.Input, args: ['cdkTrapFocus',] },], + "autoCapture": [{ type: _angular_core.Input, args: ['cdkTrapFocusAutoCapture',] },], + }; + return CdkTrapFocus; }()); -var ActiveDescendantKeyManager = (function (_super) { - __extends(ActiveDescendantKeyManager, _super); - function ActiveDescendantKeyManager() { - return _super !== null && _super.apply(this, arguments) || this; - } - /** - * This method sets the active item to the item at the specified index. - * It also adds active styles to the newly active item and removes active - * styles from the previously active item. - * @param {?} index - * @return {?} - */ - ActiveDescendantKeyManager.prototype.setActiveItem = 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} checked by tsc + */ /** * IDs are deliminated by an empty space, as per the spec. @@ -343,6 +953,17 @@ function getAriaReferenceIds(el, attr) { } /** + * @fileoverview added by tsickle + * @suppress {checkTypes} checked by tsc + */ + +/** + * Interface used to register message elements and keep a count of how many registrations have + * the same message and the reference to the message element used for the `aria-describedby`. + * @record + */ + +/** * ID used for the body container where all messages are appended. */ var MESSAGES_CONTAINER_ID = 'cdk-describedby-message-container'; @@ -372,13 +993,15 @@ var messagesContainer = null; * content. * \@docs-private */ -var AriaDescriber = (function () { +var AriaDescriber = /** @class */ (function () { + function AriaDescriber(_document) { + this._document = _document; + } /** - * @param {?} _platform + * 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. */ - function AriaDescriber(_platform) { - this._platform = _platform; - } /** * 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 @@ -387,890 +1010,810 @@ var AriaDescriber = (function () { * @param {?} message * @return {?} */ - AriaDescriber.prototype.describe = function (hostElement, message) { - if (!this._platform.isBrowser || !message.trim()) { - return; - } - if (!messageRegistry.has(message)) { - createMessageElement(message); - } - if (!isElementDescribedByMessage(hostElement, message)) { - addMessageReference(hostElement, message); - } - }; - /** - * Removes the host element's aria-describedby reference to the message element. + 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 {?} */ - AriaDescriber.prototype.removeDescription = function (hostElement, message) { - if (!this._platform.isBrowser || !message.trim()) { - return; - } - if (isElementDescribedByMessage(hostElement, message)) { - removeMessageReference(hostElement, message); - } - var /** @type {?} */ registeredMessage = messageRegistry.get(message); - if (registeredMessage && registeredMessage.referenceCount === 0) { - deleteMessageElement(message); - } - if (messagesContainer && messagesContainer.childNodes.length === 0) { - deleteMessagesContainer(); - } - }; - /** - * Unregisters all created message elements and removes the message container. - * @return {?} - */ - AriaDescriber.prototype.ngOnDestroy = function () { - if (!this._platform.isBrowser) { + function (hostElement, message) { + if (hostElement.nodeType !== this._document.ELEMENT_NODE || !message.trim()) { return; } - var /** @type {?} */ describedElements = document.querySelectorAll("[" + CDK_DESCRIBEDBY_HOST_ATTRIBUTE + "]"); - for (var /** @type {?} */ i = 0; i < describedElements.length; i++) { - removeCdkDescribedByReferenceIds(describedElements[i]); - describedElements[i].removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE); + if (!messageRegistry.has(message)) { + this._createMessageElement(message); } - if (messagesContainer) { - deleteMessagesContainer(); + if (!this._isElementDescribedByMessage(hostElement, message)) { + this._addMessageReference(hostElement, message); } - messageRegistry.clear(); }; - AriaDescriber.decorators = [ - { type: _angular_core.Injectable }, - ]; + /** Removes the host element's aria-describedby reference to the message element. */ /** - * @nocollapse - */ - AriaDescriber.ctorParameters = function () { return [ - { type: _angular_cdk_platform.Platform, }, - ]; }; - return AriaDescriber; -}()); -/** - * Creates a new element in the visually hidden message container element with the message - * as its content and adds it to the message registry. - * @param {?} message - * @return {?} - */ -function createMessageElement(message) { - var /** @type {?} */ messageElement = document.createElement('div'); - messageElement.setAttribute('id', CDK_DESCRIBEDBY_ID_PREFIX + "-" + nextId++); - messageElement.appendChild(/** @type {?} */ ((document.createTextNode(message)))); - if (!messagesContainer) { - createMessagesContainer(); - } /** @type {?} */ - ((messagesContainer)).appendChild(messageElement); - messageRegistry.set(message, { messageElement: messageElement, referenceCount: 0 }); -} -/** - * Deletes the message element from the global messages container. - * @param {?} message - * @return {?} - */ -function deleteMessageElement(message) { - var /** @type {?} */ registeredMessage = messageRegistry.get(message); - var /** @type {?} */ messageElement = registeredMessage && registeredMessage.messageElement; - if (messagesContainer && messageElement) { - messagesContainer.removeChild(messageElement); - } - messageRegistry.delete(message); -} -/** - * Creates the global container for all aria-describedby messages. - * @return {?} - */ -function createMessagesContainer() { - messagesContainer = document.createElement('div'); - messagesContainer.setAttribute('id', MESSAGES_CONTAINER_ID); - messagesContainer.setAttribute('aria-hidden', 'true'); - messagesContainer.style.display = 'none'; - document.body.appendChild(messagesContainer); -} -/** - * Deletes the global messages container. - * @return {?} - */ -function deleteMessagesContainer() { - document.body.removeChild(/** @type {?} */ ((messagesContainer))); - messagesContainer = null; -} -/** - * Removes all cdk-describedby messages that are hosted through the element. - * @param {?} element - * @return {?} - */ -function removeCdkDescribedByReferenceIds(element) { - // Remove all aria-describedby reference IDs that are prefixed by CDK_DESCRIBEDBY_ID_PREFIX - var /** @type {?} */ originalReferenceIds = getAriaReferenceIds(element, 'aria-describedby') - .filter(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. - * @param {?} element - * @param {?} message - * @return {?} - */ -function addMessageReference(element, message) { - var /** @type {?} */ registeredMessage = ((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. - * @param {?} element - * @param {?} message - * @return {?} - */ -function removeMessageReference(element, message) { - var /** @type {?} */ registeredMessage = ((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. - * @param {?} element - * @param {?} message - * @return {?} - */ -function isElementDescribedByMessage(element, message) { - var /** @type {?} */ referenceIds = getAriaReferenceIds(element, 'aria-describedby'); - var /** @type {?} */ registeredMessage = messageRegistry.get(message); - var /** @type {?} */ messageId = registeredMessage && registeredMessage.messageElement.id; - return !!messageId && referenceIds.indexOf(messageId) != -1; -} -/** - * \@docs-private - * @param {?} parentDispatcher - * @param {?} platform - * @return {?} - */ -function ARIA_DESCRIBER_PROVIDER_FACTORY(parentDispatcher, platform) { - return parentDispatcher || new AriaDescriber(platform); -} -/** - * \@docs-private - */ -var ARIA_DESCRIBER_PROVIDER = { - // If there is already an AriaDescriber available, use that. Otherwise, provide a new one. - provide: AriaDescriber, - deps: [ - [new _angular_core.Optional(), new _angular_core.SkipSelf(), AriaDescriber], - _angular_cdk_platform.Platform - ], - useFactory: ARIA_DESCRIBER_PROVIDER_FACTORY -}; - -/** - * 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; -} - -var FocusKeyManager = (function (_super) { - __extends(FocusKeyManager, _super); - function FocusKeyManager() { - return _super !== null && _super.apply(this, arguments) || this; - } + * 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 (hostElement.nodeType !== this._document.ELEMENT_NODE || !message.trim()) { + return; + } + if (this._isElementDescribedByMessage(hostElement, message)) { + this._removeMessageReference(hostElement, message); + } + var /** @type {?} */ 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. */ /** - * This method sets the active item to the item at the specified index. - * It also adds focuses the newly active item. - * @param {?} index + * Unregisters all created message elements and removes the message container. * @return {?} */ - FocusKeyManager.prototype.setActiveItem = function (index) { - _super.prototype.setActiveItem.call(this, index); - if (this.activeItem) { - this.activeItem.focus(); + AriaDescriber.prototype.ngOnDestroy = /** + * Unregisters all created message elements and removes the message container. + * @return {?} + */ + function () { + var /** @type {?} */ describedElements = this._document.querySelectorAll("[" + CDK_DESCRIBEDBY_HOST_ATTRIBUTE + "]"); + for (var /** @type {?} */ i = 0; i < describedElements.length; i++) { + this._removeCdkDescribedByReferenceIds(describedElements[i]); + describedElements[i].removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE); + } + if (messagesContainer) { + this._deleteMessagesContainer(); } + messageRegistry.clear(); }; - return FocusKeyManager; -}(ListKeyManager)); - -/** - * Utility for checking the interactivity of an element, such as whether is is focusable or - * tabbable. - */ -var InteractivityChecker = (function () { /** - * @param {?} _platform + * Creates a new element in the visually hidden message container element with the message + * as its content and adds it to the message registry. + * @param {?} message + * @return {?} */ - function InteractivityChecker(_platform) { - this._platform = _platform; - } + 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. + * @param {?} message + * @return {?} + */ + function (message) { + var /** @type {?} */ messageElement = this._document.createElement('div'); + messageElement.setAttribute('id', CDK_DESCRIBEDBY_ID_PREFIX + "-" + nextId++); + messageElement.appendChild(/** @type {?} */ ((this._document.createTextNode(message)))); + if (!messagesContainer) { + this._createMessagesContainer(); + } /** @type {?} */ + ((messagesContainer)).appendChild(messageElement); + messageRegistry.set(message, { messageElement: messageElement, referenceCount: 0 }); + }; /** - * Gets whether an element is disabled. - * - * @param {?} element Element to be checked. - * @return {?} Whether the element is disabled. + * Deletes the message element from the global messages container. + * @param {?} message + * @return {?} */ - InteractivityChecker.prototype.isDisabled = 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'); + AriaDescriber.prototype._deleteMessageElement = /** + * Deletes the message element from the global messages container. + * @param {?} message + * @return {?} + */ + function (message) { + var /** @type {?} */ registeredMessage = messageRegistry.get(message); + var /** @type {?} */ messageElement = registeredMessage && registeredMessage.messageElement; + if (messagesContainer && messageElement) { + messagesContainer.removeChild(messageElement); + } + messageRegistry.delete(message); }; /** - * 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. + * Creates the global container for all aria-describedby messages. + * @return {?} */ - InteractivityChecker.prototype.isVisible = function (element) { - return hasGeometry(element) && getComputedStyle(element).visibility === 'visible'; + AriaDescriber.prototype._createMessagesContainer = /** + * Creates the global container for all aria-describedby messages. + * @return {?} + */ + function () { + messagesContainer = this._document.createElement('div'); + messagesContainer.setAttribute('id', MESSAGES_CONTAINER_ID); + messagesContainer.setAttribute('aria-hidden', 'true'); + messagesContainer.style.display = 'none'; + this._document.body.appendChild(messagesContainer); }; /** - * 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. + * Deletes the global messages container. + * @return {?} */ - InteractivityChecker.prototype.isTabbable = function (element) { - // Nothing is tabbable on the the server ð - if (!this._platform.isBrowser) { - return false; - } - var /** @type {?} */ frameElement = (getWindow(element).frameElement); - if (frameElement) { - var /** @type {?} */ 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; - } - } - var /** @type {?} */ nodeName = element.nodeName.toLowerCase(); - var /** @type {?} */ 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; + AriaDescriber.prototype._deleteMessagesContainer = /** + * Deletes the global messages container. + * @return {?} + */ + function () { + if (messagesContainer && messagesContainer.parentNode) { + messagesContainer.parentNode.removeChild(messagesContainer); + messagesContainer = null; } - return element.tabIndex >= 0; }; /** - * Gets whether an element can be focused by the user. - * - * @param {?} element Element to be checked. - * @return {?} Whether the element is focusable. + * Removes all cdk-describedby messages that are hosted through the element. + * @param {?} element + * @return {?} */ - InteractivityChecker.prototype.isFocusable = 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); + AriaDescriber.prototype._removeCdkDescribedByReferenceIds = /** + * Removes all cdk-describedby messages that are hosted through the element. + * @param {?} element + * @return {?} + */ + function (element) { + // Remove all aria-describedby reference IDs that are prefixed by CDK_DESCRIBEDBY_ID_PREFIX + var /** @type {?} */ originalReferenceIds = getAriaReferenceIds(element, 'aria-describedby') + .filter(function (id) { return id.indexOf(CDK_DESCRIBEDBY_ID_PREFIX) != 0; }); + element.setAttribute('aria-describedby', originalReferenceIds.join(' ')); }; - InteractivityChecker.decorators = [ - { type: _angular_core.Injectable }, - ]; /** - * @nocollapse + * Adds a message reference to the element using aria-describedby and increments the registered + * message's reference count. + * @param {?} element + * @param {?} message + * @return {?} */ - InteractivityChecker.ctorParameters = function () { return [ - { type: _angular_cdk_platform.Platform, }, + AriaDescriber.prototype._addMessageReference = /** + * Adds a message reference to the element using aria-describedby and increments the registered + * message's reference count. + * @param {?} element + * @param {?} message + * @return {?} + */ + function (element, message) { + var /** @type {?} */ 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. + * @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. + * @param {?} element + * @param {?} message + * @return {?} + */ + function (element, message) { + var /** @type {?} */ 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. + * @param {?} element + * @param {?} message + * @return {?} + */ + AriaDescriber.prototype._isElementDescribedByMessage = /** + * Returns true if the element has been described by the provided message ID. + * @param {?} element + * @param {?} message + * @return {?} + */ + function (element, message) { + var /** @type {?} */ referenceIds = getAriaReferenceIds(element, 'aria-describedby'); + var /** @type {?} */ registeredMessage = messageRegistry.get(message); + var /** @type {?} */ messageId = registeredMessage && registeredMessage.messageElement.id; + return !!messageId && referenceIds.indexOf(messageId) != -1; + }; + AriaDescriber.decorators = [ + { type: _angular_core.Injectable }, + ]; + /** @nocollapse */ + AriaDescriber.ctorParameters = function () { return [ + { type: undefined, decorators: [{ type: _angular_core.Inject, args: [_angular_common.DOCUMENT,] },] }, ]; }; - return InteractivityChecker; + return AriaDescriber; }()); /** - * 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 || element.getClientRects().length); -} -/** - * Gets whether an element's - * @param {?} element - * @return {?} - */ -function isNativeFormElement(element) { - var /** @type {?} */ 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; - } - var /** @type {?} */ 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 + * \@docs-private + * @param {?} parentDispatcher + * @param {?} _document * @return {?} */ -function getTabIndexValue(element) { - if (!hasValidTabIndex(element)) { - return null; - } - // See browser issue in Gecko https://bugzilla.mozilla.org/show_bug.cgi?id=1128054 - var /** @type {?} */ tabIndex = parseInt(element.getAttribute('tabindex') || '', 10); - return isNaN(tabIndex) ? -1 : tabIndex; +function ARIA_DESCRIBER_PROVIDER_FACTORY(parentDispatcher, _document) { + return parentDispatcher || new AriaDescriber(_document); } /** - * Checks whether the specified element is potentially tabbable on iOS - * @param {?} element - * @return {?} + * \@docs-private */ -function isPotentiallyTabbableIOS(element) { - var /** @type {?} */ nodeName = element.nodeName.toLowerCase(); - var /** @type {?} */ inputType = nodeName === 'input' && ((element)).type; - return inputType === 'text' - || inputType === 'password' - || nodeName === 'select' - || nodeName === 'textarea'; -} +var ARIA_DESCRIBER_PROVIDER = { + // If there is already an AriaDescriber available, use that. Otherwise, provide a new one. + provide: AriaDescriber, + deps: [ + [new _angular_core.Optional(), new _angular_core.SkipSelf(), AriaDescriber], + /** @type {?} */ (_angular_common.DOCUMENT) + ], + useFactory: ARIA_DESCRIBER_PROVIDER_FACTORY +}; + /** - * Gets whether an element is potentially focusable without taking current visible/disabled state - * into account. - * @param {?} element - * @return {?} + * @fileoverview added by tsickle + * @suppress {checkTypes} checked by tsc */ -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 {?} + * This interface is for items that can be passed to a ListKeyManager. + * @record */ -function getWindow(node) { - return node.ownerDocument.defaultView || window; -} /** - * Class that allows for trapping focus within a DOM element. - * - * NOTE: This class currently uses a very simple (naive) 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. - * This will be replaced with a more intelligent solution before the library is considered stable. + * 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. */ -var FocusTrap = (function () { - /** - * @param {?} _element - * @param {?} _platform - * @param {?} _checker - * @param {?} _ngZone - * @param {?=} deferAnchors - */ - function FocusTrap(_element, _platform, _checker, _ngZone, deferAnchors) { - if (deferAnchors === void 0) { deferAnchors = false; } - this._element = _element; - this._platform = _platform; - this._checker = _checker; - this._ngZone = _ngZone; - this._enabled = true; - if (!deferAnchors) { - this.attachAnchors(); - } - } - Object.defineProperty(FocusTrap.prototype, "enabled", { +var ListKeyManager = /** @class */ (function () { + function ListKeyManager(_items) { + var _this = this; + this._items = _items; + this._activeItemIndex = -1; + this._wrap = false; + this._letterKeyStream = new rxjs_Subject.Subject(); + this._typeaheadSubscription = rxjs_Subscription.Subscription.EMPTY; + this._vertical = true; + this._pressedLetters = []; /** - * Whether the focus trap is active. - * @return {?} + * Stream that emits any time the TAB key is pressed, so components can react + * when focus is shifted off of the list. */ - get: function () { return this._enabled; }, + this.tabOut = new rxjs_Subject.Subject(); /** - * @param {?} val - * @return {?} + * Stream that emits whenever the active item of the list manager changes. */ - set: function (val) { - this._enabled = val; - if (this._startAnchor && this._endAnchor) { - this._startAnchor.tabIndex = this._endAnchor.tabIndex = this._enabled ? 0 : -1; + this.change = new rxjs_Subject.Subject(); + _items.changes.subscribe(function (newItems) { + if (_this._activeItem) { + var /** @type {?} */ itemArray = newItems.toArray(); + var /** @type {?} */ newIndex = itemArray.indexOf(_this._activeItem); + if (newIndex > -1 && newIndex !== _this._activeItemIndex) { + _this._activeItemIndex = newIndex; + } } - }, - enumerable: true, - configurable: true - }); + }); + } /** - * Destroys the focus trap by cleaning up the anchors. + * Turns on wrapping mode, which ensures that the active item will wrap to + * the other end of list when there are no more items in the given direction. + */ + /** + * Turns on wrapping mode, which ensures that the active item will wrap to + * the other end of list when there are no more items in the given direction. * @return {?} */ - FocusTrap.prototype.destroy = function () { - if (this._startAnchor && this._startAnchor.parentNode) { - this._startAnchor.parentNode.removeChild(this._startAnchor); - } - if (this._endAnchor && this._endAnchor.parentNode) { - this._endAnchor.parentNode.removeChild(this._endAnchor); - } - this._startAnchor = this._endAnchor = null; + ListKeyManager.prototype.withWrap = /** + * Turns on wrapping mode, which ensures that the active item will wrap to + * the other end of list when there are no more items in the given direction. + * @return {?} + */ + function () { + this._wrap = true; + return this; }; /** - * Inserts the anchors into the DOM. This is usually done automatically - * in the constructor, but can be deferred for cases like directives with `*ngIf`. + * 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. + * @param {?=} enabled Whether vertical selection should be enabled. + * @return {?} + */ + ListKeyManager.prototype.withVerticalOrientation = /** + * Configures whether the key manager should be able to move the selection vertically. + * @param {?=} enabled Whether vertical selection should be enabled. + * @return {?} + */ + function (enabled) { + if (enabled === void 0) { enabled = true; } + this._vertical = enabled; + return 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. + * @param {?} direction Direction in which the selection can be moved. + * @return {?} + */ + ListKeyManager.prototype.withHorizontalOrientation = /** + * 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. + * @return {?} + */ + function (direction) { + this._horizontal = direction; + return 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. + * @param {?=} debounceInterval Time to wait after the last keystroke before setting the active item. + * @return {?} + */ + ListKeyManager.prototype.withTypeAhead = /** + * 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. * @return {?} */ - FocusTrap.prototype.attachAnchors = function () { + function (debounceInterval) { var _this = this; - // If we're not on the browser, there can be no focus to trap. - if (!this._platform.isBrowser) { - return; - } - if (!this._startAnchor) { - this._startAnchor = this._createAnchor(); - } - if (!this._endAnchor) { - this._endAnchor = this._createAnchor(); + if (debounceInterval === void 0) { debounceInterval = 200; } + if (this._items.length && this._items.some(function (item) { return typeof item.getLabel !== 'function'; })) { + throw Error('ListKeyManager items in typeahead mode must implement the `getLabel` method.'); } - this._ngZone.runOutsideAngular(function () { - ((_this._startAnchor)).addEventListener('focus', function () { - _this.focusLastTabbableElement(); - }); /** @type {?} */ - ((_this._endAnchor)).addEventListener('focus', function () { - _this.focusFirstTabbableElement(); - }); - if (_this._element.parentNode) { - _this._element.parentNode.insertBefore(/** @type {?} */ ((_this._startAnchor)), _this._element); - _this._element.parentNode.insertBefore(/** @type {?} */ ((_this._endAnchor)), _this._element.nextSibling); + 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. + this._typeaheadSubscription = this._letterKeyStream.pipe(rxjs_operators_tap.tap(function (keyCode) { return _this._pressedLetters.push(keyCode); }), rxjs_operators_debounceTime.debounceTime(debounceInterval), rxjs_operators_filter.filter(function () { return _this._pressedLetters.length > 0; }), rxjs_operators_map.map(function () { return _this._pressedLetters.join(''); })).subscribe(function (inputString) { + var /** @type {?} */ items = _this._items.toArray(); + // Start at 1 because we want to start searching at the item immediately + // following the current active item. + for (var /** @type {?} */ i = 1; i < items.length + 1; i++) { + var /** @type {?} */ index = (_this._activeItemIndex + i) % items.length; + var /** @type {?} */ item = items[index]; + if (!item.disabled && /** @type {?} */ ((item.getLabel))().toUpperCase().trim().indexOf(inputString) === 0) { + _this.setActiveItem(index); + break; + } } + _this._pressedLetters = []; }); + return this; }; /** - * 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. + * Sets the active item to the item at the index specified. + * @param index The index of the item to be set as active. */ - FocusTrap.prototype.focusInitialElementWhenReady = function () { - var _this = this; - return new Promise(function (resolve) { - _this._executeOnStable(function () { return resolve(_this.focusInitialElement()); }); - }); + /** + * Sets the active item to the item at the index specified. + * @param {?} index The index of the item to be set as active. + * @return {?} + */ + ListKeyManager.prototype.setActiveItem = /** + * Sets the active item to the item at the index specified. + * @param {?} index The index of the item to be set as active. + * @return {?} + */ + function (index) { + var /** @type {?} */ previousIndex = this._activeItemIndex; + this._activeItemIndex = index; + this._activeItem = this._items.toArray()[index]; + if (this._activeItemIndex !== previousIndex) { + this.change.next(index); + } + }; + /** + * 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 /** @type {?} */ keyCode = event.keyCode; + switch (keyCode) { + case _angular_cdk_keycodes.TAB: + this.tabOut.next(); + return; + case _angular_cdk_keycodes.DOWN_ARROW: + if (this._vertical) { + this.setNextItemActive(); + break; + } + case _angular_cdk_keycodes.UP_ARROW: + if (this._vertical) { + this.setPreviousItemActive(); + break; + } + case _angular_cdk_keycodes.RIGHT_ARROW: + if (this._horizontal === 'ltr') { + this.setNextItemActive(); + break; + } + else if (this._horizontal === 'rtl') { + this.setPreviousItemActive(); + break; + } + case _angular_cdk_keycodes.LEFT_ARROW: + if (this._horizontal === 'ltr') { + this.setPreviousItemActive(); + break; + } + else if (this._horizontal === 'rtl') { + this.setNextItemActive(); + break; + } + default: + // 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 >= _angular_cdk_keycodes.A && keyCode <= _angular_cdk_keycodes.Z) || (keyCode >= _angular_cdk_keycodes.ZERO && keyCode <= _angular_cdk_keycodes.NINE)) { + this._letterKeyStream.next(String.fromCharCode(keyCode)); + } + // Note that we return here, in order to avoid preventing + // the default action of non-navigational keys. +
<TRUNCATED>
