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