@angular/cdk 2.0.0-beta.11 → 2.0.0-beta.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_a11y.scss +23 -0
- package/_overlay.scss +93 -0
- package/a11y/index.metadata.json +2 -1
- package/a11y/typings/a11y-module.d.ts +2 -0
- package/a11y/typings/focus-monitor.d.ts +1 -1
- package/a11y/typings/index.d.ts +1 -1
- package/a11y/typings/index.metadata.json +1 -1
- package/a11y/typings/{public_api.d.ts → public-api.d.ts} +8 -2
- package/a11y-prebuilt.css +1 -0
- package/a11y.metadata.json +2 -1
- package/bidi/index.metadata.json +2 -1
- package/bidi/typings/bidi-module.d.ts +2 -0
- package/bidi/typings/index.d.ts +1 -1
- package/bidi/typings/index.metadata.json +1 -1
- package/bidi/typings/public-api.d.ts +10 -0
- package/bidi.metadata.json +2 -1
- package/bundles/cdk-a11y.umd.js +1368 -1357
- package/bundles/cdk-a11y.umd.js.map +1 -1
- package/bundles/cdk-a11y.umd.min.js +2 -2
- package/bundles/cdk-a11y.umd.min.js.map +1 -1
- package/bundles/cdk-bidi.umd.js +42 -40
- package/bundles/cdk-bidi.umd.js.map +1 -1
- package/bundles/cdk-bidi.umd.min.js +2 -2
- package/bundles/cdk-bidi.umd.min.js.map +1 -1
- package/bundles/cdk-coercion.umd.js +12 -0
- package/bundles/cdk-coercion.umd.js.map +1 -1
- package/bundles/cdk-coercion.umd.min.js +2 -2
- package/bundles/cdk-coercion.umd.min.js.map +1 -1
- package/bundles/cdk-collections.umd.js +113 -11
- package/bundles/cdk-collections.umd.js.map +1 -1
- package/bundles/cdk-collections.umd.min.js +2 -2
- package/bundles/cdk-collections.umd.min.js.map +1 -1
- package/bundles/cdk-keycodes.umd.js.map +1 -1
- package/bundles/cdk-keycodes.umd.min.js +1 -1
- package/bundles/cdk-keycodes.umd.min.js.map +1 -1
- package/bundles/cdk-layout.umd.js +235 -0
- package/bundles/cdk-layout.umd.js.map +1 -0
- package/bundles/cdk-layout.umd.min.js +9 -0
- package/bundles/cdk-layout.umd.min.js.map +1 -0
- package/bundles/cdk-observers.umd.js +41 -40
- package/bundles/cdk-observers.umd.js.map +1 -1
- package/bundles/cdk-observers.umd.min.js +2 -2
- package/bundles/cdk-observers.umd.min.js.map +1 -1
- package/bundles/cdk-overlay.umd.js +306 -265
- package/bundles/cdk-overlay.umd.js.map +1 -1
- package/bundles/cdk-overlay.umd.min.js +2 -2
- package/bundles/cdk-overlay.umd.min.js.map +1 -1
- package/bundles/cdk-platform.umd.js +19 -17
- package/bundles/cdk-platform.umd.js.map +1 -1
- package/bundles/cdk-platform.umd.min.js +2 -2
- package/bundles/cdk-platform.umd.min.js.map +1 -1
- package/bundles/cdk-portal.umd.js +71 -37
- package/bundles/cdk-portal.umd.js.map +1 -1
- package/bundles/cdk-portal.umd.min.js +2 -2
- package/bundles/cdk-portal.umd.min.js.map +1 -1
- package/bundles/cdk-rxjs.umd.js +13 -4
- package/bundles/cdk-rxjs.umd.js.map +1 -1
- package/bundles/cdk-rxjs.umd.min.js +2 -2
- package/bundles/cdk-rxjs.umd.min.js.map +1 -1
- package/bundles/cdk-scrolling.umd.js +89 -54
- package/bundles/cdk-scrolling.umd.js.map +1 -1
- package/bundles/cdk-scrolling.umd.min.js +2 -2
- package/bundles/cdk-scrolling.umd.min.js.map +1 -1
- package/bundles/cdk-stepper.umd.js +115 -90
- package/bundles/cdk-stepper.umd.js.map +1 -1
- package/bundles/cdk-stepper.umd.min.js +2 -2
- package/bundles/cdk-stepper.umd.min.js.map +1 -1
- package/bundles/cdk-table.umd.js +261 -218
- package/bundles/cdk-table.umd.js.map +1 -1
- package/bundles/cdk-table.umd.min.js +2 -2
- package/bundles/cdk-table.umd.min.js.map +1 -1
- package/bundles/cdk.umd.js +1 -1
- package/bundles/cdk.umd.js.map +1 -1
- package/bundles/cdk.umd.min.js +2 -2
- package/bundles/cdk.umd.min.js.map +1 -1
- package/cdk.metadata.json +2 -1
- package/coercion/index.metadata.json +2 -1
- package/coercion/typings/array.d.ts +9 -0
- package/coercion/typings/index.d.ts +1 -1
- package/coercion/typings/index.metadata.json +1 -1
- package/{typings/coercion/public_api.d.ts → coercion/typings/public-api.d.ts} +1 -0
- package/coercion.metadata.json +2 -1
- package/collections/index.metadata.json +2 -1
- package/collections/typings/index.d.ts +2 -1
- package/collections/typings/index.metadata.json +1 -1
- package/{typings/collections/public_api.d.ts → collections/typings/public-api.d.ts} +1 -0
- package/collections/typings/selection.d.ts +13 -3
- package/collections/typings/unique-selection-dispatcher.d.ts +40 -0
- package/collections.metadata.json +2 -1
- package/esm2015/a11y.js +1252 -1250
- package/esm2015/a11y.js.map +1 -1
- package/esm2015/bidi.js +1 -1
- package/esm2015/bidi.js.map +1 -1
- package/esm2015/cdk.js +1 -1
- package/esm2015/cdk.js.map +1 -1
- package/esm2015/coercion.js +11 -1
- package/esm2015/coercion.js.map +1 -1
- package/esm2015/collections.js +93 -8
- package/esm2015/collections.js.map +1 -1
- package/esm2015/keycodes.js.map +1 -1
- package/esm2015/layout.js +226 -0
- package/esm2015/layout.js.map +1 -0
- package/esm2015/observers.js +8 -7
- package/esm2015/observers.js.map +1 -1
- package/esm2015/overlay.js +157 -136
- package/esm2015/overlay.js.map +1 -1
- package/esm2015/platform.js +1 -1
- package/esm2015/platform.js.map +1 -1
- package/esm2015/portal.js +30 -1
- package/esm2015/portal.js.map +1 -1
- package/esm2015/rxjs.js +5 -1
- package/esm2015/rxjs.js.map +1 -1
- package/esm2015/scrolling.js +39 -8
- package/esm2015/scrolling.js.map +1 -1
- package/esm2015/stepper.js +27 -5
- package/esm2015/stepper.js.map +1 -1
- package/esm2015/table.js +68 -29
- package/esm2015/table.js.map +1 -1
- package/esm5/a11y.es5.js +1372 -1357
- package/esm5/a11y.es5.js.map +1 -1
- package/esm5/bidi.es5.js +45 -40
- package/esm5/bidi.es5.js.map +1 -1
- package/esm5/cdk.es5.js +4 -1
- package/esm5/cdk.es5.js.map +1 -1
- package/esm5/coercion.es5.js +14 -1
- package/esm5/coercion.es5.js.map +1 -1
- package/esm5/collections.es5.js +110 -8
- package/esm5/collections.es5.js.map +1 -1
- package/esm5/keycodes.es5.js +2 -0
- package/esm5/keycodes.es5.js.map +1 -1
- package/esm5/layout.es5.js +234 -0
- package/esm5/layout.es5.js.map +1 -0
- package/esm5/observers.es5.js +44 -40
- package/esm5/observers.es5.js.map +1 -1
- package/esm5/overlay.es5.js +304 -259
- package/esm5/overlay.es5.js.map +1 -1
- package/esm5/platform.es5.js +22 -17
- package/esm5/platform.es5.js.map +1 -1
- package/esm5/portal.es5.js +81 -44
- package/esm5/portal.es5.js.map +1 -1
- package/esm5/rxjs.es5.js +12 -1
- package/esm5/rxjs.es5.js.map +1 -1
- package/esm5/scrolling.es5.js +89 -51
- package/esm5/scrolling.es5.js.map +1 -1
- package/esm5/stepper.es5.js +119 -91
- package/esm5/stepper.es5.js.map +1 -1
- package/esm5/table.es5.js +265 -218
- package/esm5/table.es5.js.map +1 -1
- package/keycodes/index.metadata.json +2 -1
- package/keycodes/typings/index.d.ts +1 -1
- package/keycodes/typings/{public_api.d.ts → public-api.d.ts} +0 -0
- package/keycodes.metadata.json +2 -1
- package/layout/index.d.ts +8 -0
- package/layout/index.metadata.json +12 -0
- package/layout/package.json +7 -0
- package/layout/typings/breakpoints-observer.d.ts +37 -0
- package/layout/typings/breakpoints.d.ts +18 -0
- package/layout/typings/index.d.ts +4 -0
- package/layout/typings/index.metadata.json +1 -0
- package/layout/typings/media-matcher.d.ts +15 -0
- package/layout/typings/public-api.d.ts +5 -0
- package/layout.d.ts +8 -0
- package/layout.metadata.json +12 -0
- package/observers/index.metadata.json +2 -1
- package/observers/typings/index.d.ts +1 -1
- package/observers/typings/index.metadata.json +1 -1
- package/observers/typings/observe-content.d.ts +3 -3
- package/observers/typings/{public_api.d.ts → public-api.d.ts} +0 -0
- package/observers.metadata.json +2 -1
- package/overlay/index.metadata.json +2 -1
- package/overlay/typings/index.d.ts +2 -2
- package/overlay/typings/index.metadata.json +1 -1
- package/overlay/typings/overlay-config.d.ts +1 -1
- package/overlay/typings/overlay-directives.d.ts +3 -3
- package/overlay/typings/overlay-module.d.ts +11 -0
- package/overlay/typings/overlay-ref.d.ts +6 -6
- package/overlay/typings/overlay.d.ts +2 -2
- package/overlay/typings/position/connected-position-strategy.d.ts +5 -0
- package/overlay/typings/position/position-strategy.d.ts +2 -0
- package/overlay/typings/{public_api.d.ts → public-api.d.ts} +1 -4
- package/overlay/typings/scroll/scroll-strategy.d.ts +1 -1
- package/overlay-prebuilt.css +1 -0
- package/overlay.metadata.json +2 -1
- package/package.json +3 -3
- package/platform/index.metadata.json +2 -1
- package/platform/typings/index.d.ts +1 -1
- package/platform/typings/index.metadata.json +1 -1
- package/platform/typings/platform-module.d.ts +2 -0
- package/platform/typings/public-api.d.ts +10 -0
- package/platform.metadata.json +2 -1
- package/portal/index.metadata.json +2 -1
- package/portal/typings/index.d.ts +1 -1
- package/portal/typings/index.metadata.json +1 -1
- package/portal/typings/{public_api.d.ts → public-api.d.ts} +1 -0
- package/portal.metadata.json +2 -1
- package/rxjs/index.metadata.json +2 -1
- package/rxjs/typings/index.d.ts +1 -1
- package/rxjs/typings/index.metadata.json +1 -1
- package/rxjs/typings/{public_api.d.ts → public-api.d.ts} +0 -0
- package/rxjs/typings/rx-operators.d.ts +7 -0
- package/rxjs.metadata.json +2 -1
- package/scrolling/index.metadata.json +2 -1
- package/scrolling/typings/index.d.ts +1 -1
- package/scrolling/typings/index.metadata.json +1 -1
- package/scrolling/typings/public-api.d.ts +11 -0
- package/scrolling/typings/scrolling-module.d.ts +2 -0
- package/scrolling/typings/viewport-ruler.d.ts +20 -6
- package/scrolling.metadata.json +2 -1
- package/stepper/index.metadata.json +2 -1
- package/stepper/typings/index.d.ts +1 -1
- package/stepper/typings/index.metadata.json +1 -1
- package/stepper/typings/public-api.d.ts +11 -0
- package/stepper/typings/stepper-module.d.ts +2 -0
- package/stepper/typings/stepper.d.ts +8 -4
- package/stepper.metadata.json +2 -1
- package/table/index.metadata.json +2 -1
- package/table/typings/index.d.ts +1 -1
- package/table/typings/index.metadata.json +1 -1
- package/table/typings/public-api.d.ts +13 -0
- package/table/typings/row.d.ts +11 -3
- package/table/typings/table-errors.d.ts +10 -0
- package/table/typings/table-module.d.ts +2 -0
- package/table/typings/table.d.ts +17 -8
- package/table.metadata.json +2 -1
- package/typings/a11y/a11y-module.d.ts +2 -0
- package/typings/a11y/focus-monitor.d.ts +1 -1
- package/typings/a11y/index.d.ts +1 -1
- package/typings/a11y/index.metadata.json +1 -1
- package/typings/a11y/{public_api.d.ts → public-api.d.ts} +8 -2
- package/typings/bidi/bidi-module.d.ts +2 -0
- package/typings/bidi/index.d.ts +1 -1
- package/typings/bidi/index.metadata.json +1 -1
- package/typings/bidi/public-api.d.ts +10 -0
- package/typings/coercion/array.d.ts +9 -0
- package/typings/coercion/index.d.ts +1 -1
- package/typings/coercion/index.metadata.json +1 -1
- package/{coercion/typings/public_api.d.ts → typings/coercion/public-api.d.ts} +1 -0
- package/typings/collections/index.d.ts +2 -1
- package/typings/collections/index.metadata.json +1 -1
- package/{collections/typings/public_api.d.ts → typings/collections/public-api.d.ts} +1 -0
- package/typings/collections/selection.d.ts +13 -3
- package/typings/collections/unique-selection-dispatcher.d.ts +40 -0
- package/typings/index.d.ts +1 -1
- package/typings/index.metadata.json +1 -1
- package/typings/keycodes/index.d.ts +1 -1
- package/typings/keycodes/{public_api.d.ts → public-api.d.ts} +0 -0
- package/typings/layout/breakpoints-observer.d.ts +37 -0
- package/typings/layout/breakpoints.d.ts +18 -0
- package/typings/layout/index.d.ts +4 -0
- package/typings/layout/index.metadata.json +1 -0
- package/typings/layout/media-matcher.d.ts +15 -0
- package/typings/layout/public-api.d.ts +5 -0
- package/typings/observers/index.d.ts +1 -1
- package/typings/observers/index.metadata.json +1 -1
- package/typings/observers/observe-content.d.ts +3 -3
- package/typings/observers/{public_api.d.ts → public-api.d.ts} +0 -0
- package/typings/overlay/index.d.ts +2 -2
- package/typings/overlay/index.metadata.json +1 -1
- package/typings/overlay/overlay-config.d.ts +1 -1
- package/typings/overlay/overlay-directives.d.ts +3 -3
- package/typings/overlay/overlay-module.d.ts +11 -0
- package/typings/overlay/overlay-ref.d.ts +6 -6
- package/typings/overlay/overlay.d.ts +2 -2
- package/typings/overlay/position/connected-position-strategy.d.ts +5 -0
- package/typings/overlay/position/position-strategy.d.ts +2 -0
- package/typings/overlay/{public_api.d.ts → public-api.d.ts} +1 -4
- package/typings/overlay/scroll/scroll-strategy.d.ts +1 -1
- package/typings/platform/index.d.ts +1 -1
- package/typings/platform/index.metadata.json +1 -1
- package/typings/platform/platform-module.d.ts +2 -0
- package/typings/platform/public-api.d.ts +10 -0
- package/typings/portal/index.d.ts +1 -1
- package/typings/portal/index.metadata.json +1 -1
- package/typings/portal/{public_api.d.ts → public-api.d.ts} +1 -0
- package/typings/{public_api.d.ts → public-api.d.ts} +0 -0
- package/typings/rxjs/index.d.ts +1 -1
- package/typings/rxjs/index.metadata.json +1 -1
- package/typings/rxjs/{public_api.d.ts → public-api.d.ts} +0 -0
- package/typings/rxjs/rx-operators.d.ts +7 -0
- package/typings/scrolling/index.d.ts +1 -1
- package/typings/scrolling/index.metadata.json +1 -1
- package/typings/scrolling/public-api.d.ts +11 -0
- package/typings/scrolling/scrolling-module.d.ts +2 -0
- package/typings/scrolling/viewport-ruler.d.ts +20 -6
- package/typings/stepper/index.d.ts +1 -1
- package/typings/stepper/index.metadata.json +1 -1
- package/typings/stepper/public-api.d.ts +11 -0
- package/typings/stepper/stepper-module.d.ts +2 -0
- package/typings/stepper/stepper.d.ts +8 -4
- package/typings/table/index.d.ts +1 -1
- package/typings/table/index.metadata.json +1 -1
- package/typings/table/public-api.d.ts +13 -0
- package/typings/table/row.d.ts +11 -3
- package/typings/table/table-errors.d.ts +10 -0
- package/typings/table/table-module.d.ts +2 -0
- package/typings/table/table.d.ts +17 -8
- package/bidi/typings/public_api.d.ts +0 -4
- package/platform/typings/public_api.d.ts +0 -4
- package/scrolling/typings/public_api.d.ts +0 -5
- package/stepper/typings/public_api.d.ts +0 -5
- package/table/typings/public_api.d.ts +0 -7
- package/typings/bidi/public_api.d.ts +0 -4
- package/typings/platform/public_api.d.ts +0 -4
- package/typings/scrolling/public_api.d.ts +0 -5
- package/typings/stepper/public_api.d.ts +0 -5
- package/typings/table/public_api.d.ts +0 -7
package/bundles/cdk-a11y.umd.js
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* found in the LICENSE file at https://angular.io/license
|
|
7
7
|
*/
|
|
8
8
|
(function (global, factory) {
|
|
9
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('
|
|
10
|
-
typeof define === 'function' && define.amd ? define(['exports', '
|
|
11
|
-
(factory((global.ng = global.ng || {}, global.ng.cdk = global.ng.cdk || {}, global.ng.cdk.a11y = global.ng.cdk.a11y || {}),global.
|
|
12
|
-
}(this, (function (exports,
|
|
9
|
+
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')) :
|
|
10
|
+
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) :
|
|
11
|
+
(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));
|
|
12
|
+
}(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';
|
|
13
13
|
|
|
14
14
|
/*! *****************************************************************************
|
|
15
15
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
@@ -38,1650 +38,1660 @@ function __extends(d, b) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
41
|
+
* This class manages keyboard events for selectable lists. If you pass it a query list
|
|
42
|
+
* of items, it will set the active item correctly when arrow events occur.
|
|
43
43
|
*/
|
|
44
|
-
var
|
|
44
|
+
var ListKeyManager = (function () {
|
|
45
45
|
/**
|
|
46
|
-
* @param {?}
|
|
46
|
+
* @param {?} _items
|
|
47
47
|
*/
|
|
48
|
-
function
|
|
49
|
-
this.
|
|
48
|
+
function ListKeyManager(_items) {
|
|
49
|
+
this._items = _items;
|
|
50
|
+
this._activeItemIndex = -1;
|
|
51
|
+
this._wrap = false;
|
|
52
|
+
this._letterKeyStream = new rxjs_Subject.Subject();
|
|
53
|
+
this._typeaheadSubscription = rxjs_Subscription.Subscription.EMPTY;
|
|
54
|
+
this._pressedLetters = [];
|
|
55
|
+
/**
|
|
56
|
+
* Stream that emits any time the TAB key is pressed, so components can react
|
|
57
|
+
* when focus is shifted off of the list.
|
|
58
|
+
*/
|
|
59
|
+
this.tabOut = new rxjs_Subject.Subject();
|
|
50
60
|
}
|
|
51
61
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* @
|
|
55
|
-
* @return {?} Whether the element is disabled.
|
|
56
|
-
*/
|
|
57
|
-
InteractivityChecker.prototype.isDisabled = function (element) {
|
|
58
|
-
// This does not capture some cases, such as a non-form control with a disabled attribute or
|
|
59
|
-
// a form control inside of a disabled form, but should capture the most common cases.
|
|
60
|
-
return element.hasAttribute('disabled');
|
|
61
|
-
};
|
|
62
|
-
/**
|
|
63
|
-
* Gets whether an element is visible for the purposes of interactivity.
|
|
64
|
-
*
|
|
65
|
-
* This will capture states like `display: none` and `visibility: hidden`, but not things like
|
|
66
|
-
* being clipped by an `overflow: hidden` parent or being outside the viewport.
|
|
67
|
-
*
|
|
68
|
-
* @param {?} element
|
|
69
|
-
* @return {?} Whether the element is visible.
|
|
62
|
+
* Turns on wrapping mode, which ensures that the active item will wrap to
|
|
63
|
+
* the other end of list when there are no more items in the given direction.
|
|
64
|
+
* @return {?}
|
|
70
65
|
*/
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
ListKeyManager.prototype.withWrap = function () {
|
|
67
|
+
this._wrap = true;
|
|
68
|
+
return this;
|
|
73
69
|
};
|
|
74
70
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* @param {?} element Element to be checked.
|
|
79
|
-
* @return {?} Whether the element is tabbable.
|
|
71
|
+
* Turns on typeahead mode which allows users to set the active item by typing.
|
|
72
|
+
* @param {?=} debounceInterval Time to wait after the last keystroke before setting the active item.
|
|
73
|
+
* @return {?}
|
|
80
74
|
*/
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
var /** @type {?} */ frameElement = (getWindow(element).frameElement);
|
|
87
|
-
if (frameElement) {
|
|
88
|
-
var /** @type {?} */ frameType = frameElement && frameElement.nodeName.toLowerCase();
|
|
89
|
-
// Frame elements inherit their tabindex onto all child elements.
|
|
90
|
-
if (getTabIndexValue(frameElement) === -1) {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
// Webkit and Blink consider anything inside of an <object> element as non-tabbable.
|
|
94
|
-
if ((this._platform.BLINK || this._platform.WEBKIT) && frameType === 'object') {
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
// Webkit and Blink disable tabbing to an element inside of an invisible frame.
|
|
98
|
-
if ((this._platform.BLINK || this._platform.WEBKIT) && !this.isVisible(frameElement)) {
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
var /** @type {?} */ nodeName = element.nodeName.toLowerCase();
|
|
103
|
-
var /** @type {?} */ tabIndexValue = getTabIndexValue(element);
|
|
104
|
-
if (element.hasAttribute('contenteditable')) {
|
|
105
|
-
return tabIndexValue !== -1;
|
|
106
|
-
}
|
|
107
|
-
if (nodeName === 'iframe') {
|
|
108
|
-
// The frames may be tabbable depending on content, but it's not possibly to reliably
|
|
109
|
-
// investigate the content of the frames.
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
if (nodeName === 'audio') {
|
|
113
|
-
if (!element.hasAttribute('controls')) {
|
|
114
|
-
// By default an <audio> element without the controls enabled is not tabbable.
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
else if (this._platform.BLINK) {
|
|
118
|
-
// In Blink <audio controls> elements are always tabbable.
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
75
|
+
ListKeyManager.prototype.withTypeAhead = function (debounceInterval) {
|
|
76
|
+
var _this = this;
|
|
77
|
+
if (debounceInterval === void 0) { debounceInterval = 200; }
|
|
78
|
+
if (this._items.length && this._items.some(function (item) { return typeof item.getLabel !== 'function'; })) {
|
|
79
|
+
throw Error('ListKeyManager items in typeahead mode must implement the `getLabel` method.');
|
|
121
80
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
81
|
+
this._typeaheadSubscription.unsubscribe();
|
|
82
|
+
// Debounce the presses of non-navigational keys, collect the ones that correspond to letters
|
|
83
|
+
// and convert those letters back into a string. Afterwards find the first item that starts
|
|
84
|
+
// with that string and select it.
|
|
85
|
+
this._typeaheadSubscription = _angular_cdk_rxjs.RxChain.from(this._letterKeyStream)
|
|
86
|
+
.call(_angular_cdk_rxjs.doOperator, function (keyCode) { return _this._pressedLetters.push(keyCode); })
|
|
87
|
+
.call(_angular_cdk_rxjs.debounceTime, debounceInterval)
|
|
88
|
+
.call(_angular_cdk_rxjs.filter, function () { return _this._pressedLetters.length > 0; })
|
|
89
|
+
.call(_angular_cdk_rxjs.map, function () { return _this._pressedLetters.join(''); })
|
|
90
|
+
.subscribe(function (inputString) {
|
|
91
|
+
var /** @type {?} */ items = _this._items.toArray();
|
|
92
|
+
// Start at 1 because we want to start searching at the item immediately
|
|
93
|
+
// following the current active item.
|
|
94
|
+
for (var /** @type {?} */ i = 1; i < items.length + 1; i++) {
|
|
95
|
+
var /** @type {?} */ index = (_this._activeItemIndex + i) % items.length;
|
|
96
|
+
var /** @type {?} */ item = items[index];
|
|
97
|
+
if (!item.disabled && ((item.getLabel))().toUpperCase().trim().indexOf(inputString) === 0) {
|
|
98
|
+
_this.setActiveItem(index);
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
130
101
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
// In iOS the browser only considers some specific elements as tabbable.
|
|
137
|
-
if (this._platform.WEBKIT && this._platform.IOS && !isPotentiallyTabbableIOS(element)) {
|
|
138
|
-
return false;
|
|
139
|
-
}
|
|
140
|
-
return element.tabIndex >= 0;
|
|
102
|
+
_this._pressedLetters = [];
|
|
103
|
+
});
|
|
104
|
+
return this;
|
|
141
105
|
};
|
|
142
106
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* @
|
|
146
|
-
* @return {?} Whether the element is focusable.
|
|
107
|
+
* Sets the active item to the item at the index specified.
|
|
108
|
+
* @param {?} index The index of the item to be set as active.
|
|
109
|
+
* @return {?}
|
|
147
110
|
*/
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return isPotentiallyFocusable(element) && !this.isDisabled(element) && this.isVisible(element);
|
|
111
|
+
ListKeyManager.prototype.setActiveItem = function (index) {
|
|
112
|
+
this._activeItemIndex = index;
|
|
113
|
+
this._activeItem = this._items.toArray()[index];
|
|
152
114
|
};
|
|
153
|
-
return InteractivityChecker;
|
|
154
|
-
}());
|
|
155
|
-
InteractivityChecker.decorators = [
|
|
156
|
-
{ type: _angular_core.Injectable },
|
|
157
|
-
];
|
|
158
|
-
/**
|
|
159
|
-
* @nocollapse
|
|
160
|
-
*/
|
|
161
|
-
InteractivityChecker.ctorParameters = function () { return [
|
|
162
|
-
{ type: _angular_cdk_platform.Platform, },
|
|
163
|
-
]; };
|
|
164
|
-
/**
|
|
165
|
-
* Checks whether the specified element has any geometry / rectangles.
|
|
166
|
-
* @param {?} element
|
|
167
|
-
* @return {?}
|
|
168
|
-
*/
|
|
169
|
-
function hasGeometry(element) {
|
|
170
|
-
// Use logic from jQuery to check for an invisible element.
|
|
171
|
-
// See https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js#L12
|
|
172
|
-
return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Gets whether an element's
|
|
176
|
-
* @param {?} element
|
|
177
|
-
* @return {?}
|
|
178
|
-
*/
|
|
179
|
-
function isNativeFormElement(element) {
|
|
180
|
-
var /** @type {?} */ nodeName = element.nodeName.toLowerCase();
|
|
181
|
-
return nodeName === 'input' ||
|
|
182
|
-
nodeName === 'select' ||
|
|
183
|
-
nodeName === 'button' ||
|
|
184
|
-
nodeName === 'textarea';
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Gets whether an element is an <input type="hidden">.
|
|
188
|
-
* @param {?} element
|
|
189
|
-
* @return {?}
|
|
190
|
-
*/
|
|
191
|
-
function isHiddenInput(element) {
|
|
192
|
-
return isInputElement(element) && element.type == 'hidden';
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* Gets whether an element is an anchor that has an href attribute.
|
|
196
|
-
* @param {?} element
|
|
197
|
-
* @return {?}
|
|
198
|
-
*/
|
|
199
|
-
function isAnchorWithHref(element) {
|
|
200
|
-
return isAnchorElement(element) && element.hasAttribute('href');
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Gets whether an element is an input element.
|
|
204
|
-
* @param {?} element
|
|
205
|
-
* @return {?}
|
|
206
|
-
*/
|
|
207
|
-
function isInputElement(element) {
|
|
208
|
-
return element.nodeName.toLowerCase() == 'input';
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Gets whether an element is an anchor element.
|
|
212
|
-
* @param {?} element
|
|
213
|
-
* @return {?}
|
|
214
|
-
*/
|
|
215
|
-
function isAnchorElement(element) {
|
|
216
|
-
return element.nodeName.toLowerCase() == 'a';
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Gets whether an element has a valid tabindex.
|
|
220
|
-
* @param {?} element
|
|
221
|
-
* @return {?}
|
|
222
|
-
*/
|
|
223
|
-
function hasValidTabIndex(element) {
|
|
224
|
-
if (!element.hasAttribute('tabindex') || element.tabIndex === undefined) {
|
|
225
|
-
return false;
|
|
226
|
-
}
|
|
227
|
-
var /** @type {?} */ tabIndex = element.getAttribute('tabindex');
|
|
228
|
-
// IE11 parses tabindex="" as the value "-32768"
|
|
229
|
-
if (tabIndex == '-32768') {
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
return !!(tabIndex && !isNaN(parseInt(tabIndex, 10)));
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Returns the parsed tabindex from the element attributes instead of returning the
|
|
236
|
-
* evaluated tabindex from the browsers defaults.
|
|
237
|
-
* @param {?} element
|
|
238
|
-
* @return {?}
|
|
239
|
-
*/
|
|
240
|
-
function getTabIndexValue(element) {
|
|
241
|
-
if (!hasValidTabIndex(element)) {
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
// See browser issue in Gecko https://bugzilla.mozilla.org/show_bug.cgi?id=1128054
|
|
245
|
-
var /** @type {?} */ tabIndex = parseInt(element.getAttribute('tabindex') || '', 10);
|
|
246
|
-
return isNaN(tabIndex) ? -1 : tabIndex;
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Checks whether the specified element is potentially tabbable on iOS
|
|
250
|
-
* @param {?} element
|
|
251
|
-
* @return {?}
|
|
252
|
-
*/
|
|
253
|
-
function isPotentiallyTabbableIOS(element) {
|
|
254
|
-
var /** @type {?} */ nodeName = element.nodeName.toLowerCase();
|
|
255
|
-
var /** @type {?} */ inputType = nodeName === 'input' && ((element)).type;
|
|
256
|
-
return inputType === 'text'
|
|
257
|
-
|| inputType === 'password'
|
|
258
|
-
|| nodeName === 'select'
|
|
259
|
-
|| nodeName === 'textarea';
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Gets whether an element is potentially focusable without taking current visible/disabled state
|
|
263
|
-
* into account.
|
|
264
|
-
* @param {?} element
|
|
265
|
-
* @return {?}
|
|
266
|
-
*/
|
|
267
|
-
function isPotentiallyFocusable(element) {
|
|
268
|
-
// Inputs are potentially focusable *unless* they're type="hidden".
|
|
269
|
-
if (isHiddenInput(element)) {
|
|
270
|
-
return false;
|
|
271
|
-
}
|
|
272
|
-
return isNativeFormElement(element) ||
|
|
273
|
-
isAnchorWithHref(element) ||
|
|
274
|
-
element.hasAttribute('contenteditable') ||
|
|
275
|
-
hasValidTabIndex(element);
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Gets the parent window of a DOM node with regards of being inside of an iframe.
|
|
279
|
-
* @param {?} node
|
|
280
|
-
* @return {?}
|
|
281
|
-
*/
|
|
282
|
-
function getWindow(node) {
|
|
283
|
-
return node.ownerDocument.defaultView || window;
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Class that allows for trapping focus within a DOM element.
|
|
287
|
-
*
|
|
288
|
-
* NOTE: This class currently uses a very simple (naive) approach to focus trapping.
|
|
289
|
-
* It assumes that the tab order is the same as DOM order, which is not necessarily true.
|
|
290
|
-
* Things like tabIndex > 0, flex `order`, and shadow roots can cause to two to misalign.
|
|
291
|
-
* This will be replaced with a more intelligent solution before the library is considered stable.
|
|
292
|
-
*/
|
|
293
|
-
var FocusTrap = (function () {
|
|
294
115
|
/**
|
|
295
|
-
*
|
|
296
|
-
* @param {?}
|
|
297
|
-
* @
|
|
298
|
-
* @param {?} _ngZone
|
|
299
|
-
* @param {?=} deferAnchors
|
|
116
|
+
* Sets the active item depending on the key event passed in.
|
|
117
|
+
* @param {?} event Keyboard event to be used for determining which element should be active.
|
|
118
|
+
* @return {?}
|
|
300
119
|
*/
|
|
301
|
-
function
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
120
|
+
ListKeyManager.prototype.onKeydown = function (event) {
|
|
121
|
+
switch (event.keyCode) {
|
|
122
|
+
case _angular_cdk_keycodes.DOWN_ARROW:
|
|
123
|
+
this.setNextItemActive();
|
|
124
|
+
break;
|
|
125
|
+
case _angular_cdk_keycodes.UP_ARROW:
|
|
126
|
+
this.setPreviousItemActive();
|
|
127
|
+
break;
|
|
128
|
+
case _angular_cdk_keycodes.TAB:
|
|
129
|
+
this.tabOut.next();
|
|
130
|
+
return;
|
|
131
|
+
default:
|
|
132
|
+
var /** @type {?} */ keyCode = event.keyCode;
|
|
133
|
+
// Attempt to use the `event.key` which also maps it to the user's keyboard language,
|
|
134
|
+
// otherwise fall back to resolving alphanumeric characters via the keyCode.
|
|
135
|
+
if (event.key && event.key.length === 1) {
|
|
136
|
+
this._letterKeyStream.next(event.key.toLocaleUpperCase());
|
|
137
|
+
}
|
|
138
|
+
else if ((keyCode >= _angular_cdk_keycodes.A && keyCode <= _angular_cdk_keycodes.Z) || (keyCode >= _angular_cdk_keycodes.ZERO && keyCode <= _angular_cdk_keycodes.NINE)) {
|
|
139
|
+
this._letterKeyStream.next(String.fromCharCode(keyCode));
|
|
140
|
+
}
|
|
141
|
+
// Note that we return here, in order to avoid preventing
|
|
142
|
+
// the default action of non-navigational keys.
|
|
143
|
+
return;
|
|
310
144
|
}
|
|
311
|
-
|
|
312
|
-
|
|
145
|
+
this._pressedLetters = [];
|
|
146
|
+
event.preventDefault();
|
|
147
|
+
};
|
|
148
|
+
Object.defineProperty(ListKeyManager.prototype, "activeItemIndex", {
|
|
313
149
|
/**
|
|
314
|
-
*
|
|
150
|
+
* Index of the currently active item.
|
|
315
151
|
* @return {?}
|
|
316
152
|
*/
|
|
317
|
-
get: function () {
|
|
153
|
+
get: function () {
|
|
154
|
+
return this._activeItemIndex;
|
|
155
|
+
},
|
|
156
|
+
enumerable: true,
|
|
157
|
+
configurable: true
|
|
158
|
+
});
|
|
159
|
+
Object.defineProperty(ListKeyManager.prototype, "activeItem", {
|
|
318
160
|
/**
|
|
319
|
-
*
|
|
161
|
+
* The active item.
|
|
320
162
|
* @return {?}
|
|
321
163
|
*/
|
|
322
|
-
|
|
323
|
-
this.
|
|
324
|
-
if (this._startAnchor && this._endAnchor) {
|
|
325
|
-
this._startAnchor.tabIndex = this._endAnchor.tabIndex = this._enabled ? 0 : -1;
|
|
326
|
-
}
|
|
164
|
+
get: function () {
|
|
165
|
+
return this._activeItem;
|
|
327
166
|
},
|
|
328
167
|
enumerable: true,
|
|
329
168
|
configurable: true
|
|
330
169
|
});
|
|
331
170
|
/**
|
|
332
|
-
*
|
|
171
|
+
* Sets the active item to the first enabled item in the list.
|
|
333
172
|
* @return {?}
|
|
334
173
|
*/
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
this._startAnchor.parentNode.removeChild(this._startAnchor);
|
|
338
|
-
}
|
|
339
|
-
if (this._endAnchor && this._endAnchor.parentNode) {
|
|
340
|
-
this._endAnchor.parentNode.removeChild(this._endAnchor);
|
|
341
|
-
}
|
|
342
|
-
this._startAnchor = this._endAnchor = null;
|
|
174
|
+
ListKeyManager.prototype.setFirstItemActive = function () {
|
|
175
|
+
this._setActiveItemByIndex(0, 1);
|
|
343
176
|
};
|
|
344
177
|
/**
|
|
345
|
-
*
|
|
346
|
-
* in the constructor, but can be deferred for cases like directives with `*ngIf`.
|
|
178
|
+
* Sets the active item to the last enabled item in the list.
|
|
347
179
|
* @return {?}
|
|
348
180
|
*/
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
// If we're not on the browser, there can be no focus to trap.
|
|
352
|
-
if (!this._platform.isBrowser) {
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
if (!this._startAnchor) {
|
|
356
|
-
this._startAnchor = this._createAnchor();
|
|
357
|
-
}
|
|
358
|
-
if (!this._endAnchor) {
|
|
359
|
-
this._endAnchor = this._createAnchor();
|
|
360
|
-
}
|
|
361
|
-
this._ngZone.runOutsideAngular(function () {
|
|
362
|
-
((_this._startAnchor)).addEventListener('focus', function () {
|
|
363
|
-
_this.focusLastTabbableElement();
|
|
364
|
-
}); /** @type {?} */
|
|
365
|
-
((_this._endAnchor)).addEventListener('focus', function () {
|
|
366
|
-
_this.focusFirstTabbableElement();
|
|
367
|
-
});
|
|
368
|
-
if (_this._element.parentNode) {
|
|
369
|
-
_this._element.parentNode.insertBefore(/** @type {?} */ ((_this._startAnchor)), _this._element);
|
|
370
|
-
_this._element.parentNode.insertBefore(/** @type {?} */ ((_this._endAnchor)), _this._element.nextSibling);
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
};
|
|
374
|
-
/**
|
|
375
|
-
* Waits for the zone to stabilize, then either focuses the first element that the
|
|
376
|
-
* user specified, or the first tabbable element.
|
|
377
|
-
* @return {?} Returns a promise that resolves with a boolean, depending
|
|
378
|
-
* on whether focus was moved successfuly.
|
|
379
|
-
*/
|
|
380
|
-
FocusTrap.prototype.focusInitialElementWhenReady = function () {
|
|
381
|
-
var _this = this;
|
|
382
|
-
return new Promise(function (resolve) {
|
|
383
|
-
_this._executeOnStable(function () { return resolve(_this.focusInitialElement()); });
|
|
384
|
-
});
|
|
385
|
-
};
|
|
386
|
-
/**
|
|
387
|
-
* Waits for the zone to stabilize, then focuses
|
|
388
|
-
* the first tabbable element within the focus trap region.
|
|
389
|
-
* @return {?} Returns a promise that resolves with a boolean, depending
|
|
390
|
-
* on whether focus was moved successfuly.
|
|
391
|
-
*/
|
|
392
|
-
FocusTrap.prototype.focusFirstTabbableElementWhenReady = function () {
|
|
393
|
-
var _this = this;
|
|
394
|
-
return new Promise(function (resolve) {
|
|
395
|
-
_this._executeOnStable(function () { return resolve(_this.focusFirstTabbableElement()); });
|
|
396
|
-
});
|
|
397
|
-
};
|
|
398
|
-
/**
|
|
399
|
-
* Waits for the zone to stabilize, then focuses
|
|
400
|
-
* the last tabbable element within the focus trap region.
|
|
401
|
-
* @return {?} Returns a promise that resolves with a boolean, depending
|
|
402
|
-
* on whether focus was moved successfuly.
|
|
403
|
-
*/
|
|
404
|
-
FocusTrap.prototype.focusLastTabbableElementWhenReady = function () {
|
|
405
|
-
var _this = this;
|
|
406
|
-
return new Promise(function (resolve) {
|
|
407
|
-
_this._executeOnStable(function () { return resolve(_this.focusLastTabbableElement()); });
|
|
408
|
-
});
|
|
409
|
-
};
|
|
410
|
-
/**
|
|
411
|
-
* Get the specified boundary element of the trapped region.
|
|
412
|
-
* @param {?} bound The boundary to get (start or end of trapped region).
|
|
413
|
-
* @return {?} The boundary element.
|
|
414
|
-
*/
|
|
415
|
-
FocusTrap.prototype._getRegionBoundary = function (bound) {
|
|
416
|
-
// Contains the deprecated version of selector, for temporary backwards comparability.
|
|
417
|
-
var /** @type {?} */ markers = (this._element.querySelectorAll("[cdk-focus-region-" + bound + "], " +
|
|
418
|
-
("[cdk-focus-" + bound + "]")));
|
|
419
|
-
for (var /** @type {?} */ i = 0; i < markers.length; i++) {
|
|
420
|
-
if (markers[i].hasAttribute("cdk-focus-" + bound)) {
|
|
421
|
-
console.warn("Found use of deprecated attribute 'cdk-focus-" + bound + "'," +
|
|
422
|
-
(" use 'cdk-focus-region-" + bound + "' instead."), markers[i]);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
if (bound == 'start') {
|
|
426
|
-
return markers.length ? markers[0] : this._getFirstTabbableElement(this._element);
|
|
427
|
-
}
|
|
428
|
-
return markers.length ?
|
|
429
|
-
markers[markers.length - 1] : this._getLastTabbableElement(this._element);
|
|
181
|
+
ListKeyManager.prototype.setLastItemActive = function () {
|
|
182
|
+
this._setActiveItemByIndex(this._items.length - 1, -1);
|
|
430
183
|
};
|
|
431
184
|
/**
|
|
432
|
-
*
|
|
433
|
-
* @return {?}
|
|
185
|
+
* Sets the active item to the next enabled item in the list.
|
|
186
|
+
* @return {?}
|
|
434
187
|
*/
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
if (redirectToElement) {
|
|
438
|
-
redirectToElement.focus();
|
|
439
|
-
return true;
|
|
440
|
-
}
|
|
441
|
-
return this.focusFirstTabbableElement();
|
|
188
|
+
ListKeyManager.prototype.setNextItemActive = function () {
|
|
189
|
+
this._activeItemIndex < 0 ? this.setFirstItemActive() : this._setActiveItemByDelta(1);
|
|
442
190
|
};
|
|
443
|
-
/**
|
|
444
|
-
*
|
|
445
|
-
* @return {?}
|
|
191
|
+
/**
|
|
192
|
+
* Sets the active item to a previous enabled item in the list.
|
|
193
|
+
* @return {?}
|
|
446
194
|
*/
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
redirectToElement.focus();
|
|
451
|
-
}
|
|
452
|
-
return !!redirectToElement;
|
|
195
|
+
ListKeyManager.prototype.setPreviousItemActive = function () {
|
|
196
|
+
this._activeItemIndex < 0 && this._wrap ? this.setLastItemActive()
|
|
197
|
+
: this._setActiveItemByDelta(-1);
|
|
453
198
|
};
|
|
454
199
|
/**
|
|
455
|
-
*
|
|
456
|
-
* @
|
|
200
|
+
* Allows setting of the activeItemIndex without any other effects.
|
|
201
|
+
* @param {?} index The new activeItemIndex.
|
|
202
|
+
* @return {?}
|
|
457
203
|
*/
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
if (redirectToElement) {
|
|
461
|
-
redirectToElement.focus();
|
|
462
|
-
}
|
|
463
|
-
return !!redirectToElement;
|
|
204
|
+
ListKeyManager.prototype.updateActiveItemIndex = function (index) {
|
|
205
|
+
this._activeItemIndex = index;
|
|
464
206
|
};
|
|
465
207
|
/**
|
|
466
|
-
*
|
|
467
|
-
*
|
|
208
|
+
* This method sets the active item, given a list of items and the delta between the
|
|
209
|
+
* currently active item and the new active item. It will calculate differently
|
|
210
|
+
* depending on whether wrap mode is turned on.
|
|
211
|
+
* @param {?} delta
|
|
212
|
+
* @param {?=} items
|
|
468
213
|
* @return {?}
|
|
469
214
|
*/
|
|
470
|
-
|
|
471
|
-
if (
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
// Iterate in DOM order. Note that IE doesn't have `children` for SVG so we fall
|
|
475
|
-
// back to `childNodes` which includes text nodes, comments etc.
|
|
476
|
-
var /** @type {?} */ children = root.children || root.childNodes;
|
|
477
|
-
for (var /** @type {?} */ i = 0; i < children.length; i++) {
|
|
478
|
-
var /** @type {?} */ tabbableChild = children[i].nodeType === Node.ELEMENT_NODE ?
|
|
479
|
-
this._getFirstTabbableElement(/** @type {?} */ (children[i])) :
|
|
480
|
-
null;
|
|
481
|
-
if (tabbableChild) {
|
|
482
|
-
return tabbableChild;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
return null;
|
|
215
|
+
ListKeyManager.prototype._setActiveItemByDelta = function (delta, items) {
|
|
216
|
+
if (items === void 0) { items = this._items.toArray(); }
|
|
217
|
+
this._wrap ? this._setActiveInWrapMode(delta, items)
|
|
218
|
+
: this._setActiveInDefaultMode(delta, items);
|
|
486
219
|
};
|
|
487
220
|
/**
|
|
488
|
-
*
|
|
489
|
-
*
|
|
221
|
+
* Sets the active item properly given "wrap" mode. In other words, it will continue to move
|
|
222
|
+
* down the list until it finds an item that is not disabled, and it will wrap if it
|
|
223
|
+
* encounters either end of the list.
|
|
224
|
+
* @param {?} delta
|
|
225
|
+
* @param {?} items
|
|
490
226
|
* @return {?}
|
|
491
227
|
*/
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
228
|
+
ListKeyManager.prototype._setActiveInWrapMode = function (delta, items) {
|
|
229
|
+
// when active item would leave menu, wrap to beginning or end
|
|
230
|
+
this._activeItemIndex =
|
|
231
|
+
(this._activeItemIndex + delta + items.length) % items.length;
|
|
232
|
+
// skip all disabled menu items recursively until an enabled one is reached
|
|
233
|
+
if (items[this._activeItemIndex].disabled) {
|
|
234
|
+
this._setActiveInWrapMode(delta, items);
|
|
495
235
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
for (var /** @type {?} */ i = children.length - 1; i >= 0; i--) {
|
|
499
|
-
var /** @type {?} */ tabbableChild = children[i].nodeType === Node.ELEMENT_NODE ?
|
|
500
|
-
this._getLastTabbableElement(/** @type {?} */ (children[i])) :
|
|
501
|
-
null;
|
|
502
|
-
if (tabbableChild) {
|
|
503
|
-
return tabbableChild;
|
|
504
|
-
}
|
|
236
|
+
else {
|
|
237
|
+
this.setActiveItem(this._activeItemIndex);
|
|
505
238
|
}
|
|
506
|
-
return null;
|
|
507
239
|
};
|
|
508
240
|
/**
|
|
509
|
-
*
|
|
241
|
+
* Sets the active item properly given the default mode. In other words, it will
|
|
242
|
+
* continue to move down the list until it finds an item that is not disabled. If
|
|
243
|
+
* it encounters either end of the list, it will stop and not wrap.
|
|
244
|
+
* @param {?} delta
|
|
245
|
+
* @param {?} items
|
|
510
246
|
* @return {?}
|
|
511
247
|
*/
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
anchor.tabIndex = this._enabled ? 0 : -1;
|
|
515
|
-
anchor.classList.add('cdk-visually-hidden');
|
|
516
|
-
anchor.classList.add('cdk-focus-trap-anchor');
|
|
517
|
-
return anchor;
|
|
248
|
+
ListKeyManager.prototype._setActiveInDefaultMode = function (delta, items) {
|
|
249
|
+
this._setActiveItemByIndex(this._activeItemIndex + delta, delta, items);
|
|
518
250
|
};
|
|
519
251
|
/**
|
|
520
|
-
*
|
|
521
|
-
*
|
|
252
|
+
* Sets the active item to the first enabled item starting at the index specified. If the
|
|
253
|
+
* item is disabled, it will move in the fallbackDelta direction until it either
|
|
254
|
+
* finds an enabled item or encounters the end of the list.
|
|
255
|
+
* @param {?} index
|
|
256
|
+
* @param {?} fallbackDelta
|
|
257
|
+
* @param {?=} items
|
|
522
258
|
* @return {?}
|
|
523
259
|
*/
|
|
524
|
-
|
|
525
|
-
if (this.
|
|
526
|
-
|
|
260
|
+
ListKeyManager.prototype._setActiveItemByIndex = function (index, fallbackDelta, items) {
|
|
261
|
+
if (items === void 0) { items = this._items.toArray(); }
|
|
262
|
+
if (!items[index]) {
|
|
263
|
+
return;
|
|
527
264
|
}
|
|
528
|
-
|
|
529
|
-
|
|
265
|
+
while (items[index].disabled) {
|
|
266
|
+
index += fallbackDelta;
|
|
267
|
+
if (!items[index]) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
530
270
|
}
|
|
271
|
+
this.setActiveItem(index);
|
|
531
272
|
};
|
|
532
|
-
return
|
|
273
|
+
return ListKeyManager;
|
|
533
274
|
}());
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
* @param {?} _checker
|
|
540
|
-
* @param {?} _platform
|
|
541
|
-
* @param {?} _ngZone
|
|
542
|
-
*/
|
|
543
|
-
function FocusTrapFactory(_checker, _platform, _ngZone) {
|
|
544
|
-
this._checker = _checker;
|
|
545
|
-
this._platform = _platform;
|
|
546
|
-
this._ngZone = _ngZone;
|
|
275
|
+
|
|
276
|
+
var ActiveDescendantKeyManager = (function (_super) {
|
|
277
|
+
__extends(ActiveDescendantKeyManager, _super);
|
|
278
|
+
function ActiveDescendantKeyManager() {
|
|
279
|
+
return _super !== null && _super.apply(this, arguments) || this;
|
|
547
280
|
}
|
|
548
281
|
/**
|
|
549
|
-
*
|
|
550
|
-
*
|
|
282
|
+
* This method sets the active item to the item at the specified index.
|
|
283
|
+
* It also adds active styles to the newly active item and removes active
|
|
284
|
+
* styles from the previously active item.
|
|
285
|
+
* @param {?} index
|
|
551
286
|
* @return {?}
|
|
552
287
|
*/
|
|
553
|
-
|
|
554
|
-
if (
|
|
555
|
-
|
|
288
|
+
ActiveDescendantKeyManager.prototype.setActiveItem = function (index) {
|
|
289
|
+
if (this.activeItem) {
|
|
290
|
+
this.activeItem.setInactiveStyles();
|
|
291
|
+
}
|
|
292
|
+
_super.prototype.setActiveItem.call(this, index);
|
|
293
|
+
if (this.activeItem) {
|
|
294
|
+
this.activeItem.setActiveStyles();
|
|
295
|
+
}
|
|
556
296
|
};
|
|
557
|
-
return
|
|
558
|
-
}());
|
|
559
|
-
|
|
560
|
-
{ type: _angular_core.Injectable },
|
|
561
|
-
];
|
|
297
|
+
return ActiveDescendantKeyManager;
|
|
298
|
+
}(ListKeyManager));
|
|
299
|
+
|
|
562
300
|
/**
|
|
563
|
-
*
|
|
301
|
+
* IDs are deliminated by an empty space, as per the spec.
|
|
564
302
|
*/
|
|
565
|
-
|
|
566
|
-
{ type: InteractivityChecker, },
|
|
567
|
-
{ type: _angular_cdk_platform.Platform, },
|
|
568
|
-
{ type: _angular_core.NgZone, },
|
|
569
|
-
]; };
|
|
303
|
+
var ID_DELIMINATOR = ' ';
|
|
570
304
|
/**
|
|
571
|
-
*
|
|
572
|
-
*
|
|
305
|
+
* Adds the given ID to the specified ARIA attribute on an element.
|
|
306
|
+
* Used for attributes such as aria-labelledby, aria-owns, etc.
|
|
307
|
+
* @param {?} el
|
|
308
|
+
* @param {?} attr
|
|
309
|
+
* @param {?} id
|
|
310
|
+
* @return {?}
|
|
573
311
|
*/
|
|
574
|
-
|
|
575
|
-
/**
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
*/
|
|
579
|
-
function FocusTrapDeprecatedDirective(_elementRef, _focusTrapFactory) {
|
|
580
|
-
this._elementRef = _elementRef;
|
|
581
|
-
this._focusTrapFactory = _focusTrapFactory;
|
|
582
|
-
this.focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement, true);
|
|
312
|
+
function addAriaReferencedId(el, attr, id) {
|
|
313
|
+
var /** @type {?} */ ids = getAriaReferenceIds(el, attr);
|
|
314
|
+
if (ids.some(function (existingId) { return existingId.trim() == id.trim(); })) {
|
|
315
|
+
return;
|
|
583
316
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
317
|
+
ids.push(id.trim());
|
|
318
|
+
el.setAttribute(attr, ids.join(ID_DELIMINATOR));
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Removes the given ID from the specified ARIA attribute on an element.
|
|
322
|
+
* Used for attributes such as aria-labelledby, aria-owns, etc.
|
|
323
|
+
* @param {?} el
|
|
324
|
+
* @param {?} attr
|
|
325
|
+
* @param {?} id
|
|
326
|
+
* @return {?}
|
|
327
|
+
*/
|
|
328
|
+
function removeAriaReferencedId(el, attr, id) {
|
|
329
|
+
var /** @type {?} */ ids = getAriaReferenceIds(el, attr);
|
|
330
|
+
var /** @type {?} */ filteredIds = ids.filter(function (val) { return val != id.trim(); });
|
|
331
|
+
el.setAttribute(attr, filteredIds.join(ID_DELIMINATOR));
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Gets the list of IDs referenced by the given ARIA attribute on an element.
|
|
335
|
+
* Used for attributes such as aria-labelledby, aria-owns, etc.
|
|
336
|
+
* @param {?} el
|
|
337
|
+
* @param {?} attr
|
|
338
|
+
* @return {?}
|
|
339
|
+
*/
|
|
340
|
+
function getAriaReferenceIds(el, attr) {
|
|
341
|
+
// Get string array of all individual ids (whitespace deliminated) in the attribute value
|
|
342
|
+
return (el.getAttribute(attr) || '').match(/\S+/g) || [];
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* ID used for the body container where all messages are appended.
|
|
347
|
+
*/
|
|
348
|
+
var MESSAGES_CONTAINER_ID = 'cdk-describedby-message-container';
|
|
349
|
+
/**
|
|
350
|
+
* ID prefix used for each created message element.
|
|
351
|
+
*/
|
|
352
|
+
var CDK_DESCRIBEDBY_ID_PREFIX = 'cdk-describedby-message';
|
|
353
|
+
/**
|
|
354
|
+
* Attribute given to each host element that is described by a message element.
|
|
355
|
+
*/
|
|
356
|
+
var CDK_DESCRIBEDBY_HOST_ATTRIBUTE = 'cdk-describedby-host';
|
|
357
|
+
/**
|
|
358
|
+
* Global incremental identifier for each registered message element.
|
|
359
|
+
*/
|
|
360
|
+
var nextId = 0;
|
|
361
|
+
/**
|
|
362
|
+
* Global map of all registered message elements that have been placed into the document.
|
|
363
|
+
*/
|
|
364
|
+
var messageRegistry = new Map();
|
|
619
365
|
/**
|
|
620
|
-
*
|
|
366
|
+
* Container for all registered messages.
|
|
621
367
|
*/
|
|
622
|
-
|
|
623
|
-
{ type: _angular_core.ElementRef, },
|
|
624
|
-
{ type: FocusTrapFactory, },
|
|
625
|
-
]; };
|
|
626
|
-
FocusTrapDeprecatedDirective.propDecorators = {
|
|
627
|
-
'disabled': [{ type: _angular_core.Input },],
|
|
628
|
-
};
|
|
368
|
+
var messagesContainer = null;
|
|
629
369
|
/**
|
|
630
|
-
*
|
|
370
|
+
* Utility that creates visually hidden elements with a message content. Useful for elements that
|
|
371
|
+
* want to use aria-describedby to further describe themselves without adding additional visual
|
|
372
|
+
* content.
|
|
373
|
+
* \@docs-private
|
|
631
374
|
*/
|
|
632
|
-
var
|
|
375
|
+
var AriaDescriber = (function () {
|
|
633
376
|
/**
|
|
634
|
-
* @param {?}
|
|
635
|
-
* @param {?} _focusTrapFactory
|
|
377
|
+
* @param {?} _platform
|
|
636
378
|
*/
|
|
637
|
-
function
|
|
638
|
-
this.
|
|
639
|
-
this._focusTrapFactory = _focusTrapFactory;
|
|
640
|
-
this.focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement, true);
|
|
379
|
+
function AriaDescriber(_platform) {
|
|
380
|
+
this._platform = _platform;
|
|
641
381
|
}
|
|
642
|
-
Object.defineProperty(FocusTrapDirective.prototype, "enabled", {
|
|
643
|
-
/**
|
|
644
|
-
* Whether the focus trap is active.
|
|
645
|
-
* @return {?}
|
|
646
|
-
*/
|
|
647
|
-
get: function () { return this.focusTrap.enabled; },
|
|
648
|
-
/**
|
|
649
|
-
* @param {?} value
|
|
650
|
-
* @return {?}
|
|
651
|
-
*/
|
|
652
|
-
set: function (value) { this.focusTrap.enabled = _angular_cdk_coercion.coerceBooleanProperty(value); },
|
|
653
|
-
enumerable: true,
|
|
654
|
-
configurable: true
|
|
655
|
-
});
|
|
656
382
|
/**
|
|
383
|
+
* Adds to the host element an aria-describedby reference to a hidden element that contains
|
|
384
|
+
* the message. If the same message has already been registered, then it will reuse the created
|
|
385
|
+
* message element.
|
|
386
|
+
* @param {?} hostElement
|
|
387
|
+
* @param {?} message
|
|
657
388
|
* @return {?}
|
|
658
389
|
*/
|
|
659
|
-
|
|
660
|
-
this.
|
|
390
|
+
AriaDescriber.prototype.describe = function (hostElement, message) {
|
|
391
|
+
if (!this._platform.isBrowser || !message.trim()) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (!messageRegistry.has(message)) {
|
|
395
|
+
createMessageElement(message);
|
|
396
|
+
}
|
|
397
|
+
if (!isElementDescribedByMessage(hostElement, message)) {
|
|
398
|
+
addMessageReference(hostElement, message);
|
|
399
|
+
}
|
|
661
400
|
};
|
|
662
401
|
/**
|
|
402
|
+
* Removes the host element's aria-describedby reference to the message element.
|
|
403
|
+
* @param {?} hostElement
|
|
404
|
+
* @param {?} message
|
|
663
405
|
* @return {?}
|
|
664
406
|
*/
|
|
665
|
-
|
|
666
|
-
this.
|
|
407
|
+
AriaDescriber.prototype.removeDescription = function (hostElement, message) {
|
|
408
|
+
if (!this._platform.isBrowser || !message.trim()) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (isElementDescribedByMessage(hostElement, message)) {
|
|
412
|
+
removeMessageReference(hostElement, message);
|
|
413
|
+
}
|
|
414
|
+
var /** @type {?} */ registeredMessage = messageRegistry.get(message);
|
|
415
|
+
if (registeredMessage && registeredMessage.referenceCount === 0) {
|
|
416
|
+
deleteMessageElement(message);
|
|
417
|
+
}
|
|
418
|
+
if (messagesContainer && messagesContainer.childNodes.length === 0) {
|
|
419
|
+
deleteMessagesContainer();
|
|
420
|
+
}
|
|
667
421
|
};
|
|
668
|
-
|
|
422
|
+
/**
|
|
423
|
+
* Unregisters all created message elements and removes the message container.
|
|
424
|
+
* @return {?}
|
|
425
|
+
*/
|
|
426
|
+
AriaDescriber.prototype.ngOnDestroy = function () {
|
|
427
|
+
if (!this._platform.isBrowser) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
var /** @type {?} */ describedElements = document.querySelectorAll("[" + CDK_DESCRIBEDBY_HOST_ATTRIBUTE + "]");
|
|
431
|
+
for (var /** @type {?} */ i = 0; i < describedElements.length; i++) {
|
|
432
|
+
removeCdkDescribedByReferenceIds(describedElements[i]);
|
|
433
|
+
describedElements[i].removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE);
|
|
434
|
+
}
|
|
435
|
+
if (messagesContainer) {
|
|
436
|
+
deleteMessagesContainer();
|
|
437
|
+
}
|
|
438
|
+
messageRegistry.clear();
|
|
439
|
+
};
|
|
440
|
+
AriaDescriber.decorators = [
|
|
441
|
+
{ type: _angular_core.Injectable },
|
|
442
|
+
];
|
|
443
|
+
/**
|
|
444
|
+
* @nocollapse
|
|
445
|
+
*/
|
|
446
|
+
AriaDescriber.ctorParameters = function () { return [
|
|
447
|
+
{ type: _angular_cdk_platform.Platform, },
|
|
448
|
+
]; };
|
|
449
|
+
return AriaDescriber;
|
|
669
450
|
}());
|
|
670
|
-
FocusTrapDirective.decorators = [
|
|
671
|
-
{ type: _angular_core.Directive, args: [{
|
|
672
|
-
selector: '[cdkTrapFocus]',
|
|
673
|
-
exportAs: 'cdkTrapFocus',
|
|
674
|
-
},] },
|
|
675
|
-
];
|
|
676
451
|
/**
|
|
677
|
-
*
|
|
452
|
+
* Creates a new element in the visually hidden message container element with the message
|
|
453
|
+
* as its content and adds it to the message registry.
|
|
454
|
+
* @param {?} message
|
|
455
|
+
* @return {?}
|
|
456
|
+
*/
|
|
457
|
+
function createMessageElement(message) {
|
|
458
|
+
var /** @type {?} */ messageElement = document.createElement('div');
|
|
459
|
+
messageElement.setAttribute('id', CDK_DESCRIBEDBY_ID_PREFIX + "-" + nextId++);
|
|
460
|
+
messageElement.appendChild(/** @type {?} */ ((document.createTextNode(message))));
|
|
461
|
+
if (!messagesContainer) {
|
|
462
|
+
createMessagesContainer();
|
|
463
|
+
} /** @type {?} */
|
|
464
|
+
((messagesContainer)).appendChild(messageElement);
|
|
465
|
+
messageRegistry.set(message, { messageElement: messageElement, referenceCount: 0 });
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Deletes the message element from the global messages container.
|
|
469
|
+
* @param {?} message
|
|
470
|
+
* @return {?}
|
|
471
|
+
*/
|
|
472
|
+
function deleteMessageElement(message) {
|
|
473
|
+
var /** @type {?} */ registeredMessage = messageRegistry.get(message);
|
|
474
|
+
var /** @type {?} */ messageElement = registeredMessage && registeredMessage.messageElement;
|
|
475
|
+
if (messagesContainer && messageElement) {
|
|
476
|
+
messagesContainer.removeChild(messageElement);
|
|
477
|
+
}
|
|
478
|
+
messageRegistry.delete(message);
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Creates the global container for all aria-describedby messages.
|
|
482
|
+
* @return {?}
|
|
483
|
+
*/
|
|
484
|
+
function createMessagesContainer() {
|
|
485
|
+
messagesContainer = document.createElement('div');
|
|
486
|
+
messagesContainer.setAttribute('id', MESSAGES_CONTAINER_ID);
|
|
487
|
+
messagesContainer.setAttribute('aria-hidden', 'true');
|
|
488
|
+
messagesContainer.style.display = 'none';
|
|
489
|
+
document.body.appendChild(messagesContainer);
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Deletes the global messages container.
|
|
493
|
+
* @return {?}
|
|
494
|
+
*/
|
|
495
|
+
function deleteMessagesContainer() {
|
|
496
|
+
document.body.removeChild(/** @type {?} */ ((messagesContainer)));
|
|
497
|
+
messagesContainer = null;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Removes all cdk-describedby messages that are hosted through the element.
|
|
501
|
+
* @param {?} element
|
|
502
|
+
* @return {?}
|
|
503
|
+
*/
|
|
504
|
+
function removeCdkDescribedByReferenceIds(element) {
|
|
505
|
+
// Remove all aria-describedby reference IDs that are prefixed by CDK_DESCRIBEDBY_ID_PREFIX
|
|
506
|
+
var /** @type {?} */ originalReferenceIds = getAriaReferenceIds(element, 'aria-describedby')
|
|
507
|
+
.filter(function (id) { return id.indexOf(CDK_DESCRIBEDBY_ID_PREFIX) != 0; });
|
|
508
|
+
element.setAttribute('aria-describedby', originalReferenceIds.join(' '));
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Adds a message reference to the element using aria-describedby and increments the registered
|
|
512
|
+
* message's reference count.
|
|
513
|
+
* @param {?} element
|
|
514
|
+
* @param {?} message
|
|
515
|
+
* @return {?}
|
|
516
|
+
*/
|
|
517
|
+
function addMessageReference(element, message) {
|
|
518
|
+
var /** @type {?} */ registeredMessage = ((messageRegistry.get(message)));
|
|
519
|
+
// Add the aria-describedby reference and set the describedby_host attribute to mark the element.
|
|
520
|
+
addAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id);
|
|
521
|
+
element.setAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE, '');
|
|
522
|
+
registeredMessage.referenceCount++;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Removes a message reference from the element using aria-describedby and decrements the registered
|
|
526
|
+
* message's reference count.
|
|
527
|
+
* @param {?} element
|
|
528
|
+
* @param {?} message
|
|
529
|
+
* @return {?}
|
|
530
|
+
*/
|
|
531
|
+
function removeMessageReference(element, message) {
|
|
532
|
+
var /** @type {?} */ registeredMessage = ((messageRegistry.get(message)));
|
|
533
|
+
registeredMessage.referenceCount--;
|
|
534
|
+
removeAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id);
|
|
535
|
+
element.removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Returns true if the element has been described by the provided message ID.
|
|
539
|
+
* @param {?} element
|
|
540
|
+
* @param {?} message
|
|
541
|
+
* @return {?}
|
|
542
|
+
*/
|
|
543
|
+
function isElementDescribedByMessage(element, message) {
|
|
544
|
+
var /** @type {?} */ referenceIds = getAriaReferenceIds(element, 'aria-describedby');
|
|
545
|
+
var /** @type {?} */ registeredMessage = messageRegistry.get(message);
|
|
546
|
+
var /** @type {?} */ messageId = registeredMessage && registeredMessage.messageElement.id;
|
|
547
|
+
return !!messageId && referenceIds.indexOf(messageId) != -1;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* \@docs-private
|
|
551
|
+
* @param {?} parentDispatcher
|
|
552
|
+
* @param {?} platform
|
|
553
|
+
* @return {?}
|
|
554
|
+
*/
|
|
555
|
+
function ARIA_DESCRIBER_PROVIDER_FACTORY(parentDispatcher, platform) {
|
|
556
|
+
return parentDispatcher || new AriaDescriber(platform);
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* \@docs-private
|
|
678
560
|
*/
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
561
|
+
var ARIA_DESCRIBER_PROVIDER = {
|
|
562
|
+
// If there is already an AriaDescriber available, use that. Otherwise, provide a new one.
|
|
563
|
+
provide: AriaDescriber,
|
|
564
|
+
deps: [
|
|
565
|
+
[new _angular_core.Optional(), new _angular_core.SkipSelf(), AriaDescriber],
|
|
566
|
+
_angular_cdk_platform.Platform
|
|
567
|
+
],
|
|
568
|
+
useFactory: ARIA_DESCRIBER_PROVIDER_FACTORY
|
|
685
569
|
};
|
|
686
|
-
|
|
687
|
-
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Screenreaders will often fire fake mousedown events when a focusable element
|
|
573
|
+
* is activated using the keyboard. We can typically distinguish between these faked
|
|
574
|
+
* mousedown events and real mousedown events using the "buttons" property. While
|
|
575
|
+
* real mousedowns will indicate the mouse button that was pressed (e.g. "1" for
|
|
576
|
+
* the left mouse button), faked mousedowns will usually set the property value to 0.
|
|
577
|
+
* @param {?} event
|
|
578
|
+
* @return {?}
|
|
579
|
+
*/
|
|
580
|
+
function isFakeMousedownFromScreenReader(event) {
|
|
581
|
+
return event.buttons === 0;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
var FocusKeyManager = (function (_super) {
|
|
585
|
+
__extends(FocusKeyManager, _super);
|
|
586
|
+
function FocusKeyManager() {
|
|
587
|
+
return _super !== null && _super.apply(this, arguments) || this;
|
|
588
|
+
}
|
|
688
589
|
/**
|
|
689
|
-
*
|
|
690
|
-
*
|
|
590
|
+
* This method sets the active item to the item at the specified index.
|
|
591
|
+
* It also adds focuses the newly active item.
|
|
592
|
+
* @param {?} index
|
|
593
|
+
* @return {?}
|
|
691
594
|
*/
|
|
692
|
-
function
|
|
693
|
-
|
|
694
|
-
if (
|
|
695
|
-
|
|
696
|
-
// browser globals (HTMLElement) on non-browser environments, since having a class decorator
|
|
697
|
-
// causes TypeScript to preserve the constructor signature types.
|
|
698
|
-
this._liveElement = elementToken || this._createLiveElement();
|
|
595
|
+
FocusKeyManager.prototype.setActiveItem = function (index) {
|
|
596
|
+
_super.prototype.setActiveItem.call(this, index);
|
|
597
|
+
if (this.activeItem) {
|
|
598
|
+
this.activeItem.focus();
|
|
699
599
|
}
|
|
600
|
+
};
|
|
601
|
+
return FocusKeyManager;
|
|
602
|
+
}(ListKeyManager));
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Utility for checking the interactivity of an element, such as whether is is focusable or
|
|
606
|
+
* tabbable.
|
|
607
|
+
*/
|
|
608
|
+
var InteractivityChecker = (function () {
|
|
609
|
+
/**
|
|
610
|
+
* @param {?} _platform
|
|
611
|
+
*/
|
|
612
|
+
function InteractivityChecker(_platform) {
|
|
613
|
+
this._platform = _platform;
|
|
700
614
|
}
|
|
701
615
|
/**
|
|
702
|
-
*
|
|
703
|
-
*
|
|
704
|
-
* @param {
|
|
705
|
-
* @return {?}
|
|
616
|
+
* Gets whether an element is disabled.
|
|
617
|
+
*
|
|
618
|
+
* @param {?} element Element to be checked.
|
|
619
|
+
* @return {?} Whether the element is disabled.
|
|
706
620
|
*/
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
// TODO: ensure changing the politeness works on all environments we support.
|
|
712
|
-
this._liveElement.setAttribute('aria-live', politeness);
|
|
713
|
-
// This 100ms timeout is necessary for some browser + screen-reader combinations:
|
|
714
|
-
// - Both JAWS and NVDA over IE11 will not announce anything without a non-zero timeout.
|
|
715
|
-
// - With Chrome and IE11 with NVDA or JAWS, a repeated (identical) message won't be read a
|
|
716
|
-
// second time without clearing and then using a non-zero delay.
|
|
717
|
-
// (using JAWS 17 at time of this writing).
|
|
718
|
-
setTimeout(function () { return _this._liveElement.textContent = message; }, 100);
|
|
621
|
+
InteractivityChecker.prototype.isDisabled = function (element) {
|
|
622
|
+
// This does not capture some cases, such as a non-form control with a disabled attribute or
|
|
623
|
+
// a form control inside of a disabled form, but should capture the most common cases.
|
|
624
|
+
return element.hasAttribute('disabled');
|
|
719
625
|
};
|
|
720
626
|
/**
|
|
721
|
-
*
|
|
627
|
+
* Gets whether an element is visible for the purposes of interactivity.
|
|
628
|
+
*
|
|
629
|
+
* This will capture states like `display: none` and `visibility: hidden`, but not things like
|
|
630
|
+
* being clipped by an `overflow: hidden` parent or being outside the viewport.
|
|
631
|
+
*
|
|
632
|
+
* @param {?} element
|
|
633
|
+
* @return {?} Whether the element is visible.
|
|
722
634
|
*/
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
635
|
+
InteractivityChecker.prototype.isVisible = function (element) {
|
|
636
|
+
return hasGeometry(element) && getComputedStyle(element).visibility === 'visible';
|
|
637
|
+
};
|
|
638
|
+
/**
|
|
639
|
+
* Gets whether an element can be reached via Tab key.
|
|
640
|
+
* Assumes that the element has already been checked with isFocusable.
|
|
641
|
+
*
|
|
642
|
+
* @param {?} element Element to be checked.
|
|
643
|
+
* @return {?} Whether the element is tabbable.
|
|
644
|
+
*/
|
|
645
|
+
InteractivityChecker.prototype.isTabbable = function (element) {
|
|
646
|
+
// Nothing is tabbable on the the server 😎
|
|
647
|
+
if (!this._platform.isBrowser) {
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
var /** @type {?} */ frameElement = (getWindow(element).frameElement);
|
|
651
|
+
if (frameElement) {
|
|
652
|
+
var /** @type {?} */ frameType = frameElement && frameElement.nodeName.toLowerCase();
|
|
653
|
+
// Frame elements inherit their tabindex onto all child elements.
|
|
654
|
+
if (getTabIndexValue(frameElement) === -1) {
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
// Webkit and Blink consider anything inside of an <object> element as non-tabbable.
|
|
658
|
+
if ((this._platform.BLINK || this._platform.WEBKIT) && frameType === 'object') {
|
|
659
|
+
return false;
|
|
660
|
+
}
|
|
661
|
+
// Webkit and Blink disable tabbing to an element inside of an invisible frame.
|
|
662
|
+
if ((this._platform.BLINK || this._platform.WEBKIT) && !this.isVisible(frameElement)) {
|
|
663
|
+
return false;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
var /** @type {?} */ nodeName = element.nodeName.toLowerCase();
|
|
667
|
+
var /** @type {?} */ tabIndexValue = getTabIndexValue(element);
|
|
668
|
+
if (element.hasAttribute('contenteditable')) {
|
|
669
|
+
return tabIndexValue !== -1;
|
|
670
|
+
}
|
|
671
|
+
if (nodeName === 'iframe') {
|
|
672
|
+
// The frames may be tabbable depending on content, but it's not possibly to reliably
|
|
673
|
+
// investigate the content of the frames.
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
if (nodeName === 'audio') {
|
|
677
|
+
if (!element.hasAttribute('controls')) {
|
|
678
|
+
// By default an <audio> element without the controls enabled is not tabbable.
|
|
679
|
+
return false;
|
|
680
|
+
}
|
|
681
|
+
else if (this._platform.BLINK) {
|
|
682
|
+
// In Blink <audio controls> elements are always tabbable.
|
|
683
|
+
return true;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (nodeName === 'video') {
|
|
687
|
+
if (!element.hasAttribute('controls') && this._platform.TRIDENT) {
|
|
688
|
+
// In Trident a <video> element without the controls enabled is not tabbable.
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
else if (this._platform.BLINK || this._platform.FIREFOX) {
|
|
692
|
+
// In Chrome and Firefox <video controls> elements are always tabbable.
|
|
693
|
+
return true;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (nodeName === 'object' && (this._platform.BLINK || this._platform.WEBKIT)) {
|
|
697
|
+
// In all Blink and WebKit based browsers <object> elements are never tabbable.
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
// In iOS the browser only considers some specific elements as tabbable.
|
|
701
|
+
if (this._platform.WEBKIT && this._platform.IOS && !isPotentiallyTabbableIOS(element)) {
|
|
702
|
+
return false;
|
|
726
703
|
}
|
|
704
|
+
return element.tabIndex >= 0;
|
|
727
705
|
};
|
|
728
706
|
/**
|
|
729
|
-
*
|
|
707
|
+
* Gets whether an element can be focused by the user.
|
|
708
|
+
*
|
|
709
|
+
* @param {?} element Element to be checked.
|
|
710
|
+
* @return {?} Whether the element is focusable.
|
|
730
711
|
*/
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
liveEl.setAttribute('aria-live', 'polite');
|
|
736
|
-
document.body.appendChild(liveEl);
|
|
737
|
-
return liveEl;
|
|
712
|
+
InteractivityChecker.prototype.isFocusable = function (element) {
|
|
713
|
+
// Perform checks in order of left to most expensive.
|
|
714
|
+
// Again, naive approach that does not capture many edge cases and browser quirks.
|
|
715
|
+
return isPotentiallyFocusable(element) && !this.isDisabled(element) && this.isVisible(element);
|
|
738
716
|
};
|
|
739
|
-
|
|
717
|
+
InteractivityChecker.decorators = [
|
|
718
|
+
{ type: _angular_core.Injectable },
|
|
719
|
+
];
|
|
720
|
+
/**
|
|
721
|
+
* @nocollapse
|
|
722
|
+
*/
|
|
723
|
+
InteractivityChecker.ctorParameters = function () { return [
|
|
724
|
+
{ type: _angular_cdk_platform.Platform, },
|
|
725
|
+
]; };
|
|
726
|
+
return InteractivityChecker;
|
|
740
727
|
}());
|
|
741
|
-
LiveAnnouncer.decorators = [
|
|
742
|
-
{ type: _angular_core.Injectable },
|
|
743
|
-
];
|
|
744
|
-
/**
|
|
745
|
-
* @nocollapse
|
|
746
|
-
*/
|
|
747
|
-
LiveAnnouncer.ctorParameters = function () { return [
|
|
748
|
-
{ type: undefined, decorators: [{ type: _angular_core.Optional }, { type: _angular_core.Inject, args: [LIVE_ANNOUNCER_ELEMENT_TOKEN,] },] },
|
|
749
|
-
{ type: _angular_cdk_platform.Platform, },
|
|
750
|
-
]; };
|
|
751
728
|
/**
|
|
752
|
-
*
|
|
753
|
-
* @param {?}
|
|
754
|
-
* @param {?} liveElement
|
|
755
|
-
* @param {?} platform
|
|
729
|
+
* Checks whether the specified element has any geometry / rectangles.
|
|
730
|
+
* @param {?} element
|
|
756
731
|
* @return {?}
|
|
757
732
|
*/
|
|
758
|
-
function
|
|
759
|
-
|
|
733
|
+
function hasGeometry(element) {
|
|
734
|
+
// Use logic from jQuery to check for an invisible element.
|
|
735
|
+
// See https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js#L12
|
|
736
|
+
return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
|
|
760
737
|
}
|
|
761
738
|
/**
|
|
762
|
-
*
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
// If there is already a LiveAnnouncer available, use that. Otherwise, provide a new one.
|
|
766
|
-
provide: LiveAnnouncer,
|
|
767
|
-
deps: [
|
|
768
|
-
[new _angular_core.Optional(), new _angular_core.SkipSelf(), LiveAnnouncer],
|
|
769
|
-
[new _angular_core.Optional(), new _angular_core.Inject(LIVE_ANNOUNCER_ELEMENT_TOKEN)],
|
|
770
|
-
_angular_cdk_platform.Platform,
|
|
771
|
-
],
|
|
772
|
-
useFactory: LIVE_ANNOUNCER_PROVIDER_FACTORY
|
|
773
|
-
};
|
|
774
|
-
/**
|
|
775
|
-
* IDs are deliminated by an empty space, as per the spec.
|
|
739
|
+
* Gets whether an element's
|
|
740
|
+
* @param {?} element
|
|
741
|
+
* @return {?}
|
|
776
742
|
*/
|
|
777
|
-
|
|
743
|
+
function isNativeFormElement(element) {
|
|
744
|
+
var /** @type {?} */ nodeName = element.nodeName.toLowerCase();
|
|
745
|
+
return nodeName === 'input' ||
|
|
746
|
+
nodeName === 'select' ||
|
|
747
|
+
nodeName === 'button' ||
|
|
748
|
+
nodeName === 'textarea';
|
|
749
|
+
}
|
|
778
750
|
/**
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
* @param {?} el
|
|
782
|
-
* @param {?} attr
|
|
783
|
-
* @param {?} id
|
|
751
|
+
* Gets whether an element is an <input type="hidden">.
|
|
752
|
+
* @param {?} element
|
|
784
753
|
* @return {?}
|
|
785
754
|
*/
|
|
786
|
-
function
|
|
787
|
-
|
|
788
|
-
if (ids.some(function (existingId) { return existingId.trim() == id.trim(); })) {
|
|
789
|
-
return;
|
|
790
|
-
}
|
|
791
|
-
ids.push(id.trim());
|
|
792
|
-
el.setAttribute(attr, ids.join(ID_DELIMINATOR));
|
|
755
|
+
function isHiddenInput(element) {
|
|
756
|
+
return isInputElement(element) && element.type == 'hidden';
|
|
793
757
|
}
|
|
794
758
|
/**
|
|
795
|
-
*
|
|
796
|
-
*
|
|
797
|
-
* @param {?} el
|
|
798
|
-
* @param {?} attr
|
|
799
|
-
* @param {?} id
|
|
759
|
+
* Gets whether an element is an anchor that has an href attribute.
|
|
760
|
+
* @param {?} element
|
|
800
761
|
* @return {?}
|
|
801
762
|
*/
|
|
802
|
-
function
|
|
803
|
-
|
|
804
|
-
var /** @type {?} */ filteredIds = ids.filter(function (val) { return val != id.trim(); });
|
|
805
|
-
el.setAttribute(attr, filteredIds.join(ID_DELIMINATOR));
|
|
763
|
+
function isAnchorWithHref(element) {
|
|
764
|
+
return isAnchorElement(element) && element.hasAttribute('href');
|
|
806
765
|
}
|
|
807
766
|
/**
|
|
808
|
-
* Gets
|
|
809
|
-
*
|
|
810
|
-
* @param {?} el
|
|
811
|
-
* @param {?} attr
|
|
767
|
+
* Gets whether an element is an input element.
|
|
768
|
+
* @param {?} element
|
|
812
769
|
* @return {?}
|
|
813
770
|
*/
|
|
814
|
-
function
|
|
815
|
-
|
|
816
|
-
return (el.getAttribute(attr) || '').match(/\S+/g) || [];
|
|
771
|
+
function isInputElement(element) {
|
|
772
|
+
return element.nodeName.toLowerCase() == 'input';
|
|
817
773
|
}
|
|
818
774
|
/**
|
|
819
|
-
*
|
|
775
|
+
* Gets whether an element is an anchor element.
|
|
776
|
+
* @param {?} element
|
|
777
|
+
* @return {?}
|
|
820
778
|
*/
|
|
821
|
-
|
|
779
|
+
function isAnchorElement(element) {
|
|
780
|
+
return element.nodeName.toLowerCase() == 'a';
|
|
781
|
+
}
|
|
822
782
|
/**
|
|
823
|
-
*
|
|
783
|
+
* Gets whether an element has a valid tabindex.
|
|
784
|
+
* @param {?} element
|
|
785
|
+
* @return {?}
|
|
824
786
|
*/
|
|
825
|
-
|
|
787
|
+
function hasValidTabIndex(element) {
|
|
788
|
+
if (!element.hasAttribute('tabindex') || element.tabIndex === undefined) {
|
|
789
|
+
return false;
|
|
790
|
+
}
|
|
791
|
+
var /** @type {?} */ tabIndex = element.getAttribute('tabindex');
|
|
792
|
+
// IE11 parses tabindex="" as the value "-32768"
|
|
793
|
+
if (tabIndex == '-32768') {
|
|
794
|
+
return false;
|
|
795
|
+
}
|
|
796
|
+
return !!(tabIndex && !isNaN(parseInt(tabIndex, 10)));
|
|
797
|
+
}
|
|
826
798
|
/**
|
|
827
|
-
*
|
|
799
|
+
* Returns the parsed tabindex from the element attributes instead of returning the
|
|
800
|
+
* evaluated tabindex from the browsers defaults.
|
|
801
|
+
* @param {?} element
|
|
802
|
+
* @return {?}
|
|
828
803
|
*/
|
|
829
|
-
|
|
804
|
+
function getTabIndexValue(element) {
|
|
805
|
+
if (!hasValidTabIndex(element)) {
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
// See browser issue in Gecko https://bugzilla.mozilla.org/show_bug.cgi?id=1128054
|
|
809
|
+
var /** @type {?} */ tabIndex = parseInt(element.getAttribute('tabindex') || '', 10);
|
|
810
|
+
return isNaN(tabIndex) ? -1 : tabIndex;
|
|
811
|
+
}
|
|
830
812
|
/**
|
|
831
|
-
*
|
|
813
|
+
* Checks whether the specified element is potentially tabbable on iOS
|
|
814
|
+
* @param {?} element
|
|
815
|
+
* @return {?}
|
|
832
816
|
*/
|
|
833
|
-
|
|
817
|
+
function isPotentiallyTabbableIOS(element) {
|
|
818
|
+
var /** @type {?} */ nodeName = element.nodeName.toLowerCase();
|
|
819
|
+
var /** @type {?} */ inputType = nodeName === 'input' && ((element)).type;
|
|
820
|
+
return inputType === 'text'
|
|
821
|
+
|| inputType === 'password'
|
|
822
|
+
|| nodeName === 'select'
|
|
823
|
+
|| nodeName === 'textarea';
|
|
824
|
+
}
|
|
834
825
|
/**
|
|
835
|
-
*
|
|
826
|
+
* Gets whether an element is potentially focusable without taking current visible/disabled state
|
|
827
|
+
* into account.
|
|
828
|
+
* @param {?} element
|
|
829
|
+
* @return {?}
|
|
836
830
|
*/
|
|
837
|
-
|
|
831
|
+
function isPotentiallyFocusable(element) {
|
|
832
|
+
// Inputs are potentially focusable *unless* they're type="hidden".
|
|
833
|
+
if (isHiddenInput(element)) {
|
|
834
|
+
return false;
|
|
835
|
+
}
|
|
836
|
+
return isNativeFormElement(element) ||
|
|
837
|
+
isAnchorWithHref(element) ||
|
|
838
|
+
element.hasAttribute('contenteditable') ||
|
|
839
|
+
hasValidTabIndex(element);
|
|
840
|
+
}
|
|
838
841
|
/**
|
|
839
|
-
*
|
|
842
|
+
* Gets the parent window of a DOM node with regards of being inside of an iframe.
|
|
843
|
+
* @param {?} node
|
|
844
|
+
* @return {?}
|
|
840
845
|
*/
|
|
841
|
-
|
|
846
|
+
function getWindow(node) {
|
|
847
|
+
return node.ownerDocument.defaultView || window;
|
|
848
|
+
}
|
|
849
|
+
|
|
842
850
|
/**
|
|
843
|
-
*
|
|
844
|
-
*
|
|
845
|
-
*
|
|
846
|
-
*
|
|
851
|
+
* Class that allows for trapping focus within a DOM element.
|
|
852
|
+
*
|
|
853
|
+
* NOTE: This class currently uses a very simple (naive) approach to focus trapping.
|
|
854
|
+
* It assumes that the tab order is the same as DOM order, which is not necessarily true.
|
|
855
|
+
* Things like tabIndex > 0, flex `order`, and shadow roots can cause to two to misalign.
|
|
856
|
+
* This will be replaced with a more intelligent solution before the library is considered stable.
|
|
847
857
|
*/
|
|
848
|
-
var
|
|
858
|
+
var FocusTrap = (function () {
|
|
849
859
|
/**
|
|
860
|
+
* @param {?} _element
|
|
850
861
|
* @param {?} _platform
|
|
862
|
+
* @param {?} _checker
|
|
863
|
+
* @param {?} _ngZone
|
|
864
|
+
* @param {?=} deferAnchors
|
|
851
865
|
*/
|
|
852
|
-
function
|
|
866
|
+
function FocusTrap(_element, _platform, _checker, _ngZone, deferAnchors) {
|
|
867
|
+
if (deferAnchors === void 0) { deferAnchors = false; }
|
|
868
|
+
this._element = _element;
|
|
853
869
|
this._platform = _platform;
|
|
870
|
+
this._checker = _checker;
|
|
871
|
+
this._ngZone = _ngZone;
|
|
872
|
+
this._enabled = true;
|
|
873
|
+
if (!deferAnchors) {
|
|
874
|
+
this.attachAnchors();
|
|
875
|
+
}
|
|
854
876
|
}
|
|
877
|
+
Object.defineProperty(FocusTrap.prototype, "enabled", {
|
|
878
|
+
/**
|
|
879
|
+
* Whether the focus trap is active.
|
|
880
|
+
* @return {?}
|
|
881
|
+
*/
|
|
882
|
+
get: function () { return this._enabled; },
|
|
883
|
+
/**
|
|
884
|
+
* @param {?} val
|
|
885
|
+
* @return {?}
|
|
886
|
+
*/
|
|
887
|
+
set: function (val) {
|
|
888
|
+
this._enabled = val;
|
|
889
|
+
if (this._startAnchor && this._endAnchor) {
|
|
890
|
+
this._startAnchor.tabIndex = this._endAnchor.tabIndex = this._enabled ? 0 : -1;
|
|
891
|
+
}
|
|
892
|
+
},
|
|
893
|
+
enumerable: true,
|
|
894
|
+
configurable: true
|
|
895
|
+
});
|
|
855
896
|
/**
|
|
856
|
-
*
|
|
857
|
-
* the message. If the same message has already been registered, then it will reuse the created
|
|
858
|
-
* message element.
|
|
859
|
-
* @param {?} hostElement
|
|
860
|
-
* @param {?} message
|
|
897
|
+
* Destroys the focus trap by cleaning up the anchors.
|
|
861
898
|
* @return {?}
|
|
862
899
|
*/
|
|
863
|
-
|
|
864
|
-
if (
|
|
900
|
+
FocusTrap.prototype.destroy = function () {
|
|
901
|
+
if (this._startAnchor && this._startAnchor.parentNode) {
|
|
902
|
+
this._startAnchor.parentNode.removeChild(this._startAnchor);
|
|
903
|
+
}
|
|
904
|
+
if (this._endAnchor && this._endAnchor.parentNode) {
|
|
905
|
+
this._endAnchor.parentNode.removeChild(this._endAnchor);
|
|
906
|
+
}
|
|
907
|
+
this._startAnchor = this._endAnchor = null;
|
|
908
|
+
};
|
|
909
|
+
/**
|
|
910
|
+
* Inserts the anchors into the DOM. This is usually done automatically
|
|
911
|
+
* in the constructor, but can be deferred for cases like directives with `*ngIf`.
|
|
912
|
+
* @return {?}
|
|
913
|
+
*/
|
|
914
|
+
FocusTrap.prototype.attachAnchors = function () {
|
|
915
|
+
var _this = this;
|
|
916
|
+
// If we're not on the browser, there can be no focus to trap.
|
|
917
|
+
if (!this._platform.isBrowser) {
|
|
865
918
|
return;
|
|
866
919
|
}
|
|
867
|
-
if (!
|
|
868
|
-
|
|
920
|
+
if (!this._startAnchor) {
|
|
921
|
+
this._startAnchor = this._createAnchor();
|
|
869
922
|
}
|
|
870
|
-
if (!
|
|
871
|
-
|
|
923
|
+
if (!this._endAnchor) {
|
|
924
|
+
this._endAnchor = this._createAnchor();
|
|
872
925
|
}
|
|
926
|
+
this._ngZone.runOutsideAngular(function () {
|
|
927
|
+
((_this._startAnchor)).addEventListener('focus', function () {
|
|
928
|
+
_this.focusLastTabbableElement();
|
|
929
|
+
}); /** @type {?} */
|
|
930
|
+
((_this._endAnchor)).addEventListener('focus', function () {
|
|
931
|
+
_this.focusFirstTabbableElement();
|
|
932
|
+
});
|
|
933
|
+
if (_this._element.parentNode) {
|
|
934
|
+
_this._element.parentNode.insertBefore(/** @type {?} */ ((_this._startAnchor)), _this._element);
|
|
935
|
+
_this._element.parentNode.insertBefore(/** @type {?} */ ((_this._endAnchor)), _this._element.nextSibling);
|
|
936
|
+
}
|
|
937
|
+
});
|
|
873
938
|
};
|
|
874
939
|
/**
|
|
875
|
-
*
|
|
876
|
-
*
|
|
877
|
-
* @
|
|
940
|
+
* Waits for the zone to stabilize, then either focuses the first element that the
|
|
941
|
+
* user specified, or the first tabbable element.
|
|
942
|
+
* @return {?} Returns a promise that resolves with a boolean, depending
|
|
943
|
+
* on whether focus was moved successfuly.
|
|
944
|
+
*/
|
|
945
|
+
FocusTrap.prototype.focusInitialElementWhenReady = function () {
|
|
946
|
+
var _this = this;
|
|
947
|
+
return new Promise(function (resolve) {
|
|
948
|
+
_this._executeOnStable(function () { return resolve(_this.focusInitialElement()); });
|
|
949
|
+
});
|
|
950
|
+
};
|
|
951
|
+
/**
|
|
952
|
+
* Waits for the zone to stabilize, then focuses
|
|
953
|
+
* the first tabbable element within the focus trap region.
|
|
954
|
+
* @return {?} Returns a promise that resolves with a boolean, depending
|
|
955
|
+
* on whether focus was moved successfuly.
|
|
956
|
+
*/
|
|
957
|
+
FocusTrap.prototype.focusFirstTabbableElementWhenReady = function () {
|
|
958
|
+
var _this = this;
|
|
959
|
+
return new Promise(function (resolve) {
|
|
960
|
+
_this._executeOnStable(function () { return resolve(_this.focusFirstTabbableElement()); });
|
|
961
|
+
});
|
|
962
|
+
};
|
|
963
|
+
/**
|
|
964
|
+
* Waits for the zone to stabilize, then focuses
|
|
965
|
+
* the last tabbable element within the focus trap region.
|
|
966
|
+
* @return {?} Returns a promise that resolves with a boolean, depending
|
|
967
|
+
* on whether focus was moved successfuly.
|
|
968
|
+
*/
|
|
969
|
+
FocusTrap.prototype.focusLastTabbableElementWhenReady = function () {
|
|
970
|
+
var _this = this;
|
|
971
|
+
return new Promise(function (resolve) {
|
|
972
|
+
_this._executeOnStable(function () { return resolve(_this.focusLastTabbableElement()); });
|
|
973
|
+
});
|
|
974
|
+
};
|
|
975
|
+
/**
|
|
976
|
+
* Get the specified boundary element of the trapped region.
|
|
977
|
+
* @param {?} bound The boundary to get (start or end of trapped region).
|
|
978
|
+
* @return {?} The boundary element.
|
|
979
|
+
*/
|
|
980
|
+
FocusTrap.prototype._getRegionBoundary = function (bound) {
|
|
981
|
+
// Contains the deprecated version of selector, for temporary backwards comparability.
|
|
982
|
+
var /** @type {?} */ markers = (this._element.querySelectorAll("[cdk-focus-region-" + bound + "], " +
|
|
983
|
+
("[cdk-focus-" + bound + "]")));
|
|
984
|
+
for (var /** @type {?} */ i = 0; i < markers.length; i++) {
|
|
985
|
+
if (markers[i].hasAttribute("cdk-focus-" + bound)) {
|
|
986
|
+
console.warn("Found use of deprecated attribute 'cdk-focus-" + bound + "'," +
|
|
987
|
+
(" use 'cdk-focus-region-" + bound + "' instead."), markers[i]);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
if (bound == 'start') {
|
|
991
|
+
return markers.length ? markers[0] : this._getFirstTabbableElement(this._element);
|
|
992
|
+
}
|
|
993
|
+
return markers.length ?
|
|
994
|
+
markers[markers.length - 1] : this._getLastTabbableElement(this._element);
|
|
995
|
+
};
|
|
996
|
+
/**
|
|
997
|
+
* Focuses the element that should be focused when the focus trap is initialized.
|
|
998
|
+
* @return {?} Returns whether focus was moved successfuly.
|
|
999
|
+
*/
|
|
1000
|
+
FocusTrap.prototype.focusInitialElement = function () {
|
|
1001
|
+
var /** @type {?} */ redirectToElement = (this._element.querySelector('[cdk-focus-initial]'));
|
|
1002
|
+
if (redirectToElement) {
|
|
1003
|
+
redirectToElement.focus();
|
|
1004
|
+
return true;
|
|
1005
|
+
}
|
|
1006
|
+
return this.focusFirstTabbableElement();
|
|
1007
|
+
};
|
|
1008
|
+
/**
|
|
1009
|
+
* Focuses the first tabbable element within the focus trap region.
|
|
1010
|
+
* @return {?} Returns whether focus was moved successfuly.
|
|
1011
|
+
*/
|
|
1012
|
+
FocusTrap.prototype.focusFirstTabbableElement = function () {
|
|
1013
|
+
var /** @type {?} */ redirectToElement = this._getRegionBoundary('start');
|
|
1014
|
+
if (redirectToElement) {
|
|
1015
|
+
redirectToElement.focus();
|
|
1016
|
+
}
|
|
1017
|
+
return !!redirectToElement;
|
|
1018
|
+
};
|
|
1019
|
+
/**
|
|
1020
|
+
* Focuses the last tabbable element within the focus trap region.
|
|
1021
|
+
* @return {?} Returns whether focus was moved successfuly.
|
|
1022
|
+
*/
|
|
1023
|
+
FocusTrap.prototype.focusLastTabbableElement = function () {
|
|
1024
|
+
var /** @type {?} */ redirectToElement = this._getRegionBoundary('end');
|
|
1025
|
+
if (redirectToElement) {
|
|
1026
|
+
redirectToElement.focus();
|
|
1027
|
+
}
|
|
1028
|
+
return !!redirectToElement;
|
|
1029
|
+
};
|
|
1030
|
+
/**
|
|
1031
|
+
* Get the first tabbable element from a DOM subtree (inclusive).
|
|
1032
|
+
* @param {?} root
|
|
878
1033
|
* @return {?}
|
|
879
1034
|
*/
|
|
880
|
-
|
|
881
|
-
if (
|
|
882
|
-
return;
|
|
1035
|
+
FocusTrap.prototype._getFirstTabbableElement = function (root) {
|
|
1036
|
+
if (this._checker.isFocusable(root) && this._checker.isTabbable(root)) {
|
|
1037
|
+
return root;
|
|
883
1038
|
}
|
|
884
|
-
|
|
885
|
-
|
|
1039
|
+
// Iterate in DOM order. Note that IE doesn't have `children` for SVG so we fall
|
|
1040
|
+
// back to `childNodes` which includes text nodes, comments etc.
|
|
1041
|
+
var /** @type {?} */ children = root.children || root.childNodes;
|
|
1042
|
+
for (var /** @type {?} */ i = 0; i < children.length; i++) {
|
|
1043
|
+
var /** @type {?} */ tabbableChild = children[i].nodeType === Node.ELEMENT_NODE ?
|
|
1044
|
+
this._getFirstTabbableElement(/** @type {?} */ (children[i])) :
|
|
1045
|
+
null;
|
|
1046
|
+
if (tabbableChild) {
|
|
1047
|
+
return tabbableChild;
|
|
1048
|
+
}
|
|
886
1049
|
}
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
1050
|
+
return null;
|
|
1051
|
+
};
|
|
1052
|
+
/**
|
|
1053
|
+
* Get the last tabbable element from a DOM subtree (inclusive).
|
|
1054
|
+
* @param {?} root
|
|
1055
|
+
* @return {?}
|
|
1056
|
+
*/
|
|
1057
|
+
FocusTrap.prototype._getLastTabbableElement = function (root) {
|
|
1058
|
+
if (this._checker.isFocusable(root) && this._checker.isTabbable(root)) {
|
|
1059
|
+
return root;
|
|
890
1060
|
}
|
|
891
|
-
|
|
892
|
-
|
|
1061
|
+
// Iterate in reverse DOM order.
|
|
1062
|
+
var /** @type {?} */ children = root.children || root.childNodes;
|
|
1063
|
+
for (var /** @type {?} */ i = children.length - 1; i >= 0; i--) {
|
|
1064
|
+
var /** @type {?} */ tabbableChild = children[i].nodeType === Node.ELEMENT_NODE ?
|
|
1065
|
+
this._getLastTabbableElement(/** @type {?} */ (children[i])) :
|
|
1066
|
+
null;
|
|
1067
|
+
if (tabbableChild) {
|
|
1068
|
+
return tabbableChild;
|
|
1069
|
+
}
|
|
893
1070
|
}
|
|
1071
|
+
return null;
|
|
894
1072
|
};
|
|
895
1073
|
/**
|
|
896
|
-
*
|
|
1074
|
+
* Creates an anchor element.
|
|
897
1075
|
* @return {?}
|
|
898
1076
|
*/
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
1077
|
+
FocusTrap.prototype._createAnchor = function () {
|
|
1078
|
+
var /** @type {?} */ anchor = document.createElement('div');
|
|
1079
|
+
anchor.tabIndex = this._enabled ? 0 : -1;
|
|
1080
|
+
anchor.classList.add('cdk-visually-hidden');
|
|
1081
|
+
anchor.classList.add('cdk-focus-trap-anchor');
|
|
1082
|
+
return anchor;
|
|
1083
|
+
};
|
|
1084
|
+
/**
|
|
1085
|
+
* Executes a function when the zone is stable.
|
|
1086
|
+
* @param {?} fn
|
|
1087
|
+
* @return {?}
|
|
1088
|
+
*/
|
|
1089
|
+
FocusTrap.prototype._executeOnStable = function (fn) {
|
|
1090
|
+
if (this._ngZone.isStable) {
|
|
1091
|
+
fn();
|
|
907
1092
|
}
|
|
908
|
-
|
|
909
|
-
|
|
1093
|
+
else {
|
|
1094
|
+
_angular_cdk_rxjs.first.call(this._ngZone.onStable.asObservable()).subscribe(fn);
|
|
910
1095
|
}
|
|
911
|
-
messageRegistry.clear();
|
|
912
1096
|
};
|
|
913
|
-
return
|
|
1097
|
+
return FocusTrap;
|
|
914
1098
|
}());
|
|
915
|
-
AriaDescriber.decorators = [
|
|
916
|
-
{ type: _angular_core.Injectable },
|
|
917
|
-
];
|
|
918
|
-
/**
|
|
919
|
-
* @nocollapse
|
|
920
|
-
*/
|
|
921
|
-
AriaDescriber.ctorParameters = function () { return [
|
|
922
|
-
{ type: _angular_cdk_platform.Platform, },
|
|
923
|
-
]; };
|
|
924
|
-
/**
|
|
925
|
-
* Creates a new element in the visually hidden message container element with the message
|
|
926
|
-
* as its content and adds it to the message registry.
|
|
927
|
-
* @param {?} message
|
|
928
|
-
* @return {?}
|
|
929
|
-
*/
|
|
930
|
-
function createMessageElement(message) {
|
|
931
|
-
var /** @type {?} */ messageElement = document.createElement('div');
|
|
932
|
-
messageElement.setAttribute('id', CDK_DESCRIBEDBY_ID_PREFIX + "-" + nextId++);
|
|
933
|
-
messageElement.appendChild(/** @type {?} */ ((document.createTextNode(message))));
|
|
934
|
-
if (!messagesContainer) {
|
|
935
|
-
createMessagesContainer();
|
|
936
|
-
} /** @type {?} */
|
|
937
|
-
((messagesContainer)).appendChild(messageElement);
|
|
938
|
-
messageRegistry.set(message, { messageElement: messageElement, referenceCount: 0 });
|
|
939
|
-
}
|
|
940
|
-
/**
|
|
941
|
-
* Deletes the message element from the global messages container.
|
|
942
|
-
* @param {?} message
|
|
943
|
-
* @return {?}
|
|
944
|
-
*/
|
|
945
|
-
function deleteMessageElement(message) {
|
|
946
|
-
var /** @type {?} */ registeredMessage = messageRegistry.get(message);
|
|
947
|
-
var /** @type {?} */ messageElement = registeredMessage && registeredMessage.messageElement;
|
|
948
|
-
if (messagesContainer && messageElement) {
|
|
949
|
-
messagesContainer.removeChild(messageElement);
|
|
950
|
-
}
|
|
951
|
-
messageRegistry.delete(message);
|
|
952
|
-
}
|
|
953
|
-
/**
|
|
954
|
-
* Creates the global container for all aria-describedby messages.
|
|
955
|
-
* @return {?}
|
|
956
|
-
*/
|
|
957
|
-
function createMessagesContainer() {
|
|
958
|
-
messagesContainer = document.createElement('div');
|
|
959
|
-
messagesContainer.setAttribute('id', MESSAGES_CONTAINER_ID);
|
|
960
|
-
messagesContainer.setAttribute('aria-hidden', 'true');
|
|
961
|
-
messagesContainer.style.display = 'none';
|
|
962
|
-
document.body.appendChild(messagesContainer);
|
|
963
|
-
}
|
|
964
|
-
/**
|
|
965
|
-
* Deletes the global messages container.
|
|
966
|
-
* @return {?}
|
|
967
|
-
*/
|
|
968
|
-
function deleteMessagesContainer() {
|
|
969
|
-
document.body.removeChild(/** @type {?} */ ((messagesContainer)));
|
|
970
|
-
messagesContainer = null;
|
|
971
|
-
}
|
|
972
|
-
/**
|
|
973
|
-
* Removes all cdk-describedby messages that are hosted through the element.
|
|
974
|
-
* @param {?} element
|
|
975
|
-
* @return {?}
|
|
976
|
-
*/
|
|
977
|
-
function removeCdkDescribedByReferenceIds(element) {
|
|
978
|
-
// Remove all aria-describedby reference IDs that are prefixed by CDK_DESCRIBEDBY_ID_PREFIX
|
|
979
|
-
var /** @type {?} */ originalReferenceIds = getAriaReferenceIds(element, 'aria-describedby')
|
|
980
|
-
.filter(function (id) { return id.indexOf(CDK_DESCRIBEDBY_ID_PREFIX) != 0; });
|
|
981
|
-
element.setAttribute('aria-describedby', originalReferenceIds.join(' '));
|
|
982
|
-
}
|
|
983
|
-
/**
|
|
984
|
-
* Adds a message reference to the element using aria-describedby and increments the registered
|
|
985
|
-
* message's reference count.
|
|
986
|
-
* @param {?} element
|
|
987
|
-
* @param {?} message
|
|
988
|
-
* @return {?}
|
|
989
|
-
*/
|
|
990
|
-
function addMessageReference(element, message) {
|
|
991
|
-
var /** @type {?} */ registeredMessage = ((messageRegistry.get(message)));
|
|
992
|
-
// Add the aria-describedby reference and set the describedby_host attribute to mark the element.
|
|
993
|
-
addAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id);
|
|
994
|
-
element.setAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE, '');
|
|
995
|
-
registeredMessage.referenceCount++;
|
|
996
|
-
}
|
|
997
|
-
/**
|
|
998
|
-
* Removes a message reference from the element using aria-describedby and decrements the registered
|
|
999
|
-
* message's reference count.
|
|
1000
|
-
* @param {?} element
|
|
1001
|
-
* @param {?} message
|
|
1002
|
-
* @return {?}
|
|
1003
|
-
*/
|
|
1004
|
-
function removeMessageReference(element, message) {
|
|
1005
|
-
var /** @type {?} */ registeredMessage = ((messageRegistry.get(message)));
|
|
1006
|
-
registeredMessage.referenceCount--;
|
|
1007
|
-
removeAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id);
|
|
1008
|
-
element.removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE);
|
|
1009
|
-
}
|
|
1010
|
-
/**
|
|
1011
|
-
* Returns true if the element has been described by the provided message ID.
|
|
1012
|
-
* @param {?} element
|
|
1013
|
-
* @param {?} message
|
|
1014
|
-
* @return {?}
|
|
1015
|
-
*/
|
|
1016
|
-
function isElementDescribedByMessage(element, message) {
|
|
1017
|
-
var /** @type {?} */ referenceIds = getAriaReferenceIds(element, 'aria-describedby');
|
|
1018
|
-
var /** @type {?} */ registeredMessage = messageRegistry.get(message);
|
|
1019
|
-
var /** @type {?} */ messageId = registeredMessage && registeredMessage.messageElement.id;
|
|
1020
|
-
return !!messageId && referenceIds.indexOf(messageId) != -1;
|
|
1021
|
-
}
|
|
1022
|
-
/**
|
|
1023
|
-
* \@docs-private
|
|
1024
|
-
* @param {?} parentDispatcher
|
|
1025
|
-
* @param {?} platform
|
|
1026
|
-
* @return {?}
|
|
1027
|
-
*/
|
|
1028
|
-
function ARIA_DESCRIBER_PROVIDER_FACTORY(parentDispatcher, platform) {
|
|
1029
|
-
return parentDispatcher || new AriaDescriber(platform);
|
|
1030
|
-
}
|
|
1031
1099
|
/**
|
|
1032
|
-
*
|
|
1033
|
-
*/
|
|
1034
|
-
var ARIA_DESCRIBER_PROVIDER = {
|
|
1035
|
-
// If there is already an AriaDescriber available, use that. Otherwise, provide a new one.
|
|
1036
|
-
provide: AriaDescriber,
|
|
1037
|
-
deps: [
|
|
1038
|
-
[new _angular_core.Optional(), new _angular_core.SkipSelf(), AriaDescriber],
|
|
1039
|
-
_angular_cdk_platform.Platform
|
|
1040
|
-
],
|
|
1041
|
-
useFactory: ARIA_DESCRIBER_PROVIDER_FACTORY
|
|
1042
|
-
};
|
|
1043
|
-
// This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found
|
|
1044
|
-
// that a value of around 650ms seems appropriate.
|
|
1045
|
-
var TOUCH_BUFFER_MS = 650;
|
|
1046
|
-
/**
|
|
1047
|
-
* Monitors mouse and keyboard events to determine the cause of focus events.
|
|
1100
|
+
* Factory that allows easy instantiation of focus traps.
|
|
1048
1101
|
*/
|
|
1049
|
-
var
|
|
1102
|
+
var FocusTrapFactory = (function () {
|
|
1050
1103
|
/**
|
|
1051
|
-
* @param {?}
|
|
1104
|
+
* @param {?} _checker
|
|
1052
1105
|
* @param {?} _platform
|
|
1106
|
+
* @param {?} _ngZone
|
|
1053
1107
|
*/
|
|
1054
|
-
function
|
|
1055
|
-
|
|
1056
|
-
this._ngZone = _ngZone;
|
|
1108
|
+
function FocusTrapFactory(_checker, _platform, _ngZone) {
|
|
1109
|
+
this._checker = _checker;
|
|
1057
1110
|
this._platform = _platform;
|
|
1058
|
-
|
|
1059
|
-
* The focus origin that the next focus event is a result of.
|
|
1060
|
-
*/
|
|
1061
|
-
this._origin = null;
|
|
1062
|
-
/**
|
|
1063
|
-
* Whether the window has just been focused.
|
|
1064
|
-
*/
|
|
1065
|
-
this._windowFocused = false;
|
|
1066
|
-
/**
|
|
1067
|
-
* Weak map of elements being monitored to their info.
|
|
1068
|
-
*/
|
|
1069
|
-
this._elementInfo = new WeakMap();
|
|
1070
|
-
this._ngZone.runOutsideAngular(function () { return _this._registerDocumentEvents(); });
|
|
1111
|
+
this._ngZone = _ngZone;
|
|
1071
1112
|
}
|
|
1072
1113
|
/**
|
|
1073
|
-
*
|
|
1074
|
-
* @param {
|
|
1075
|
-
* @
|
|
1076
|
-
* @param {?} checkChildren Whether to count the element as focused when its children are focused.
|
|
1077
|
-
* @return {?} An observable that emits when the focus state of the element changes.
|
|
1078
|
-
* When the element is blurred, null will be emitted.
|
|
1114
|
+
* @param {?} element
|
|
1115
|
+
* @param {?=} deferAnchors
|
|
1116
|
+
* @return {?}
|
|
1079
1117
|
*/
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
if (!this._platform.isBrowser) {
|
|
1084
|
-
return rxjs_observable_of.of(null);
|
|
1085
|
-
}
|
|
1086
|
-
// Check if we're already monitoring this element.
|
|
1087
|
-
if (this._elementInfo.has(element)) {
|
|
1088
|
-
var /** @type {?} */ cachedInfo = this._elementInfo.get(element); /** @type {?} */
|
|
1089
|
-
((cachedInfo)).checkChildren = checkChildren;
|
|
1090
|
-
return ((cachedInfo)).subject.asObservable();
|
|
1091
|
-
}
|
|
1092
|
-
// Create monitored element info.
|
|
1093
|
-
var /** @type {?} */ info = {
|
|
1094
|
-
unlisten: function () { },
|
|
1095
|
-
checkChildren: checkChildren,
|
|
1096
|
-
renderer: renderer,
|
|
1097
|
-
subject: new rxjs_Subject.Subject()
|
|
1098
|
-
};
|
|
1099
|
-
this._elementInfo.set(element, info);
|
|
1100
|
-
// Start listening. We need to listen in capture phase since focus events don't bubble.
|
|
1101
|
-
var /** @type {?} */ focusListener = function (event) { return _this._onFocus(event, element); };
|
|
1102
|
-
var /** @type {?} */ blurListener = function (event) { return _this._onBlur(event, element); };
|
|
1103
|
-
this._ngZone.runOutsideAngular(function () {
|
|
1104
|
-
element.addEventListener('focus', focusListener, true);
|
|
1105
|
-
element.addEventListener('blur', blurListener, true);
|
|
1106
|
-
});
|
|
1107
|
-
// Create an unlisten function for later.
|
|
1108
|
-
info.unlisten = function () {
|
|
1109
|
-
element.removeEventListener('focus', focusListener, true);
|
|
1110
|
-
element.removeEventListener('blur', blurListener, true);
|
|
1111
|
-
};
|
|
1112
|
-
return info.subject.asObservable();
|
|
1118
|
+
FocusTrapFactory.prototype.create = function (element, deferAnchors) {
|
|
1119
|
+
if (deferAnchors === void 0) { deferAnchors = false; }
|
|
1120
|
+
return new FocusTrap(element, this._platform, this._checker, this._ngZone, deferAnchors);
|
|
1113
1121
|
};
|
|
1122
|
+
FocusTrapFactory.decorators = [
|
|
1123
|
+
{ type: _angular_core.Injectable },
|
|
1124
|
+
];
|
|
1125
|
+
/**
|
|
1126
|
+
* @nocollapse
|
|
1127
|
+
*/
|
|
1128
|
+
FocusTrapFactory.ctorParameters = function () { return [
|
|
1129
|
+
{ type: InteractivityChecker, },
|
|
1130
|
+
{ type: _angular_cdk_platform.Platform, },
|
|
1131
|
+
{ type: _angular_core.NgZone, },
|
|
1132
|
+
]; };
|
|
1133
|
+
return FocusTrapFactory;
|
|
1134
|
+
}());
|
|
1135
|
+
/**
|
|
1136
|
+
* Directive for trapping focus within a region.
|
|
1137
|
+
* @deprecated
|
|
1138
|
+
*/
|
|
1139
|
+
var FocusTrapDeprecatedDirective = (function () {
|
|
1140
|
+
/**
|
|
1141
|
+
* @param {?} _elementRef
|
|
1142
|
+
* @param {?} _focusTrapFactory
|
|
1143
|
+
*/
|
|
1144
|
+
function FocusTrapDeprecatedDirective(_elementRef, _focusTrapFactory) {
|
|
1145
|
+
this._elementRef = _elementRef;
|
|
1146
|
+
this._focusTrapFactory = _focusTrapFactory;
|
|
1147
|
+
this.focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement, true);
|
|
1148
|
+
}
|
|
1149
|
+
Object.defineProperty(FocusTrapDeprecatedDirective.prototype, "disabled", {
|
|
1150
|
+
/**
|
|
1151
|
+
* Whether the focus trap is active.
|
|
1152
|
+
* @return {?}
|
|
1153
|
+
*/
|
|
1154
|
+
get: function () { return !this.focusTrap.enabled; },
|
|
1155
|
+
/**
|
|
1156
|
+
* @param {?} val
|
|
1157
|
+
* @return {?}
|
|
1158
|
+
*/
|
|
1159
|
+
set: function (val) {
|
|
1160
|
+
this.focusTrap.enabled = !_angular_cdk_coercion.coerceBooleanProperty(val);
|
|
1161
|
+
},
|
|
1162
|
+
enumerable: true,
|
|
1163
|
+
configurable: true
|
|
1164
|
+
});
|
|
1114
1165
|
/**
|
|
1115
|
-
* Stops monitoring an element and removes all focus classes.
|
|
1116
|
-
* @param {?} element The element to stop monitoring.
|
|
1117
1166
|
* @return {?}
|
|
1118
1167
|
*/
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
if (elementInfo) {
|
|
1122
|
-
elementInfo.unlisten();
|
|
1123
|
-
elementInfo.subject.complete();
|
|
1124
|
-
this._setClasses(element);
|
|
1125
|
-
this._elementInfo.delete(element);
|
|
1126
|
-
}
|
|
1168
|
+
FocusTrapDeprecatedDirective.prototype.ngOnDestroy = function () {
|
|
1169
|
+
this.focusTrap.destroy();
|
|
1127
1170
|
};
|
|
1128
1171
|
/**
|
|
1129
|
-
* Focuses the element via the specified focus origin.
|
|
1130
|
-
* @param {?} element The element to focus.
|
|
1131
|
-
* @param {?} origin The focus origin.
|
|
1132
1172
|
* @return {?}
|
|
1133
1173
|
*/
|
|
1134
|
-
|
|
1135
|
-
this.
|
|
1136
|
-
element.focus();
|
|
1174
|
+
FocusTrapDeprecatedDirective.prototype.ngAfterContentInit = function () {
|
|
1175
|
+
this.focusTrap.attachAnchors();
|
|
1137
1176
|
};
|
|
1177
|
+
FocusTrapDeprecatedDirective.decorators = [
|
|
1178
|
+
{ type: _angular_core.Directive, args: [{
|
|
1179
|
+
selector: 'cdk-focus-trap',
|
|
1180
|
+
},] },
|
|
1181
|
+
];
|
|
1138
1182
|
/**
|
|
1139
|
-
*
|
|
1140
|
-
* @return {?}
|
|
1183
|
+
* @nocollapse
|
|
1141
1184
|
*/
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
}
|
|
1148
|
-
// Note: we listen to events in the capture phase so we can detect them even if the user stops
|
|
1149
|
-
// propagation.
|
|
1150
|
-
// On keydown record the origin and clear any touch event that may be in progress.
|
|
1151
|
-
document.addEventListener('keydown', function () {
|
|
1152
|
-
_this._lastTouchTarget = null;
|
|
1153
|
-
_this._setOriginForCurrentEventQueue('keyboard');
|
|
1154
|
-
}, true);
|
|
1155
|
-
// On mousedown record the origin only if there is not touch target, since a mousedown can
|
|
1156
|
-
// happen as a result of a touch event.
|
|
1157
|
-
document.addEventListener('mousedown', function () {
|
|
1158
|
-
if (!_this._lastTouchTarget) {
|
|
1159
|
-
_this._setOriginForCurrentEventQueue('mouse');
|
|
1160
|
-
}
|
|
1161
|
-
}, true);
|
|
1162
|
-
// When the touchstart event fires the focus event is not yet in the event queue. This means
|
|
1163
|
-
// we can't rely on the trick used above (setting timeout of 0ms). Instead we wait 650ms to
|
|
1164
|
-
// see if a focus happens.
|
|
1165
|
-
document.addEventListener('touchstart', function (event) {
|
|
1166
|
-
if (_this._touchTimeout != null) {
|
|
1167
|
-
clearTimeout(_this._touchTimeout);
|
|
1168
|
-
}
|
|
1169
|
-
_this._lastTouchTarget = event.target;
|
|
1170
|
-
_this._touchTimeout = setTimeout(function () { return _this._lastTouchTarget = null; }, TOUCH_BUFFER_MS);
|
|
1171
|
-
}, true);
|
|
1172
|
-
// Make a note of when the window regains focus, so we can restore the origin info for the
|
|
1173
|
-
// focused element.
|
|
1174
|
-
window.addEventListener('focus', function () {
|
|
1175
|
-
_this._windowFocused = true;
|
|
1176
|
-
setTimeout(function () { return _this._windowFocused = false; }, 0);
|
|
1177
|
-
});
|
|
1185
|
+
FocusTrapDeprecatedDirective.ctorParameters = function () { return [
|
|
1186
|
+
{ type: _angular_core.ElementRef, },
|
|
1187
|
+
{ type: FocusTrapFactory, },
|
|
1188
|
+
]; };
|
|
1189
|
+
FocusTrapDeprecatedDirective.propDecorators = {
|
|
1190
|
+
'disabled': [{ type: _angular_core.Input },],
|
|
1178
1191
|
};
|
|
1192
|
+
return FocusTrapDeprecatedDirective;
|
|
1193
|
+
}());
|
|
1194
|
+
/**
|
|
1195
|
+
* Directive for trapping focus within a region.
|
|
1196
|
+
*/
|
|
1197
|
+
var FocusTrapDirective = (function () {
|
|
1198
|
+
/**
|
|
1199
|
+
* @param {?} _elementRef
|
|
1200
|
+
* @param {?} _focusTrapFactory
|
|
1201
|
+
*/
|
|
1202
|
+
function FocusTrapDirective(_elementRef, _focusTrapFactory) {
|
|
1203
|
+
this._elementRef = _elementRef;
|
|
1204
|
+
this._focusTrapFactory = _focusTrapFactory;
|
|
1205
|
+
this.focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement, true);
|
|
1206
|
+
}
|
|
1207
|
+
Object.defineProperty(FocusTrapDirective.prototype, "enabled", {
|
|
1208
|
+
/**
|
|
1209
|
+
* Whether the focus trap is active.
|
|
1210
|
+
* @return {?}
|
|
1211
|
+
*/
|
|
1212
|
+
get: function () { return this.focusTrap.enabled; },
|
|
1213
|
+
/**
|
|
1214
|
+
* @param {?} value
|
|
1215
|
+
* @return {?}
|
|
1216
|
+
*/
|
|
1217
|
+
set: function (value) { this.focusTrap.enabled = _angular_cdk_coercion.coerceBooleanProperty(value); },
|
|
1218
|
+
enumerable: true,
|
|
1219
|
+
configurable: true
|
|
1220
|
+
});
|
|
1179
1221
|
/**
|
|
1180
|
-
* Sets the focus classes on the element based on the given focus origin.
|
|
1181
|
-
* @param {?} element The element to update the classes on.
|
|
1182
|
-
* @param {?=} origin The focus origin.
|
|
1183
1222
|
* @return {?}
|
|
1184
1223
|
*/
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
if (elementInfo) {
|
|
1188
|
-
var /** @type {?} */ toggleClass = function (className, shouldSet) {
|
|
1189
|
-
shouldSet ? elementInfo.renderer.addClass(element, className) :
|
|
1190
|
-
elementInfo.renderer.removeClass(element, className);
|
|
1191
|
-
};
|
|
1192
|
-
toggleClass('cdk-focused', !!origin);
|
|
1193
|
-
toggleClass('cdk-touch-focused', origin === 'touch');
|
|
1194
|
-
toggleClass('cdk-keyboard-focused', origin === 'keyboard');
|
|
1195
|
-
toggleClass('cdk-mouse-focused', origin === 'mouse');
|
|
1196
|
-
toggleClass('cdk-program-focused', origin === 'program');
|
|
1197
|
-
}
|
|
1224
|
+
FocusTrapDirective.prototype.ngOnDestroy = function () {
|
|
1225
|
+
this.focusTrap.destroy();
|
|
1198
1226
|
};
|
|
1199
1227
|
/**
|
|
1200
|
-
* Sets the origin and schedules an async function to clear it at the end of the event queue.
|
|
1201
|
-
* @param {?} origin The origin to set.
|
|
1202
1228
|
* @return {?}
|
|
1203
1229
|
*/
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
this._origin = origin;
|
|
1207
|
-
setTimeout(function () { return _this._origin = null; }, 0);
|
|
1230
|
+
FocusTrapDirective.prototype.ngAfterContentInit = function () {
|
|
1231
|
+
this.focusTrap.attachAnchors();
|
|
1208
1232
|
};
|
|
1233
|
+
FocusTrapDirective.decorators = [
|
|
1234
|
+
{ type: _angular_core.Directive, args: [{
|
|
1235
|
+
selector: '[cdkTrapFocus]',
|
|
1236
|
+
exportAs: 'cdkTrapFocus',
|
|
1237
|
+
},] },
|
|
1238
|
+
];
|
|
1209
1239
|
/**
|
|
1210
|
-
*
|
|
1211
|
-
* @param {?} event The focus event to check.
|
|
1212
|
-
* @return {?} Whether the event was caused by a touch.
|
|
1240
|
+
* @nocollapse
|
|
1213
1241
|
*/
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
// </div>
|
|
1221
|
-
//
|
|
1222
|
-
// If the user touches the #child element and the #parent is programmatically focused as a
|
|
1223
|
-
// result, this code will still consider it to have been caused by the touch event and will
|
|
1224
|
-
// apply the cdk-touch-focused class rather than the cdk-program-focused class. This is a
|
|
1225
|
-
// relatively small edge-case that can be worked around by using
|
|
1226
|
-
// focusVia(parentEl, renderer, 'program') to focus the parent element.
|
|
1227
|
-
//
|
|
1228
|
-
// If we decide that we absolutely must handle this case correctly, we can do so by listening
|
|
1229
|
-
// for the first focus event after the touchstart, and then the first blur event after that
|
|
1230
|
-
// focus event. When that blur event fires we know that whatever follows is not a result of the
|
|
1231
|
-
// touchstart.
|
|
1232
|
-
var /** @type {?} */ focusTarget = event.target;
|
|
1233
|
-
return this._lastTouchTarget instanceof Node && focusTarget instanceof Node &&
|
|
1234
|
-
(focusTarget === this._lastTouchTarget || focusTarget.contains(this._lastTouchTarget));
|
|
1242
|
+
FocusTrapDirective.ctorParameters = function () { return [
|
|
1243
|
+
{ type: _angular_core.ElementRef, },
|
|
1244
|
+
{ type: FocusTrapFactory, },
|
|
1245
|
+
]; };
|
|
1246
|
+
FocusTrapDirective.propDecorators = {
|
|
1247
|
+
'enabled': [{ type: _angular_core.Input, args: ['cdkTrapFocus',] },],
|
|
1235
1248
|
};
|
|
1249
|
+
return FocusTrapDirective;
|
|
1250
|
+
}());
|
|
1251
|
+
|
|
1252
|
+
var LIVE_ANNOUNCER_ELEMENT_TOKEN = new _angular_core.InjectionToken('liveAnnouncerElement');
|
|
1253
|
+
var LiveAnnouncer = (function () {
|
|
1236
1254
|
/**
|
|
1237
|
-
*
|
|
1238
|
-
* @param {?}
|
|
1239
|
-
* @param {?} element The monitored element.
|
|
1240
|
-
* @return {?}
|
|
1255
|
+
* @param {?} elementToken
|
|
1256
|
+
* @param {?} platform
|
|
1241
1257
|
*/
|
|
1242
|
-
|
|
1243
|
-
//
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
var /** @type {?} */ elementInfo = this._elementInfo.get(element);
|
|
1250
|
-
if (!elementInfo || (!elementInfo.checkChildren && element !== event.target)) {
|
|
1251
|
-
return;
|
|
1252
|
-
}
|
|
1253
|
-
// If we couldn't detect a cause for the focus event, it's due to one of three reasons:
|
|
1254
|
-
// 1) The window has just regained focus, in which case we want to restore the focused state of
|
|
1255
|
-
// the element from before the window blurred.
|
|
1256
|
-
// 2) It was caused by a touch event, in which case we mark the origin as 'touch'.
|
|
1257
|
-
// 3) The element was programmatically focused, in which case we should mark the origin as
|
|
1258
|
-
// 'program'.
|
|
1259
|
-
if (!this._origin) {
|
|
1260
|
-
if (this._windowFocused && this._lastFocusOrigin) {
|
|
1261
|
-
this._origin = this._lastFocusOrigin;
|
|
1262
|
-
}
|
|
1263
|
-
else if (this._wasCausedByTouch(event)) {
|
|
1264
|
-
this._origin = 'touch';
|
|
1265
|
-
}
|
|
1266
|
-
else {
|
|
1267
|
-
this._origin = 'program';
|
|
1268
|
-
}
|
|
1258
|
+
function LiveAnnouncer(elementToken, platform) {
|
|
1259
|
+
// Only do anything if we're on the browser platform.
|
|
1260
|
+
if (platform.isBrowser) {
|
|
1261
|
+
// We inject the live element as `any` because the constructor signature cannot reference
|
|
1262
|
+
// browser globals (HTMLElement) on non-browser environments, since having a class decorator
|
|
1263
|
+
// causes TypeScript to preserve the constructor signature types.
|
|
1264
|
+
this._liveElement = elementToken || this._createLiveElement();
|
|
1269
1265
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Announces a message to screenreaders.
|
|
1269
|
+
* @param {?} message Message to be announced to the screenreader
|
|
1270
|
+
* @param {?=} politeness The politeness of the announcer element
|
|
1271
|
+
* @return {?}
|
|
1272
|
+
*/
|
|
1273
|
+
LiveAnnouncer.prototype.announce = function (message, politeness) {
|
|
1274
|
+
var _this = this;
|
|
1275
|
+
if (politeness === void 0) { politeness = 'polite'; }
|
|
1276
|
+
this._liveElement.textContent = '';
|
|
1277
|
+
// TODO: ensure changing the politeness works on all environments we support.
|
|
1278
|
+
this._liveElement.setAttribute('aria-live', politeness);
|
|
1279
|
+
// This 100ms timeout is necessary for some browser + screen-reader combinations:
|
|
1280
|
+
// - Both JAWS and NVDA over IE11 will not announce anything without a non-zero timeout.
|
|
1281
|
+
// - With Chrome and IE11 with NVDA or JAWS, a repeated (identical) message won't be read a
|
|
1282
|
+
// second time without clearing and then using a non-zero delay.
|
|
1283
|
+
// (using JAWS 17 at time of this writing).
|
|
1284
|
+
setTimeout(function () { return _this._liveElement.textContent = message; }, 100);
|
|
1274
1285
|
};
|
|
1275
1286
|
/**
|
|
1276
|
-
* Handles blur events on a registered element.
|
|
1277
|
-
* @param {?} event The blur event.
|
|
1278
|
-
* @param {?} element The monitored element.
|
|
1279
1287
|
* @return {?}
|
|
1280
1288
|
*/
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
var /** @type {?} */ elementInfo = this._elementInfo.get(element);
|
|
1285
|
-
if (!elementInfo || (elementInfo.checkChildren && event.relatedTarget instanceof Node &&
|
|
1286
|
-
element.contains(event.relatedTarget))) {
|
|
1287
|
-
return;
|
|
1289
|
+
LiveAnnouncer.prototype.ngOnDestroy = function () {
|
|
1290
|
+
if (this._liveElement && this._liveElement.parentNode) {
|
|
1291
|
+
this._liveElement.parentNode.removeChild(this._liveElement);
|
|
1288
1292
|
}
|
|
1289
|
-
this._setClasses(element);
|
|
1290
|
-
elementInfo.subject.next(null);
|
|
1291
1293
|
};
|
|
1292
|
-
return FocusMonitor;
|
|
1293
|
-
}());
|
|
1294
|
-
FocusMonitor.decorators = [
|
|
1295
|
-
{ type: _angular_core.Injectable },
|
|
1296
|
-
];
|
|
1297
|
-
/**
|
|
1298
|
-
* @nocollapse
|
|
1299
|
-
*/
|
|
1300
|
-
FocusMonitor.ctorParameters = function () { return [
|
|
1301
|
-
{ type: _angular_core.NgZone, },
|
|
1302
|
-
{ type: _angular_cdk_platform.Platform, },
|
|
1303
|
-
]; };
|
|
1304
|
-
/**
|
|
1305
|
-
* Directive that determines how a particular element was focused (via keyboard, mouse, touch, or
|
|
1306
|
-
* programmatically) and adds corresponding classes to the element.
|
|
1307
|
-
*
|
|
1308
|
-
* There are two variants of this directive:
|
|
1309
|
-
* 1) cdkMonitorElementFocus: does not consider an element to be focused if one of its children is
|
|
1310
|
-
* focused.
|
|
1311
|
-
* 2) cdkMonitorSubtreeFocus: considers an element focused if it or any of its children are focused.
|
|
1312
|
-
*/
|
|
1313
|
-
var CdkMonitorFocus = (function () {
|
|
1314
1294
|
/**
|
|
1315
|
-
* @
|
|
1316
|
-
* @param {?} _focusMonitor
|
|
1317
|
-
* @param {?} renderer
|
|
1295
|
+
* @return {?}
|
|
1318
1296
|
*/
|
|
1319
|
-
function
|
|
1320
|
-
var
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
}
|
|
1297
|
+
LiveAnnouncer.prototype._createLiveElement = function () {
|
|
1298
|
+
var /** @type {?} */ liveEl = document.createElement('div');
|
|
1299
|
+
liveEl.classList.add('cdk-visually-hidden');
|
|
1300
|
+
liveEl.setAttribute('aria-atomic', 'true');
|
|
1301
|
+
liveEl.setAttribute('aria-live', 'polite');
|
|
1302
|
+
document.body.appendChild(liveEl);
|
|
1303
|
+
return liveEl;
|
|
1304
|
+
};
|
|
1305
|
+
LiveAnnouncer.decorators = [
|
|
1306
|
+
{ type: _angular_core.Injectable },
|
|
1307
|
+
];
|
|
1327
1308
|
/**
|
|
1328
|
-
* @
|
|
1309
|
+
* @nocollapse
|
|
1329
1310
|
*/
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
};
|
|
1334
|
-
return
|
|
1311
|
+
LiveAnnouncer.ctorParameters = function () { return [
|
|
1312
|
+
{ type: undefined, decorators: [{ type: _angular_core.Optional }, { type: _angular_core.Inject, args: [LIVE_ANNOUNCER_ELEMENT_TOKEN,] },] },
|
|
1313
|
+
{ type: _angular_cdk_platform.Platform, },
|
|
1314
|
+
]; };
|
|
1315
|
+
return LiveAnnouncer;
|
|
1335
1316
|
}());
|
|
1336
|
-
CdkMonitorFocus.decorators = [
|
|
1337
|
-
{ type: _angular_core.Directive, args: [{
|
|
1338
|
-
selector: '[cdkMonitorElementFocus], [cdkMonitorSubtreeFocus]',
|
|
1339
|
-
},] },
|
|
1340
|
-
];
|
|
1341
|
-
/**
|
|
1342
|
-
* @nocollapse
|
|
1343
|
-
*/
|
|
1344
|
-
CdkMonitorFocus.ctorParameters = function () { return [
|
|
1345
|
-
{ type: _angular_core.ElementRef, },
|
|
1346
|
-
{ type: FocusMonitor, },
|
|
1347
|
-
{ type: _angular_core.Renderer2, },
|
|
1348
|
-
]; };
|
|
1349
|
-
CdkMonitorFocus.propDecorators = {
|
|
1350
|
-
'cdkFocusChange': [{ type: _angular_core.Output },],
|
|
1351
|
-
};
|
|
1352
1317
|
/**
|
|
1353
1318
|
* \@docs-private
|
|
1354
1319
|
* @param {?} parentDispatcher
|
|
1355
|
-
* @param {?}
|
|
1320
|
+
* @param {?} liveElement
|
|
1356
1321
|
* @param {?} platform
|
|
1357
1322
|
* @return {?}
|
|
1358
1323
|
*/
|
|
1359
|
-
function
|
|
1360
|
-
return parentDispatcher || new
|
|
1324
|
+
function LIVE_ANNOUNCER_PROVIDER_FACTORY(parentDispatcher, liveElement, platform) {
|
|
1325
|
+
return parentDispatcher || new LiveAnnouncer(liveElement, platform);
|
|
1361
1326
|
}
|
|
1362
1327
|
/**
|
|
1363
1328
|
* \@docs-private
|
|
1364
1329
|
*/
|
|
1365
|
-
var
|
|
1366
|
-
// If there is already a
|
|
1367
|
-
provide:
|
|
1368
|
-
deps: [
|
|
1369
|
-
|
|
1330
|
+
var LIVE_ANNOUNCER_PROVIDER = {
|
|
1331
|
+
// If there is already a LiveAnnouncer available, use that. Otherwise, provide a new one.
|
|
1332
|
+
provide: LiveAnnouncer,
|
|
1333
|
+
deps: [
|
|
1334
|
+
[new _angular_core.Optional(), new _angular_core.SkipSelf(), LiveAnnouncer],
|
|
1335
|
+
[new _angular_core.Optional(), new _angular_core.Inject(LIVE_ANNOUNCER_ELEMENT_TOKEN)],
|
|
1336
|
+
_angular_cdk_platform.Platform,
|
|
1337
|
+
],
|
|
1338
|
+
useFactory: LIVE_ANNOUNCER_PROVIDER_FACTORY
|
|
1370
1339
|
};
|
|
1340
|
+
|
|
1341
|
+
// This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found
|
|
1342
|
+
// that a value of around 650ms seems appropriate.
|
|
1343
|
+
var TOUCH_BUFFER_MS = 650;
|
|
1371
1344
|
/**
|
|
1372
|
-
*
|
|
1373
|
-
* of items, it will set the active item correctly when arrow events occur.
|
|
1345
|
+
* Monitors mouse and keyboard events to determine the cause of focus events.
|
|
1374
1346
|
*/
|
|
1375
|
-
var
|
|
1347
|
+
var FocusMonitor = (function () {
|
|
1376
1348
|
/**
|
|
1377
|
-
* @param {?}
|
|
1349
|
+
* @param {?} _ngZone
|
|
1350
|
+
* @param {?} _platform
|
|
1378
1351
|
*/
|
|
1379
|
-
function
|
|
1380
|
-
|
|
1381
|
-
this.
|
|
1382
|
-
this.
|
|
1383
|
-
this._letterKeyStream = new rxjs_Subject.Subject();
|
|
1384
|
-
this._typeaheadSubscription = rxjs_Subscription.Subscription.EMPTY;
|
|
1385
|
-
this._pressedLetters = [];
|
|
1352
|
+
function FocusMonitor(_ngZone, _platform) {
|
|
1353
|
+
var _this = this;
|
|
1354
|
+
this._ngZone = _ngZone;
|
|
1355
|
+
this._platform = _platform;
|
|
1386
1356
|
/**
|
|
1387
|
-
*
|
|
1388
|
-
* when focus is shifted off of the list.
|
|
1357
|
+
* The focus origin that the next focus event is a result of.
|
|
1389
1358
|
*/
|
|
1390
|
-
this.
|
|
1359
|
+
this._origin = null;
|
|
1360
|
+
/**
|
|
1361
|
+
* Whether the window has just been focused.
|
|
1362
|
+
*/
|
|
1363
|
+
this._windowFocused = false;
|
|
1364
|
+
/**
|
|
1365
|
+
* Weak map of elements being monitored to their info.
|
|
1366
|
+
*/
|
|
1367
|
+
this._elementInfo = new WeakMap();
|
|
1368
|
+
this._ngZone.runOutsideAngular(function () { return _this._registerDocumentEvents(); });
|
|
1391
1369
|
}
|
|
1392
1370
|
/**
|
|
1393
|
-
*
|
|
1394
|
-
*
|
|
1395
|
-
* @
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
return this;
|
|
1400
|
-
};
|
|
1401
|
-
/**
|
|
1402
|
-
* Turns on typeahead mode which allows users to set the active item by typing.
|
|
1403
|
-
* @param {?=} debounceInterval Time to wait after the last keystroke before setting the active item.
|
|
1404
|
-
* @return {?}
|
|
1371
|
+
* Monitors focus on an element and applies appropriate CSS classes.
|
|
1372
|
+
* @param {?} element The element to monitor
|
|
1373
|
+
* @param {?} renderer The renderer to use to apply CSS classes to the element.
|
|
1374
|
+
* @param {?} checkChildren Whether to count the element as focused when its children are focused.
|
|
1375
|
+
* @return {?} An observable that emits when the focus state of the element changes.
|
|
1376
|
+
* When the element is blurred, null will be emitted.
|
|
1405
1377
|
*/
|
|
1406
|
-
|
|
1378
|
+
FocusMonitor.prototype.monitor = function (element, renderer, checkChildren) {
|
|
1407
1379
|
var _this = this;
|
|
1408
|
-
|
|
1409
|
-
if (this.
|
|
1410
|
-
|
|
1380
|
+
// Do nothing if we're not on the browser platform.
|
|
1381
|
+
if (!this._platform.isBrowser) {
|
|
1382
|
+
return rxjs_observable_of.of(null);
|
|
1411
1383
|
}
|
|
1412
|
-
this.
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1384
|
+
// Check if we're already monitoring this element.
|
|
1385
|
+
if (this._elementInfo.has(element)) {
|
|
1386
|
+
var /** @type {?} */ cachedInfo = this._elementInfo.get(element); /** @type {?} */
|
|
1387
|
+
((cachedInfo)).checkChildren = checkChildren;
|
|
1388
|
+
return ((cachedInfo)).subject.asObservable();
|
|
1389
|
+
}
|
|
1390
|
+
// Create monitored element info.
|
|
1391
|
+
var /** @type {?} */ info = {
|
|
1392
|
+
unlisten: function () { },
|
|
1393
|
+
checkChildren: checkChildren,
|
|
1394
|
+
renderer: renderer,
|
|
1395
|
+
subject: new rxjs_Subject.Subject()
|
|
1396
|
+
};
|
|
1397
|
+
this._elementInfo.set(element, info);
|
|
1398
|
+
// Start listening. We need to listen in capture phase since focus events don't bubble.
|
|
1399
|
+
var /** @type {?} */ focusListener = function (event) { return _this._onFocus(event, element); };
|
|
1400
|
+
var /** @type {?} */ blurListener = function (event) { return _this._onBlur(event, element); };
|
|
1401
|
+
this._ngZone.runOutsideAngular(function () {
|
|
1402
|
+
element.addEventListener('focus', focusListener, true);
|
|
1403
|
+
element.addEventListener('blur', blurListener, true);
|
|
1430
1404
|
});
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
*/
|
|
1438
|
-
ListKeyManager.prototype.setActiveItem = function (index) {
|
|
1439
|
-
this._activeItemIndex = index;
|
|
1440
|
-
this._activeItem = this._items.toArray()[index];
|
|
1405
|
+
// Create an unlisten function for later.
|
|
1406
|
+
info.unlisten = function () {
|
|
1407
|
+
element.removeEventListener('focus', focusListener, true);
|
|
1408
|
+
element.removeEventListener('blur', blurListener, true);
|
|
1409
|
+
};
|
|
1410
|
+
return info.subject.asObservable();
|
|
1441
1411
|
};
|
|
1442
1412
|
/**
|
|
1443
|
-
*
|
|
1444
|
-
* @param {?}
|
|
1413
|
+
* Stops monitoring an element and removes all focus classes.
|
|
1414
|
+
* @param {?} element The element to stop monitoring.
|
|
1445
1415
|
* @return {?}
|
|
1446
1416
|
*/
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
break;
|
|
1455
|
-
case _angular_cdk_keycodes.TAB:
|
|
1456
|
-
this.tabOut.next();
|
|
1457
|
-
return;
|
|
1458
|
-
default:
|
|
1459
|
-
var /** @type {?} */ keyCode = event.keyCode;
|
|
1460
|
-
// Attempt to use the `event.key` which also maps it to the user's keyboard language,
|
|
1461
|
-
// otherwise fall back to resolving alphanumeric characters via the keyCode.
|
|
1462
|
-
if (event.key && event.key.length === 1) {
|
|
1463
|
-
this._letterKeyStream.next(event.key.toLocaleUpperCase());
|
|
1464
|
-
}
|
|
1465
|
-
else if ((keyCode >= _angular_cdk_keycodes.A && keyCode <= _angular_cdk_keycodes.Z) || (keyCode >= _angular_cdk_keycodes.ZERO && keyCode <= _angular_cdk_keycodes.NINE)) {
|
|
1466
|
-
this._letterKeyStream.next(String.fromCharCode(keyCode));
|
|
1467
|
-
}
|
|
1468
|
-
// Note that we return here, in order to avoid preventing
|
|
1469
|
-
// the default action of non-navigational keys.
|
|
1470
|
-
return;
|
|
1417
|
+
FocusMonitor.prototype.stopMonitoring = function (element) {
|
|
1418
|
+
var /** @type {?} */ elementInfo = this._elementInfo.get(element);
|
|
1419
|
+
if (elementInfo) {
|
|
1420
|
+
elementInfo.unlisten();
|
|
1421
|
+
elementInfo.subject.complete();
|
|
1422
|
+
this._setClasses(element);
|
|
1423
|
+
this._elementInfo.delete(element);
|
|
1471
1424
|
}
|
|
1472
|
-
this._pressedLetters = [];
|
|
1473
|
-
event.preventDefault();
|
|
1474
|
-
};
|
|
1475
|
-
Object.defineProperty(ListKeyManager.prototype, "activeItemIndex", {
|
|
1476
|
-
/**
|
|
1477
|
-
* Index of the currently active item.
|
|
1478
|
-
* @return {?}
|
|
1479
|
-
*/
|
|
1480
|
-
get: function () {
|
|
1481
|
-
return this._activeItemIndex;
|
|
1482
|
-
},
|
|
1483
|
-
enumerable: true,
|
|
1484
|
-
configurable: true
|
|
1485
|
-
});
|
|
1486
|
-
Object.defineProperty(ListKeyManager.prototype, "activeItem", {
|
|
1487
|
-
/**
|
|
1488
|
-
* The active item.
|
|
1489
|
-
* @return {?}
|
|
1490
|
-
*/
|
|
1491
|
-
get: function () {
|
|
1492
|
-
return this._activeItem;
|
|
1493
|
-
},
|
|
1494
|
-
enumerable: true,
|
|
1495
|
-
configurable: true
|
|
1496
|
-
});
|
|
1497
|
-
/**
|
|
1498
|
-
* Sets the active item to the first enabled item in the list.
|
|
1499
|
-
* @return {?}
|
|
1500
|
-
*/
|
|
1501
|
-
ListKeyManager.prototype.setFirstItemActive = function () {
|
|
1502
|
-
this._setActiveItemByIndex(0, 1);
|
|
1503
1425
|
};
|
|
1504
1426
|
/**
|
|
1505
|
-
*
|
|
1427
|
+
* Focuses the element via the specified focus origin.
|
|
1428
|
+
* @param {?} element The element to focus.
|
|
1429
|
+
* @param {?} origin The focus origin.
|
|
1506
1430
|
* @return {?}
|
|
1507
1431
|
*/
|
|
1508
|
-
|
|
1509
|
-
this.
|
|
1432
|
+
FocusMonitor.prototype.focusVia = function (element, origin) {
|
|
1433
|
+
this._setOriginForCurrentEventQueue(origin);
|
|
1434
|
+
element.focus();
|
|
1510
1435
|
};
|
|
1511
1436
|
/**
|
|
1512
|
-
*
|
|
1437
|
+
* Register necessary event listeners on the document and window.
|
|
1513
1438
|
* @return {?}
|
|
1514
1439
|
*/
|
|
1515
|
-
|
|
1516
|
-
|
|
1440
|
+
FocusMonitor.prototype._registerDocumentEvents = function () {
|
|
1441
|
+
var _this = this;
|
|
1442
|
+
// Do nothing if we're not on the browser platform.
|
|
1443
|
+
if (!this._platform.isBrowser) {
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
// Note: we listen to events in the capture phase so we can detect them even if the user stops
|
|
1447
|
+
// propagation.
|
|
1448
|
+
// On keydown record the origin and clear any touch event that may be in progress.
|
|
1449
|
+
document.addEventListener('keydown', function () {
|
|
1450
|
+
_this._lastTouchTarget = null;
|
|
1451
|
+
_this._setOriginForCurrentEventQueue('keyboard');
|
|
1452
|
+
}, true);
|
|
1453
|
+
// On mousedown record the origin only if there is not touch target, since a mousedown can
|
|
1454
|
+
// happen as a result of a touch event.
|
|
1455
|
+
document.addEventListener('mousedown', function () {
|
|
1456
|
+
if (!_this._lastTouchTarget) {
|
|
1457
|
+
_this._setOriginForCurrentEventQueue('mouse');
|
|
1458
|
+
}
|
|
1459
|
+
}, true);
|
|
1460
|
+
// When the touchstart event fires the focus event is not yet in the event queue. This means
|
|
1461
|
+
// we can't rely on the trick used above (setting timeout of 0ms). Instead we wait 650ms to
|
|
1462
|
+
// see if a focus happens.
|
|
1463
|
+
document.addEventListener('touchstart', function (event) {
|
|
1464
|
+
if (_this._touchTimeout != null) {
|
|
1465
|
+
clearTimeout(_this._touchTimeout);
|
|
1466
|
+
}
|
|
1467
|
+
_this._lastTouchTarget = event.target;
|
|
1468
|
+
_this._touchTimeout = setTimeout(function () { return _this._lastTouchTarget = null; }, TOUCH_BUFFER_MS);
|
|
1469
|
+
}, true);
|
|
1470
|
+
// Make a note of when the window regains focus, so we can restore the origin info for the
|
|
1471
|
+
// focused element.
|
|
1472
|
+
window.addEventListener('focus', function () {
|
|
1473
|
+
_this._windowFocused = true;
|
|
1474
|
+
setTimeout(function () { return _this._windowFocused = false; }, 0);
|
|
1475
|
+
});
|
|
1517
1476
|
};
|
|
1518
1477
|
/**
|
|
1519
|
-
* Sets the
|
|
1478
|
+
* Sets the focus classes on the element based on the given focus origin.
|
|
1479
|
+
* @param {?} element The element to update the classes on.
|
|
1480
|
+
* @param {?=} origin The focus origin.
|
|
1520
1481
|
* @return {?}
|
|
1521
1482
|
*/
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1483
|
+
FocusMonitor.prototype._setClasses = function (element, origin) {
|
|
1484
|
+
var /** @type {?} */ elementInfo = this._elementInfo.get(element);
|
|
1485
|
+
if (elementInfo) {
|
|
1486
|
+
var /** @type {?} */ toggleClass = function (className, shouldSet) {
|
|
1487
|
+
shouldSet ? elementInfo.renderer.addClass(element, className) :
|
|
1488
|
+
elementInfo.renderer.removeClass(element, className);
|
|
1489
|
+
};
|
|
1490
|
+
toggleClass('cdk-focused', !!origin);
|
|
1491
|
+
toggleClass('cdk-touch-focused', origin === 'touch');
|
|
1492
|
+
toggleClass('cdk-keyboard-focused', origin === 'keyboard');
|
|
1493
|
+
toggleClass('cdk-mouse-focused', origin === 'mouse');
|
|
1494
|
+
toggleClass('cdk-program-focused', origin === 'program');
|
|
1495
|
+
}
|
|
1525
1496
|
};
|
|
1526
1497
|
/**
|
|
1527
|
-
*
|
|
1528
|
-
* @param {?}
|
|
1498
|
+
* Sets the origin and schedules an async function to clear it at the end of the event queue.
|
|
1499
|
+
* @param {?} origin The origin to set.
|
|
1529
1500
|
* @return {?}
|
|
1530
1501
|
*/
|
|
1531
|
-
|
|
1532
|
-
|
|
1502
|
+
FocusMonitor.prototype._setOriginForCurrentEventQueue = function (origin) {
|
|
1503
|
+
var _this = this;
|
|
1504
|
+
this._origin = origin;
|
|
1505
|
+
setTimeout(function () { return _this._origin = null; }, 0);
|
|
1533
1506
|
};
|
|
1534
1507
|
/**
|
|
1535
|
-
*
|
|
1536
|
-
*
|
|
1537
|
-
*
|
|
1538
|
-
* @param {?} delta
|
|
1539
|
-
* @param {?=} items
|
|
1540
|
-
* @return {?}
|
|
1508
|
+
* Checks whether the given focus event was caused by a touchstart event.
|
|
1509
|
+
* @param {?} event The focus event to check.
|
|
1510
|
+
* @return {?} Whether the event was caused by a touch.
|
|
1541
1511
|
*/
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1512
|
+
FocusMonitor.prototype._wasCausedByTouch = function (event) {
|
|
1513
|
+
// Note(mmalerba): This implementation is not quite perfect, there is a small edge case.
|
|
1514
|
+
// Consider the following dom structure:
|
|
1515
|
+
//
|
|
1516
|
+
// <div #parent tabindex="0" cdkFocusClasses>
|
|
1517
|
+
// <div #child (click)="#parent.focus()"></div>
|
|
1518
|
+
// </div>
|
|
1519
|
+
//
|
|
1520
|
+
// If the user touches the #child element and the #parent is programmatically focused as a
|
|
1521
|
+
// result, this code will still consider it to have been caused by the touch event and will
|
|
1522
|
+
// apply the cdk-touch-focused class rather than the cdk-program-focused class. This is a
|
|
1523
|
+
// relatively small edge-case that can be worked around by using
|
|
1524
|
+
// focusVia(parentEl, renderer, 'program') to focus the parent element.
|
|
1525
|
+
//
|
|
1526
|
+
// If we decide that we absolutely must handle this case correctly, we can do so by listening
|
|
1527
|
+
// for the first focus event after the touchstart, and then the first blur event after that
|
|
1528
|
+
// focus event. When that blur event fires we know that whatever follows is not a result of the
|
|
1529
|
+
// touchstart.
|
|
1530
|
+
var /** @type {?} */ focusTarget = event.target;
|
|
1531
|
+
return this._lastTouchTarget instanceof Node && focusTarget instanceof Node &&
|
|
1532
|
+
(focusTarget === this._lastTouchTarget || focusTarget.contains(this._lastTouchTarget));
|
|
1546
1533
|
};
|
|
1547
1534
|
/**
|
|
1548
|
-
*
|
|
1549
|
-
*
|
|
1550
|
-
*
|
|
1551
|
-
* @param {?} delta
|
|
1552
|
-
* @param {?} items
|
|
1535
|
+
* Handles focus events on a registered element.
|
|
1536
|
+
* @param {?} event The focus event.
|
|
1537
|
+
* @param {?} element The monitored element.
|
|
1553
1538
|
* @return {?}
|
|
1554
1539
|
*/
|
|
1555
|
-
|
|
1556
|
-
//
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
//
|
|
1560
|
-
|
|
1561
|
-
|
|
1540
|
+
FocusMonitor.prototype._onFocus = function (event, element) {
|
|
1541
|
+
// NOTE(mmalerba): We currently set the classes based on the focus origin of the most recent
|
|
1542
|
+
// focus event affecting the monitored element. If we want to use the origin of the first event
|
|
1543
|
+
// instead we should check for the cdk-focused class here and return if the element already has
|
|
1544
|
+
// it. (This only matters for elements that have includesChildren = true).
|
|
1545
|
+
// If we are not counting child-element-focus as focused, make sure that the event target is the
|
|
1546
|
+
// monitored element itself.
|
|
1547
|
+
var /** @type {?} */ elementInfo = this._elementInfo.get(element);
|
|
1548
|
+
if (!elementInfo || (!elementInfo.checkChildren && element !== event.target)) {
|
|
1549
|
+
return;
|
|
1562
1550
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1551
|
+
// If we couldn't detect a cause for the focus event, it's due to one of three reasons:
|
|
1552
|
+
// 1) The window has just regained focus, in which case we want to restore the focused state of
|
|
1553
|
+
// the element from before the window blurred.
|
|
1554
|
+
// 2) It was caused by a touch event, in which case we mark the origin as 'touch'.
|
|
1555
|
+
// 3) The element was programmatically focused, in which case we should mark the origin as
|
|
1556
|
+
// 'program'.
|
|
1557
|
+
if (!this._origin) {
|
|
1558
|
+
if (this._windowFocused && this._lastFocusOrigin) {
|
|
1559
|
+
this._origin = this._lastFocusOrigin;
|
|
1560
|
+
}
|
|
1561
|
+
else if (this._wasCausedByTouch(event)) {
|
|
1562
|
+
this._origin = 'touch';
|
|
1563
|
+
}
|
|
1564
|
+
else {
|
|
1565
|
+
this._origin = 'program';
|
|
1566
|
+
}
|
|
1565
1567
|
}
|
|
1568
|
+
this._setClasses(element, this._origin);
|
|
1569
|
+
elementInfo.subject.next(this._origin);
|
|
1570
|
+
this._lastFocusOrigin = this._origin;
|
|
1571
|
+
this._origin = null;
|
|
1566
1572
|
};
|
|
1567
1573
|
/**
|
|
1568
|
-
*
|
|
1569
|
-
*
|
|
1570
|
-
*
|
|
1571
|
-
* @param {?} delta
|
|
1572
|
-
* @param {?} items
|
|
1573
|
-
* @return {?}
|
|
1574
|
-
*/
|
|
1575
|
-
ListKeyManager.prototype._setActiveInDefaultMode = function (delta, items) {
|
|
1576
|
-
this._setActiveItemByIndex(this._activeItemIndex + delta, delta, items);
|
|
1577
|
-
};
|
|
1578
|
-
/**
|
|
1579
|
-
* Sets the active item to the first enabled item starting at the index specified. If the
|
|
1580
|
-
* item is disabled, it will move in the fallbackDelta direction until it either
|
|
1581
|
-
* finds an enabled item or encounters the end of the list.
|
|
1582
|
-
* @param {?} index
|
|
1583
|
-
* @param {?} fallbackDelta
|
|
1584
|
-
* @param {?=} items
|
|
1574
|
+
* Handles blur events on a registered element.
|
|
1575
|
+
* @param {?} event The blur event.
|
|
1576
|
+
* @param {?} element The monitored element.
|
|
1585
1577
|
* @return {?}
|
|
1586
1578
|
*/
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1579
|
+
FocusMonitor.prototype._onBlur = function (event, element) {
|
|
1580
|
+
// If we are counting child-element-focus as focused, make sure that we aren't just blurring in
|
|
1581
|
+
// order to focus another child of the monitored element.
|
|
1582
|
+
var /** @type {?} */ elementInfo = this._elementInfo.get(element);
|
|
1583
|
+
if (!elementInfo || (elementInfo.checkChildren && event.relatedTarget instanceof Node &&
|
|
1584
|
+
element.contains(event.relatedTarget))) {
|
|
1590
1585
|
return;
|
|
1591
1586
|
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
if (!items[index]) {
|
|
1595
|
-
return;
|
|
1596
|
-
}
|
|
1597
|
-
}
|
|
1598
|
-
this.setActiveItem(index);
|
|
1587
|
+
this._setClasses(element);
|
|
1588
|
+
elementInfo.subject.next(null);
|
|
1599
1589
|
};
|
|
1600
|
-
|
|
1590
|
+
FocusMonitor.decorators = [
|
|
1591
|
+
{ type: _angular_core.Injectable },
|
|
1592
|
+
];
|
|
1593
|
+
/**
|
|
1594
|
+
* @nocollapse
|
|
1595
|
+
*/
|
|
1596
|
+
FocusMonitor.ctorParameters = function () { return [
|
|
1597
|
+
{ type: _angular_core.NgZone, },
|
|
1598
|
+
{ type: _angular_cdk_platform.Platform, },
|
|
1599
|
+
]; };
|
|
1600
|
+
return FocusMonitor;
|
|
1601
1601
|
}());
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1602
|
+
/**
|
|
1603
|
+
* Directive that determines how a particular element was focused (via keyboard, mouse, touch, or
|
|
1604
|
+
* programmatically) and adds corresponding classes to the element.
|
|
1605
|
+
*
|
|
1606
|
+
* There are two variants of this directive:
|
|
1607
|
+
* 1) cdkMonitorElementFocus: does not consider an element to be focused if one of its children is
|
|
1608
|
+
* focused.
|
|
1609
|
+
* 2) cdkMonitorSubtreeFocus: considers an element focused if it or any of its children are focused.
|
|
1610
|
+
*/
|
|
1611
|
+
var CdkMonitorFocus = (function () {
|
|
1612
|
+
/**
|
|
1613
|
+
* @param {?} _elementRef
|
|
1614
|
+
* @param {?} _focusMonitor
|
|
1615
|
+
* @param {?} renderer
|
|
1616
|
+
*/
|
|
1617
|
+
function CdkMonitorFocus(_elementRef, _focusMonitor, renderer) {
|
|
1618
|
+
var _this = this;
|
|
1619
|
+
this._elementRef = _elementRef;
|
|
1620
|
+
this._focusMonitor = _focusMonitor;
|
|
1621
|
+
this.cdkFocusChange = new _angular_core.EventEmitter();
|
|
1622
|
+
this._monitorSubscription = this._focusMonitor.monitor(this._elementRef.nativeElement, renderer, this._elementRef.nativeElement.hasAttribute('cdkMonitorSubtreeFocus'))
|
|
1623
|
+
.subscribe(function (origin) { return _this.cdkFocusChange.emit(origin); });
|
|
1606
1624
|
}
|
|
1607
1625
|
/**
|
|
1608
|
-
* This method sets the active item to the item at the specified index.
|
|
1609
|
-
* It also adds active styles to the newly active item and removes active
|
|
1610
|
-
* styles from the previously active item.
|
|
1611
|
-
* @param {?} index
|
|
1612
1626
|
* @return {?}
|
|
1613
1627
|
*/
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
if (_this.activeItem) {
|
|
1618
|
-
_this.activeItem.setInactiveStyles();
|
|
1619
|
-
}
|
|
1620
|
-
_super.prototype.setActiveItem.call(_this, index);
|
|
1621
|
-
if (_this.activeItem) {
|
|
1622
|
-
_this.activeItem.setActiveStyles();
|
|
1623
|
-
}
|
|
1624
|
-
});
|
|
1628
|
+
CdkMonitorFocus.prototype.ngOnDestroy = function () {
|
|
1629
|
+
this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);
|
|
1630
|
+
this._monitorSubscription.unsubscribe();
|
|
1625
1631
|
};
|
|
1626
|
-
|
|
1627
|
-
|
|
1632
|
+
CdkMonitorFocus.decorators = [
|
|
1633
|
+
{ type: _angular_core.Directive, args: [{
|
|
1634
|
+
selector: '[cdkMonitorElementFocus], [cdkMonitorSubtreeFocus]',
|
|
1635
|
+
},] },
|
|
1636
|
+
];
|
|
1637
|
+
/**
|
|
1638
|
+
* @nocollapse
|
|
1639
|
+
*/
|
|
1640
|
+
CdkMonitorFocus.ctorParameters = function () { return [
|
|
1641
|
+
{ type: _angular_core.ElementRef, },
|
|
1642
|
+
{ type: FocusMonitor, },
|
|
1643
|
+
{ type: _angular_core.Renderer2, },
|
|
1644
|
+
]; };
|
|
1645
|
+
CdkMonitorFocus.propDecorators = {
|
|
1646
|
+
'cdkFocusChange': [{ type: _angular_core.Output },],
|
|
1647
|
+
};
|
|
1648
|
+
return CdkMonitorFocus;
|
|
1649
|
+
}());
|
|
1628
1650
|
/**
|
|
1629
|
-
*
|
|
1630
|
-
*
|
|
1631
|
-
*
|
|
1632
|
-
*
|
|
1633
|
-
* the left mouse button), faked mousedowns will usually set the property value to 0.
|
|
1634
|
-
* @param {?} event
|
|
1651
|
+
* \@docs-private
|
|
1652
|
+
* @param {?} parentDispatcher
|
|
1653
|
+
* @param {?} ngZone
|
|
1654
|
+
* @param {?} platform
|
|
1635
1655
|
* @return {?}
|
|
1636
1656
|
*/
|
|
1637
|
-
function
|
|
1638
|
-
return
|
|
1657
|
+
function FOCUS_MONITOR_PROVIDER_FACTORY(parentDispatcher, ngZone, platform) {
|
|
1658
|
+
return parentDispatcher || new FocusMonitor(ngZone, platform);
|
|
1639
1659
|
}
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
*/
|
|
1651
|
-
FocusKeyManager.prototype.setActiveItem = function (index) {
|
|
1652
|
-
_super.prototype.setActiveItem.call(this, index);
|
|
1653
|
-
if (this.activeItem) {
|
|
1654
|
-
this.activeItem.focus();
|
|
1655
|
-
}
|
|
1656
|
-
};
|
|
1657
|
-
return FocusKeyManager;
|
|
1658
|
-
}(ListKeyManager));
|
|
1660
|
+
/**
|
|
1661
|
+
* \@docs-private
|
|
1662
|
+
*/
|
|
1663
|
+
var FOCUS_MONITOR_PROVIDER = {
|
|
1664
|
+
// If there is already a FocusMonitor available, use that. Otherwise, provide a new one.
|
|
1665
|
+
provide: FocusMonitor,
|
|
1666
|
+
deps: [[new _angular_core.Optional(), new _angular_core.SkipSelf(), FocusMonitor], _angular_core.NgZone, _angular_cdk_platform.Platform],
|
|
1667
|
+
useFactory: FOCUS_MONITOR_PROVIDER_FACTORY
|
|
1668
|
+
};
|
|
1669
|
+
|
|
1659
1670
|
var A11yModule = (function () {
|
|
1660
1671
|
function A11yModule() {
|
|
1661
1672
|
}
|
|
1673
|
+
A11yModule.decorators = [
|
|
1674
|
+
{ type: _angular_core.NgModule, args: [{
|
|
1675
|
+
imports: [_angular_common.CommonModule, _angular_cdk_platform.PlatformModule],
|
|
1676
|
+
declarations: [FocusTrapDirective, FocusTrapDeprecatedDirective, CdkMonitorFocus],
|
|
1677
|
+
exports: [FocusTrapDirective, FocusTrapDeprecatedDirective, CdkMonitorFocus],
|
|
1678
|
+
providers: [
|
|
1679
|
+
InteractivityChecker,
|
|
1680
|
+
FocusTrapFactory,
|
|
1681
|
+
AriaDescriber,
|
|
1682
|
+
LIVE_ANNOUNCER_PROVIDER,
|
|
1683
|
+
ARIA_DESCRIBER_PROVIDER,
|
|
1684
|
+
FOCUS_MONITOR_PROVIDER,
|
|
1685
|
+
]
|
|
1686
|
+
},] },
|
|
1687
|
+
];
|
|
1688
|
+
/**
|
|
1689
|
+
* @nocollapse
|
|
1690
|
+
*/
|
|
1691
|
+
A11yModule.ctorParameters = function () { return []; };
|
|
1662
1692
|
return A11yModule;
|
|
1663
1693
|
}());
|
|
1664
|
-
A11yModule.decorators = [
|
|
1665
|
-
{ type: _angular_core.NgModule, args: [{
|
|
1666
|
-
imports: [_angular_common.CommonModule, _angular_cdk_platform.PlatformModule],
|
|
1667
|
-
declarations: [FocusTrapDirective, FocusTrapDeprecatedDirective, CdkMonitorFocus],
|
|
1668
|
-
exports: [FocusTrapDirective, FocusTrapDeprecatedDirective, CdkMonitorFocus],
|
|
1669
|
-
providers: [
|
|
1670
|
-
InteractivityChecker,
|
|
1671
|
-
FocusTrapFactory,
|
|
1672
|
-
AriaDescriber,
|
|
1673
|
-
LIVE_ANNOUNCER_PROVIDER,
|
|
1674
|
-
ARIA_DESCRIBER_PROVIDER,
|
|
1675
|
-
FOCUS_MONITOR_PROVIDER,
|
|
1676
|
-
]
|
|
1677
|
-
},] },
|
|
1678
|
-
];
|
|
1679
|
-
/**
|
|
1680
|
-
* @nocollapse
|
|
1681
|
-
*/
|
|
1682
|
-
A11yModule.ctorParameters = function () { return []; };
|
|
1683
1694
|
|
|
1684
|
-
exports.A11yModule = A11yModule;
|
|
1685
1695
|
exports.ActiveDescendantKeyManager = ActiveDescendantKeyManager;
|
|
1686
1696
|
exports.MESSAGES_CONTAINER_ID = MESSAGES_CONTAINER_ID;
|
|
1687
1697
|
exports.CDK_DESCRIBEDBY_ID_PREFIX = CDK_DESCRIBEDBY_ID_PREFIX;
|
|
@@ -1706,6 +1716,7 @@ exports.FocusMonitor = FocusMonitor;
|
|
|
1706
1716
|
exports.CdkMonitorFocus = CdkMonitorFocus;
|
|
1707
1717
|
exports.FOCUS_MONITOR_PROVIDER_FACTORY = FOCUS_MONITOR_PROVIDER_FACTORY;
|
|
1708
1718
|
exports.FOCUS_MONITOR_PROVIDER = FOCUS_MONITOR_PROVIDER;
|
|
1719
|
+
exports.A11yModule = A11yModule;
|
|
1709
1720
|
|
|
1710
1721
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
1711
1722
|
|