@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.
Files changed (306) hide show
  1. package/_a11y.scss +23 -0
  2. package/_overlay.scss +93 -0
  3. package/a11y/index.metadata.json +2 -1
  4. package/a11y/typings/a11y-module.d.ts +2 -0
  5. package/a11y/typings/focus-monitor.d.ts +1 -1
  6. package/a11y/typings/index.d.ts +1 -1
  7. package/a11y/typings/index.metadata.json +1 -1
  8. package/a11y/typings/{public_api.d.ts → public-api.d.ts} +8 -2
  9. package/a11y-prebuilt.css +1 -0
  10. package/a11y.metadata.json +2 -1
  11. package/bidi/index.metadata.json +2 -1
  12. package/bidi/typings/bidi-module.d.ts +2 -0
  13. package/bidi/typings/index.d.ts +1 -1
  14. package/bidi/typings/index.metadata.json +1 -1
  15. package/bidi/typings/public-api.d.ts +10 -0
  16. package/bidi.metadata.json +2 -1
  17. package/bundles/cdk-a11y.umd.js +1368 -1357
  18. package/bundles/cdk-a11y.umd.js.map +1 -1
  19. package/bundles/cdk-a11y.umd.min.js +2 -2
  20. package/bundles/cdk-a11y.umd.min.js.map +1 -1
  21. package/bundles/cdk-bidi.umd.js +42 -40
  22. package/bundles/cdk-bidi.umd.js.map +1 -1
  23. package/bundles/cdk-bidi.umd.min.js +2 -2
  24. package/bundles/cdk-bidi.umd.min.js.map +1 -1
  25. package/bundles/cdk-coercion.umd.js +12 -0
  26. package/bundles/cdk-coercion.umd.js.map +1 -1
  27. package/bundles/cdk-coercion.umd.min.js +2 -2
  28. package/bundles/cdk-coercion.umd.min.js.map +1 -1
  29. package/bundles/cdk-collections.umd.js +113 -11
  30. package/bundles/cdk-collections.umd.js.map +1 -1
  31. package/bundles/cdk-collections.umd.min.js +2 -2
  32. package/bundles/cdk-collections.umd.min.js.map +1 -1
  33. package/bundles/cdk-keycodes.umd.js.map +1 -1
  34. package/bundles/cdk-keycodes.umd.min.js +1 -1
  35. package/bundles/cdk-keycodes.umd.min.js.map +1 -1
  36. package/bundles/cdk-layout.umd.js +235 -0
  37. package/bundles/cdk-layout.umd.js.map +1 -0
  38. package/bundles/cdk-layout.umd.min.js +9 -0
  39. package/bundles/cdk-layout.umd.min.js.map +1 -0
  40. package/bundles/cdk-observers.umd.js +41 -40
  41. package/bundles/cdk-observers.umd.js.map +1 -1
  42. package/bundles/cdk-observers.umd.min.js +2 -2
  43. package/bundles/cdk-observers.umd.min.js.map +1 -1
  44. package/bundles/cdk-overlay.umd.js +306 -265
  45. package/bundles/cdk-overlay.umd.js.map +1 -1
  46. package/bundles/cdk-overlay.umd.min.js +2 -2
  47. package/bundles/cdk-overlay.umd.min.js.map +1 -1
  48. package/bundles/cdk-platform.umd.js +19 -17
  49. package/bundles/cdk-platform.umd.js.map +1 -1
  50. package/bundles/cdk-platform.umd.min.js +2 -2
  51. package/bundles/cdk-platform.umd.min.js.map +1 -1
  52. package/bundles/cdk-portal.umd.js +71 -37
  53. package/bundles/cdk-portal.umd.js.map +1 -1
  54. package/bundles/cdk-portal.umd.min.js +2 -2
  55. package/bundles/cdk-portal.umd.min.js.map +1 -1
  56. package/bundles/cdk-rxjs.umd.js +13 -4
  57. package/bundles/cdk-rxjs.umd.js.map +1 -1
  58. package/bundles/cdk-rxjs.umd.min.js +2 -2
  59. package/bundles/cdk-rxjs.umd.min.js.map +1 -1
  60. package/bundles/cdk-scrolling.umd.js +89 -54
  61. package/bundles/cdk-scrolling.umd.js.map +1 -1
  62. package/bundles/cdk-scrolling.umd.min.js +2 -2
  63. package/bundles/cdk-scrolling.umd.min.js.map +1 -1
  64. package/bundles/cdk-stepper.umd.js +115 -90
  65. package/bundles/cdk-stepper.umd.js.map +1 -1
  66. package/bundles/cdk-stepper.umd.min.js +2 -2
  67. package/bundles/cdk-stepper.umd.min.js.map +1 -1
  68. package/bundles/cdk-table.umd.js +261 -218
  69. package/bundles/cdk-table.umd.js.map +1 -1
  70. package/bundles/cdk-table.umd.min.js +2 -2
  71. package/bundles/cdk-table.umd.min.js.map +1 -1
  72. package/bundles/cdk.umd.js +1 -1
  73. package/bundles/cdk.umd.js.map +1 -1
  74. package/bundles/cdk.umd.min.js +2 -2
  75. package/bundles/cdk.umd.min.js.map +1 -1
  76. package/cdk.metadata.json +2 -1
  77. package/coercion/index.metadata.json +2 -1
  78. package/coercion/typings/array.d.ts +9 -0
  79. package/coercion/typings/index.d.ts +1 -1
  80. package/coercion/typings/index.metadata.json +1 -1
  81. package/{typings/coercion/public_api.d.ts → coercion/typings/public-api.d.ts} +1 -0
  82. package/coercion.metadata.json +2 -1
  83. package/collections/index.metadata.json +2 -1
  84. package/collections/typings/index.d.ts +2 -1
  85. package/collections/typings/index.metadata.json +1 -1
  86. package/{typings/collections/public_api.d.ts → collections/typings/public-api.d.ts} +1 -0
  87. package/collections/typings/selection.d.ts +13 -3
  88. package/collections/typings/unique-selection-dispatcher.d.ts +40 -0
  89. package/collections.metadata.json +2 -1
  90. package/esm2015/a11y.js +1252 -1250
  91. package/esm2015/a11y.js.map +1 -1
  92. package/esm2015/bidi.js +1 -1
  93. package/esm2015/bidi.js.map +1 -1
  94. package/esm2015/cdk.js +1 -1
  95. package/esm2015/cdk.js.map +1 -1
  96. package/esm2015/coercion.js +11 -1
  97. package/esm2015/coercion.js.map +1 -1
  98. package/esm2015/collections.js +93 -8
  99. package/esm2015/collections.js.map +1 -1
  100. package/esm2015/keycodes.js.map +1 -1
  101. package/esm2015/layout.js +226 -0
  102. package/esm2015/layout.js.map +1 -0
  103. package/esm2015/observers.js +8 -7
  104. package/esm2015/observers.js.map +1 -1
  105. package/esm2015/overlay.js +157 -136
  106. package/esm2015/overlay.js.map +1 -1
  107. package/esm2015/platform.js +1 -1
  108. package/esm2015/platform.js.map +1 -1
  109. package/esm2015/portal.js +30 -1
  110. package/esm2015/portal.js.map +1 -1
  111. package/esm2015/rxjs.js +5 -1
  112. package/esm2015/rxjs.js.map +1 -1
  113. package/esm2015/scrolling.js +39 -8
  114. package/esm2015/scrolling.js.map +1 -1
  115. package/esm2015/stepper.js +27 -5
  116. package/esm2015/stepper.js.map +1 -1
  117. package/esm2015/table.js +68 -29
  118. package/esm2015/table.js.map +1 -1
  119. package/esm5/a11y.es5.js +1372 -1357
  120. package/esm5/a11y.es5.js.map +1 -1
  121. package/esm5/bidi.es5.js +45 -40
  122. package/esm5/bidi.es5.js.map +1 -1
  123. package/esm5/cdk.es5.js +4 -1
  124. package/esm5/cdk.es5.js.map +1 -1
  125. package/esm5/coercion.es5.js +14 -1
  126. package/esm5/coercion.es5.js.map +1 -1
  127. package/esm5/collections.es5.js +110 -8
  128. package/esm5/collections.es5.js.map +1 -1
  129. package/esm5/keycodes.es5.js +2 -0
  130. package/esm5/keycodes.es5.js.map +1 -1
  131. package/esm5/layout.es5.js +234 -0
  132. package/esm5/layout.es5.js.map +1 -0
  133. package/esm5/observers.es5.js +44 -40
  134. package/esm5/observers.es5.js.map +1 -1
  135. package/esm5/overlay.es5.js +304 -259
  136. package/esm5/overlay.es5.js.map +1 -1
  137. package/esm5/platform.es5.js +22 -17
  138. package/esm5/platform.es5.js.map +1 -1
  139. package/esm5/portal.es5.js +81 -44
  140. package/esm5/portal.es5.js.map +1 -1
  141. package/esm5/rxjs.es5.js +12 -1
  142. package/esm5/rxjs.es5.js.map +1 -1
  143. package/esm5/scrolling.es5.js +89 -51
  144. package/esm5/scrolling.es5.js.map +1 -1
  145. package/esm5/stepper.es5.js +119 -91
  146. package/esm5/stepper.es5.js.map +1 -1
  147. package/esm5/table.es5.js +265 -218
  148. package/esm5/table.es5.js.map +1 -1
  149. package/keycodes/index.metadata.json +2 -1
  150. package/keycodes/typings/index.d.ts +1 -1
  151. package/keycodes/typings/{public_api.d.ts → public-api.d.ts} +0 -0
  152. package/keycodes.metadata.json +2 -1
  153. package/layout/index.d.ts +8 -0
  154. package/layout/index.metadata.json +12 -0
  155. package/layout/package.json +7 -0
  156. package/layout/typings/breakpoints-observer.d.ts +37 -0
  157. package/layout/typings/breakpoints.d.ts +18 -0
  158. package/layout/typings/index.d.ts +4 -0
  159. package/layout/typings/index.metadata.json +1 -0
  160. package/layout/typings/media-matcher.d.ts +15 -0
  161. package/layout/typings/public-api.d.ts +5 -0
  162. package/layout.d.ts +8 -0
  163. package/layout.metadata.json +12 -0
  164. package/observers/index.metadata.json +2 -1
  165. package/observers/typings/index.d.ts +1 -1
  166. package/observers/typings/index.metadata.json +1 -1
  167. package/observers/typings/observe-content.d.ts +3 -3
  168. package/observers/typings/{public_api.d.ts → public-api.d.ts} +0 -0
  169. package/observers.metadata.json +2 -1
  170. package/overlay/index.metadata.json +2 -1
  171. package/overlay/typings/index.d.ts +2 -2
  172. package/overlay/typings/index.metadata.json +1 -1
  173. package/overlay/typings/overlay-config.d.ts +1 -1
  174. package/overlay/typings/overlay-directives.d.ts +3 -3
  175. package/overlay/typings/overlay-module.d.ts +11 -0
  176. package/overlay/typings/overlay-ref.d.ts +6 -6
  177. package/overlay/typings/overlay.d.ts +2 -2
  178. package/overlay/typings/position/connected-position-strategy.d.ts +5 -0
  179. package/overlay/typings/position/position-strategy.d.ts +2 -0
  180. package/overlay/typings/{public_api.d.ts → public-api.d.ts} +1 -4
  181. package/overlay/typings/scroll/scroll-strategy.d.ts +1 -1
  182. package/overlay-prebuilt.css +1 -0
  183. package/overlay.metadata.json +2 -1
  184. package/package.json +3 -3
  185. package/platform/index.metadata.json +2 -1
  186. package/platform/typings/index.d.ts +1 -1
  187. package/platform/typings/index.metadata.json +1 -1
  188. package/platform/typings/platform-module.d.ts +2 -0
  189. package/platform/typings/public-api.d.ts +10 -0
  190. package/platform.metadata.json +2 -1
  191. package/portal/index.metadata.json +2 -1
  192. package/portal/typings/index.d.ts +1 -1
  193. package/portal/typings/index.metadata.json +1 -1
  194. package/portal/typings/{public_api.d.ts → public-api.d.ts} +1 -0
  195. package/portal.metadata.json +2 -1
  196. package/rxjs/index.metadata.json +2 -1
  197. package/rxjs/typings/index.d.ts +1 -1
  198. package/rxjs/typings/index.metadata.json +1 -1
  199. package/rxjs/typings/{public_api.d.ts → public-api.d.ts} +0 -0
  200. package/rxjs/typings/rx-operators.d.ts +7 -0
  201. package/rxjs.metadata.json +2 -1
  202. package/scrolling/index.metadata.json +2 -1
  203. package/scrolling/typings/index.d.ts +1 -1
  204. package/scrolling/typings/index.metadata.json +1 -1
  205. package/scrolling/typings/public-api.d.ts +11 -0
  206. package/scrolling/typings/scrolling-module.d.ts +2 -0
  207. package/scrolling/typings/viewport-ruler.d.ts +20 -6
  208. package/scrolling.metadata.json +2 -1
  209. package/stepper/index.metadata.json +2 -1
  210. package/stepper/typings/index.d.ts +1 -1
  211. package/stepper/typings/index.metadata.json +1 -1
  212. package/stepper/typings/public-api.d.ts +11 -0
  213. package/stepper/typings/stepper-module.d.ts +2 -0
  214. package/stepper/typings/stepper.d.ts +8 -4
  215. package/stepper.metadata.json +2 -1
  216. package/table/index.metadata.json +2 -1
  217. package/table/typings/index.d.ts +1 -1
  218. package/table/typings/index.metadata.json +1 -1
  219. package/table/typings/public-api.d.ts +13 -0
  220. package/table/typings/row.d.ts +11 -3
  221. package/table/typings/table-errors.d.ts +10 -0
  222. package/table/typings/table-module.d.ts +2 -0
  223. package/table/typings/table.d.ts +17 -8
  224. package/table.metadata.json +2 -1
  225. package/typings/a11y/a11y-module.d.ts +2 -0
  226. package/typings/a11y/focus-monitor.d.ts +1 -1
  227. package/typings/a11y/index.d.ts +1 -1
  228. package/typings/a11y/index.metadata.json +1 -1
  229. package/typings/a11y/{public_api.d.ts → public-api.d.ts} +8 -2
  230. package/typings/bidi/bidi-module.d.ts +2 -0
  231. package/typings/bidi/index.d.ts +1 -1
  232. package/typings/bidi/index.metadata.json +1 -1
  233. package/typings/bidi/public-api.d.ts +10 -0
  234. package/typings/coercion/array.d.ts +9 -0
  235. package/typings/coercion/index.d.ts +1 -1
  236. package/typings/coercion/index.metadata.json +1 -1
  237. package/{coercion/typings/public_api.d.ts → typings/coercion/public-api.d.ts} +1 -0
  238. package/typings/collections/index.d.ts +2 -1
  239. package/typings/collections/index.metadata.json +1 -1
  240. package/{collections/typings/public_api.d.ts → typings/collections/public-api.d.ts} +1 -0
  241. package/typings/collections/selection.d.ts +13 -3
  242. package/typings/collections/unique-selection-dispatcher.d.ts +40 -0
  243. package/typings/index.d.ts +1 -1
  244. package/typings/index.metadata.json +1 -1
  245. package/typings/keycodes/index.d.ts +1 -1
  246. package/typings/keycodes/{public_api.d.ts → public-api.d.ts} +0 -0
  247. package/typings/layout/breakpoints-observer.d.ts +37 -0
  248. package/typings/layout/breakpoints.d.ts +18 -0
  249. package/typings/layout/index.d.ts +4 -0
  250. package/typings/layout/index.metadata.json +1 -0
  251. package/typings/layout/media-matcher.d.ts +15 -0
  252. package/typings/layout/public-api.d.ts +5 -0
  253. package/typings/observers/index.d.ts +1 -1
  254. package/typings/observers/index.metadata.json +1 -1
  255. package/typings/observers/observe-content.d.ts +3 -3
  256. package/typings/observers/{public_api.d.ts → public-api.d.ts} +0 -0
  257. package/typings/overlay/index.d.ts +2 -2
  258. package/typings/overlay/index.metadata.json +1 -1
  259. package/typings/overlay/overlay-config.d.ts +1 -1
  260. package/typings/overlay/overlay-directives.d.ts +3 -3
  261. package/typings/overlay/overlay-module.d.ts +11 -0
  262. package/typings/overlay/overlay-ref.d.ts +6 -6
  263. package/typings/overlay/overlay.d.ts +2 -2
  264. package/typings/overlay/position/connected-position-strategy.d.ts +5 -0
  265. package/typings/overlay/position/position-strategy.d.ts +2 -0
  266. package/typings/overlay/{public_api.d.ts → public-api.d.ts} +1 -4
  267. package/typings/overlay/scroll/scroll-strategy.d.ts +1 -1
  268. package/typings/platform/index.d.ts +1 -1
  269. package/typings/platform/index.metadata.json +1 -1
  270. package/typings/platform/platform-module.d.ts +2 -0
  271. package/typings/platform/public-api.d.ts +10 -0
  272. package/typings/portal/index.d.ts +1 -1
  273. package/typings/portal/index.metadata.json +1 -1
  274. package/typings/portal/{public_api.d.ts → public-api.d.ts} +1 -0
  275. package/typings/{public_api.d.ts → public-api.d.ts} +0 -0
  276. package/typings/rxjs/index.d.ts +1 -1
  277. package/typings/rxjs/index.metadata.json +1 -1
  278. package/typings/rxjs/{public_api.d.ts → public-api.d.ts} +0 -0
  279. package/typings/rxjs/rx-operators.d.ts +7 -0
  280. package/typings/scrolling/index.d.ts +1 -1
  281. package/typings/scrolling/index.metadata.json +1 -1
  282. package/typings/scrolling/public-api.d.ts +11 -0
  283. package/typings/scrolling/scrolling-module.d.ts +2 -0
  284. package/typings/scrolling/viewport-ruler.d.ts +20 -6
  285. package/typings/stepper/index.d.ts +1 -1
  286. package/typings/stepper/index.metadata.json +1 -1
  287. package/typings/stepper/public-api.d.ts +11 -0
  288. package/typings/stepper/stepper-module.d.ts +2 -0
  289. package/typings/stepper/stepper.d.ts +8 -4
  290. package/typings/table/index.d.ts +1 -1
  291. package/typings/table/index.metadata.json +1 -1
  292. package/typings/table/public-api.d.ts +13 -0
  293. package/typings/table/row.d.ts +11 -3
  294. package/typings/table/table-errors.d.ts +10 -0
  295. package/typings/table/table-module.d.ts +2 -0
  296. package/typings/table/table.d.ts +17 -8
  297. package/bidi/typings/public_api.d.ts +0 -4
  298. package/platform/typings/public_api.d.ts +0 -4
  299. package/scrolling/typings/public_api.d.ts +0 -5
  300. package/stepper/typings/public_api.d.ts +0 -5
  301. package/table/typings/public_api.d.ts +0 -7
  302. package/typings/bidi/public_api.d.ts +0 -4
  303. package/typings/platform/public_api.d.ts +0 -4
  304. package/typings/scrolling/public_api.d.ts +0 -5
  305. package/typings/stepper/public_api.d.ts +0 -5
  306. 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 { Directive, ElementRef, EventEmitter, Inject, Injectable, InjectionToken, Input, NgModule, NgZone, Optional, Output, Renderer2, SkipSelf } from '@angular/core';
10
- import { coerceBooleanProperty } from '@angular/cdk/coercion';
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
- * Utility for checking the interactivity of an element, such as whether is is focusable or
20
- * tabbable.
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 InteractivityChecker = (function () {
24
+ var ListKeyManager = (function () {
23
25
  /**
24
- * @param {?} _platform
26
+ * @param {?} _items
25
27
  */
26
- function InteractivityChecker(_platform) {
27
- this._platform = _platform;
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
- * Gets whether an element is disabled.
31
- *
32
- * @param {?} element Element to be checked.
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
- InteractivityChecker.prototype.isDisabled = function (element) {
36
- // This does not capture some cases, such as a non-form control with a disabled attribute or
37
- // a form control inside of a disabled form, but should capture the most common cases.
38
- return element.hasAttribute('disabled');
46
+ ListKeyManager.prototype.withWrap = function () {
47
+ this._wrap = true;
48
+ return this;
39
49
  };
40
50
  /**
41
- * Gets whether an element is visible for the purposes of interactivity.
42
- *
43
- * This will capture states like `display: none` and `visibility: hidden`, but not things like
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
- InteractivityChecker.prototype.isVisible = function (element) {
50
- return hasGeometry(element) && getComputedStyle(element).visibility === 'visible';
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
- * Gets whether an element can be reached via Tab key.
54
- * Assumes that the element has already been checked with isFocusable.
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
- InteractivityChecker.prototype.isTabbable = function (element) {
60
- // Nothing is tabbable on the the server 😎
61
- if (!this._platform.isBrowser) {
62
- return false;
63
- }
64
- var /** @type {?} */ frameElement = (getWindow(element).frameElement);
65
- if (frameElement) {
66
- var /** @type {?} */ frameType = frameElement && frameElement.nodeName.toLowerCase();
67
- // Frame elements inherit their tabindex onto all child elements.
68
- if (getTabIndexValue(frameElement) === -1) {
69
- return false;
70
- }
71
- // Webkit and Blink consider anything inside of an <object> element as non-tabbable.
72
- if ((this._platform.BLINK || this._platform.WEBKIT) && frameType === 'object') {
73
- return false;
74
- }
75
- // Webkit and Blink disable tabbing to an element inside of an invisible frame.
76
- if ((this._platform.BLINK || this._platform.WEBKIT) && !this.isVisible(frameElement)) {
77
- return false;
78
- }
79
- }
80
- var /** @type {?} */ nodeName = element.nodeName.toLowerCase();
81
- var /** @type {?} */ tabIndexValue = getTabIndexValue(element);
82
- if (element.hasAttribute('contenteditable')) {
83
- return tabIndexValue !== -1;
84
- }
85
- if (nodeName === 'iframe') {
86
- // The frames may be tabbable depending on content, but it's not possibly to reliably
87
- // investigate the content of the frames.
88
- return false;
89
- }
90
- if (nodeName === 'audio') {
91
- if (!element.hasAttribute('controls')) {
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
- return element.tabIndex >= 0;
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
- * Gets whether an element can be focused by the user.
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
- InteractivityChecker.prototype.isFocusable = function (element) {
127
- // Perform checks in order of left to most expensive.
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
- return InteractivityChecker;
132
- }());
133
- InteractivityChecker.decorators = [
134
- { type: Injectable },
135
- ];
136
- /**
137
- * @nocollapse
138
- */
139
- InteractivityChecker.ctorParameters = function () { return [
140
- { type: Platform, },
141
- ]; };
142
- /**
143
- * Checks whether the specified element has any geometry / rectangles.
144
- * @param {?} element
145
- * @return {?}
146
- */
147
- function hasGeometry(element) {
148
- // Use logic from jQuery to check for an invisible element.
149
- // See https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js#L12
150
- return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
151
- }
152
- /**
153
- * Gets whether an element's
154
- * @param {?} element
155
- * @return {?}
156
- */
157
- function isNativeFormElement(element) {
158
- var /** @type {?} */ nodeName = element.nodeName.toLowerCase();
159
- return nodeName === 'input' ||
160
- nodeName === 'select' ||
161
- nodeName === 'button' ||
162
- nodeName === 'textarea';
163
- }
164
- /**
165
- * Gets whether an element is an <input type="hidden">.
166
- * @param {?} element
167
- * @return {?}
168
- */
169
- function isHiddenInput(element) {
170
- return isInputElement(element) && element.type == 'hidden';
171
- }
172
- /**
173
- * Gets whether an element is an anchor that has an href attribute.
174
- * @param {?} element
175
- * @return {?}
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
- function isAnchorWithHref(element) {
178
- return isAnchorElement(element) && element.hasAttribute('href');
179
- }
283
+ var ID_DELIMINATOR = ' ';
180
284
  /**
181
- * Gets whether an element is an input element.
182
- * @param {?} element
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 isInputElement(element) {
186
- return element.nodeName.toLowerCase() == 'input';
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
- * Gets whether an element is an anchor element.
190
- * @param {?} element
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 isAnchorElement(element) {
194
- return element.nodeName.toLowerCase() == 'a';
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 whether an element has a valid tabindex.
198
- * @param {?} element
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 hasValidTabIndex(element) {
202
- if (!element.hasAttribute('tabindex') || element.tabIndex === undefined) {
203
- return false;
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
- * Returns the parsed tabindex from the element attributes instead of returning the
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
- function getTabIndexValue(element) {
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
- * Checks whether the specified element is potentially tabbable on iOS
228
- * @param {?} element
229
- * @return {?}
330
+ * ID prefix used for each created message element.
230
331
  */
231
- function isPotentiallyTabbableIOS(element) {
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
- * Gets whether an element is potentially focusable without taking current visible/disabled state
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
- function isPotentiallyFocusable(element) {
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
- * Gets the parent window of a DOM node with regards of being inside of an iframe.
257
- * @param {?} node
258
- * @return {?}
338
+ * Global incremental identifier for each registered message element.
259
339
  */
260
- function getWindow(node) {
261
- return node.ownerDocument.defaultView || window;
262
- }
340
+ var nextId = 0;
263
341
  /**
264
- * Class that allows for trapping focus within a DOM element.
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 FocusTrap = (function () {
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 FocusTrap(_element, _platform, _checker, _ngZone, deferAnchors) {
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
- * Inserts the anchors into the DOM. This is usually done automatically
324
- * in the constructor, but can be deferred for cases like directives with `*ngIf`.
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
- FocusTrap.prototype.attachAnchors = function () {
328
- var _this = this;
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 (!this._startAnchor) {
334
- this._startAnchor = this._createAnchor();
374
+ if (!messageRegistry.has(message)) {
375
+ createMessageElement(message);
335
376
  }
336
- if (!this._endAnchor) {
337
- this._endAnchor = this._createAnchor();
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
- * Get the specified boundary element of the trapped region.
390
- * @param {?} bound The boundary to get (start or end of trapped region).
391
- * @return {?} The boundary element.
382
+ * Removes the host element's aria-describedby reference to the message element.
383
+ * @param {?} hostElement
384
+ * @param {?} message
385
+ * @return {?}
392
386
  */
393
- FocusTrap.prototype._getRegionBoundary = function (bound) {
394
- // Contains the deprecated version of selector, for temporary backwards comparability.
395
- var /** @type {?} */ markers = (this._element.querySelectorAll("[cdk-focus-region-" + bound + "], " +
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
- return markers.length ?
407
- markers[markers.length - 1] : this._getLastTabbableElement(this._element);
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
- return this.focusFirstTabbableElement();
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
- return !!redirectToElement;
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
- * Get the first tabbable element from a DOM subtree (inclusive).
445
- * @param {?} root
403
+ * Unregisters all created message elements and removes the message container.
446
404
  * @return {?}
447
405
  */
448
- FocusTrap.prototype._getFirstTabbableElement = function (root) {
449
- if (this._checker.isFocusable(root) && this._checker.isTabbable(root)) {
450
- return root;
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
- return null;
464
- };
465
- /**
466
- * Get the last tabbable element from a DOM subtree (inclusive).
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
- // Iterate in reverse DOM order.
475
- var /** @type {?} */ children = root.children || root.childNodes;
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
- return null;
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
- * Executes a function when the zone is stable.
499
- * @param {?} fn
500
- * @return {?}
424
+ * @nocollapse
501
425
  */
502
- FocusTrap.prototype._executeOnStable = function (fn) {
503
- if (this._ngZone.isStable) {
504
- fn();
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
- * Factory that allows easy instantiation of focus traps.
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
- var FocusTrapFactory = (function () {
516
- /**
517
- * @param {?} _checker
518
- * @param {?} _platform
519
- * @param {?} _ngZone
520
- */
521
- function FocusTrapFactory(_checker, _platform, _ngZone) {
522
- this._checker = _checker;
523
- this._platform = _platform;
524
- this._ngZone = _ngZone;
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
- * @nocollapse
448
+ * Deletes the message element from the global messages container.
449
+ * @param {?} message
450
+ * @return {?}
542
451
  */
543
- FocusTrapFactory.ctorParameters = function () { return [
544
- { type: InteractivityChecker, },
545
- { type: Platform, },
546
- { type: NgZone, },
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
- * Directive for trapping focus within a region.
550
- * @deprecated
461
+ * Creates the global container for all aria-describedby messages.
462
+ * @return {?}
551
463
  */
552
- var FocusTrapDeprecatedDirective = (function () {
553
- /**
554
- * @param {?} _elementRef
555
- * @param {?} _focusTrapFactory
556
- */
557
- function FocusTrapDeprecatedDirective(_elementRef, _focusTrapFactory) {
558
- this._elementRef = _elementRef;
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
- * @nocollapse
472
+ * Deletes the global messages container.
473
+ * @return {?}
599
474
  */
600
- FocusTrapDeprecatedDirective.ctorParameters = function () { return [
601
- { type: ElementRef, },
602
- { type: FocusTrapFactory, },
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
- * Directive for trapping focus within a region.
480
+ * Removes all cdk-describedby messages that are hosted through the element.
481
+ * @param {?} element
482
+ * @return {?}
609
483
  */
610
- var FocusTrapDirective = (function () {
611
- /**
612
- * @param {?} _elementRef
613
- * @param {?} _focusTrapFactory
614
- */
615
- function FocusTrapDirective(_elementRef, _focusTrapFactory) {
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
- * @nocollapse
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
- FocusTrapDirective.ctorParameters = function () { return [
658
- { type: ElementRef, },
659
- { type: FocusTrapFactory, },
660
- ]; };
661
- FocusTrapDirective.propDecorators = {
662
- 'enabled': [{ type: Input, args: ['cdkTrapFocus',] },],
663
- };
664
- var LIVE_ANNOUNCER_ELEMENT_TOKEN = new InjectionToken('liveAnnouncerElement');
665
- var LiveAnnouncer = (function () {
666
- /**
667
- * @param {?} elementToken
668
- * @param {?} platform
669
- */
670
- function LiveAnnouncer(elementToken, platform) {
671
- // Only do anything if we're on the browser platform.
672
- if (platform.isBrowser) {
673
- // We inject the live element as `any` because the constructor signature cannot reference
674
- // browser globals (HTMLElement) on non-browser environments, since having a class decorator
675
- // causes TypeScript to preserve the constructor signature types.
676
- this._liveElement = elementToken || this._createLiveElement();
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
- * @nocollapse
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
- LiveAnnouncer.ctorParameters = function () { return [
726
- { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [LIVE_ANNOUNCER_ELEMENT_TOKEN,] },] },
727
- { type: Platform, },
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 LIVE_ANNOUNCER_PROVIDER_FACTORY(parentDispatcher, liveElement, platform) {
737
- return parentDispatcher || new LiveAnnouncer(liveElement, platform);
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 LIVE_ANNOUNCER_PROVIDER = {
743
- // If there is already a LiveAnnouncer available, use that. Otherwise, provide a new one.
744
- provide: LiveAnnouncer,
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(), LiveAnnouncer],
747
- [new Optional(), new Inject(LIVE_ANNOUNCER_ELEMENT_TOKEN)],
748
- Platform,
545
+ [new Optional(), new SkipSelf(), AriaDescriber],
546
+ Platform
749
547
  ],
750
- useFactory: LIVE_ANNOUNCER_PROVIDER_FACTORY
548
+ useFactory: ARIA_DESCRIBER_PROVIDER_FACTORY
751
549
  };
550
+
752
551
  /**
753
- * IDs are deliminated by an empty space, as per the spec.
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
- var ID_DELIMINATOR = ' ';
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
- * ID used for the body container where all messages are appended.
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 AriaDescriber = (function () {
588
+ var InteractivityChecker = (function () {
827
589
  /**
828
590
  * @param {?} _platform
829
591
  */
830
- function AriaDescriber(_platform) {
592
+ function InteractivityChecker(_platform) {
831
593
  this._platform = _platform;
832
594
  }
833
595
  /**
834
- * Adds to the host element an aria-describedby reference to a hidden element that contains
835
- * the message. If the same message has already been registered, then it will reuse the created
836
- * message element.
837
- * @param {?} hostElement
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
- AriaDescriber.prototype.describe = function (hostElement, message) {
842
- if (!this._platform.isBrowser || !message.trim()) {
843
- return;
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
- * Removes the host element's aria-describedby reference to the message element.
854
- * @param {?} hostElement
855
- * @param {?} message
856
- * @return {?}
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
- AriaDescriber.prototype.removeDescription = function (hostElement, message) {
859
- if (!this._platform.isBrowser || !message.trim()) {
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
- * Unregisters all created message elements and removes the message container.
875
- * @return {?}
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
- AriaDescriber.prototype.ngOnDestroy = function () {
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 {?} */ describedElements = document.querySelectorAll("[" + CDK_DESCRIBEDBY_HOST_ATTRIBUTE + "]");
882
- for (var /** @type {?} */ i = 0; i < describedElements.length; i++) {
883
- removeCdkDescribedByReferenceIds(describedElements[i]);
884
- describedElements[i].removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE);
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
- if (messagesContainer) {
887
- deleteMessagesContainer();
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
- messageRegistry.clear();
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
- return AriaDescriber;
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
- * Creates a new element in the visually hidden message container element with the message
904
- * as its content and adds it to the message registry.
905
- * @param {?} message
709
+ * Checks whether the specified element has any geometry / rectangles.
710
+ * @param {?} element
906
711
  * @return {?}
907
712
  */
908
- function createMessageElement(message) {
909
- var /** @type {?} */ messageElement = document.createElement('div');
910
- messageElement.setAttribute('id', CDK_DESCRIBEDBY_ID_PREFIX + "-" + nextId++);
911
- messageElement.appendChild(/** @type {?} */ ((document.createTextNode(message))));
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
- * Deletes the message element from the global messages container.
920
- * @param {?} message
719
+ * Gets whether an element's
720
+ * @param {?} element
921
721
  * @return {?}
922
722
  */
923
- function deleteMessageElement(message) {
924
- var /** @type {?} */ registeredMessage = messageRegistry.get(message);
925
- var /** @type {?} */ messageElement = registeredMessage && registeredMessage.messageElement;
926
- if (messagesContainer && messageElement) {
927
- messagesContainer.removeChild(messageElement);
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
- * Creates the global container for all aria-describedby messages.
731
+ * Gets whether an element is an <input type="hidden">.
732
+ * @param {?} element
933
733
  * @return {?}
934
734
  */
935
- function createMessagesContainer() {
936
- messagesContainer = document.createElement('div');
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
- * Deletes the global messages container.
739
+ * Gets whether an element is an anchor that has an href attribute.
740
+ * @param {?} element
944
741
  * @return {?}
945
742
  */
946
- function deleteMessagesContainer() {
947
- document.body.removeChild(/** @type {?} */ ((messagesContainer)));
948
- messagesContainer = null;
743
+ function isAnchorWithHref(element) {
744
+ return isAnchorElement(element) && element.hasAttribute('href');
949
745
  }
950
746
  /**
951
- * Removes all cdk-describedby messages that are hosted through the element.
747
+ * Gets whether an element is an input element.
952
748
  * @param {?} element
953
749
  * @return {?}
954
750
  */
955
- function removeCdkDescribedByReferenceIds(element) {
956
- // Remove all aria-describedby reference IDs that are prefixed by CDK_DESCRIBEDBY_ID_PREFIX
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
- * Adds a message reference to the element using aria-describedby and increments the registered
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 addMessageReference(element, message) {
969
- var /** @type {?} */ registeredMessage = ((messageRegistry.get(message)));
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
- * Removes a message reference from the element using aria-describedby and decrements the registered
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 removeMessageReference(element, message) {
983
- var /** @type {?} */ registeredMessage = ((messageRegistry.get(message)));
984
- registeredMessage.referenceCount--;
985
- removeAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id);
986
- element.removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE);
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 true if the element has been described by the provided message ID.
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 isElementDescribedByMessage(element, message) {
995
- var /** @type {?} */ referenceIds = getAriaReferenceIds(element, 'aria-describedby');
996
- var /** @type {?} */ registeredMessage = messageRegistry.get(message);
997
- var /** @type {?} */ messageId = registeredMessage && registeredMessage.messageElement.id;
998
- return !!messageId && referenceIds.indexOf(messageId) != -1;
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
- * \@docs-private
1002
- * @param {?} parentDispatcher
1003
- * @param {?} platform
793
+ * Checks whether the specified element is potentially tabbable on iOS
794
+ * @param {?} element
1004
795
  * @return {?}
1005
796
  */
1006
- function ARIA_DESCRIBER_PROVIDER_FACTORY(parentDispatcher, platform) {
1007
- return parentDispatcher || new AriaDescriber(platform);
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
- * \@docs-private
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
- var ARIA_DESCRIBER_PROVIDER = {
1013
- // If there is already an AriaDescriber available, use that. Otherwise, provide a new one.
1014
- provide: AriaDescriber,
1015
- deps: [
1016
- [new Optional(), new SkipSelf(), AriaDescriber],
1017
- Platform
1018
- ],
1019
- useFactory: ARIA_DESCRIBER_PROVIDER_FACTORY
1020
- };
1021
- // This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found
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
- * Monitors mouse and keyboard events to determine the cause of focus events.
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
- var FocusMonitor = (function () {
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 {?} _ngZone
840
+ * @param {?} _element
1030
841
  * @param {?} _platform
842
+ * @param {?} _checker
843
+ * @param {?} _ngZone
844
+ * @param {?=} deferAnchors
1031
845
  */
1032
- function FocusMonitor(_ngZone, _platform) {
1033
- var _this = this;
1034
- this._ngZone = _ngZone;
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
- * The focus origin that the next focus event is a result of.
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._windowFocused = false;
862
+ get: function () { return this._enabled; },
1044
863
  /**
1045
- * Weak map of elements being monitored to their info.
864
+ * @param {?} val
865
+ * @return {?}
1046
866
  */
1047
- this._elementInfo = new WeakMap();
1048
- this._ngZone.runOutsideAngular(function () { return _this._registerDocumentEvents(); });
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
- * Monitors focus on an element and applies appropriate CSS classes.
1052
- * @param {?} element The element to monitor
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
- FocusMonitor.prototype.monitor = function (element, renderer, checkChildren) {
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
- // Do nothing if we're not on the browser platform.
896
+ // If we're not on the browser, there can be no focus to trap.
1061
897
  if (!this._platform.isBrowser) {
1062
- return of(null);
898
+ return;
1063
899
  }
1064
- // Check if we're already monitoring this element.
1065
- if (this._elementInfo.has(element)) {
1066
- var /** @type {?} */ cachedInfo = this._elementInfo.get(element); /** @type {?} */
1067
- ((cachedInfo)).checkChildren = checkChildren;
1068
- return ((cachedInfo)).subject.asObservable();
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
- element.addEventListener('focus', focusListener, true);
1083
- element.addEventListener('blur', blurListener, true);
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
- * Stops monitoring an element and removes all focus classes.
1094
- * @param {?} element The element to stop monitoring.
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
- FocusMonitor.prototype.stopMonitoring = function (element) {
1098
- var /** @type {?} */ elementInfo = this._elementInfo.get(element);
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
- FocusMonitor.prototype.focusVia = function (element, origin) {
1113
- this._setOriginForCurrentEventQueue(origin);
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
- * Register necessary event listeners on the document and window.
1118
- * @return {?}
1163
+ * @nocollapse
1119
1164
  */
1120
- FocusMonitor.prototype._registerDocumentEvents = function () {
1121
- var _this = this;
1122
- // Do nothing if we're not on the browser platform.
1123
- if (!this._platform.isBrowser) {
1124
- return;
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
- FocusMonitor.prototype._setClasses = function (element, origin) {
1164
- var /** @type {?} */ elementInfo = this._elementInfo.get(element);
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
- FocusMonitor.prototype._setOriginForCurrentEventQueue = function (origin) {
1183
- var _this = this;
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
- * Checks whether the given focus event was caused by a touchstart event.
1189
- * @param {?} event The focus event to check.
1190
- * @return {?} Whether the event was caused by a touch.
1220
+ * @nocollapse
1191
1221
  */
1192
- FocusMonitor.prototype._wasCausedByTouch = function (event) {
1193
- // Note(mmalerba): This implementation is not quite perfect, there is a small edge case.
1194
- // Consider the following dom structure:
1195
- //
1196
- // <div #parent tabindex="0" cdkFocusClasses>
1197
- // <div #child (click)="#parent.focus()"></div>
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
- * Handles focus events on a registered element.
1216
- * @param {?} event The focus event.
1217
- * @param {?} element The monitored element.
1218
- * @return {?}
1235
+ * @param {?} elementToken
1236
+ * @param {?} platform
1219
1237
  */
1220
- FocusMonitor.prototype._onFocus = function (event, element) {
1221
- // NOTE(mmalerba): We currently set the classes based on the focus origin of the most recent
1222
- // focus event affecting the monitored element. If we want to use the origin of the first event
1223
- // instead we should check for the cdk-focused class here and return if the element already has
1224
- // it. (This only matters for elements that have includesChildren = true).
1225
- // If we are not counting child-element-focus as focused, make sure that the event target is the
1226
- // monitored element itself.
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
- this._setClasses(element, this._origin);
1249
- elementInfo.subject.next(this._origin);
1250
- this._lastFocusOrigin = this._origin;
1251
- this._origin = null;
1252
- };
1246
+ }
1253
1247
  /**
1254
- * Handles blur events on a registered element.
1255
- * @param {?} event The blur event.
1256
- * @param {?} element The monitored element.
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
- FocusMonitor.prototype._onBlur = function (event, element) {
1260
- // If we are counting child-element-focus as focused, make sure that we aren't just blurring in
1261
- // order to focus another child of the monitored element.
1262
- var /** @type {?} */ elementInfo = this._elementInfo.get(element);
1263
- if (!elementInfo || (elementInfo.checkChildren && event.relatedTarget instanceof Node &&
1264
- element.contains(event.relatedTarget))) {
1265
- return;
1266
- }
1267
- this._setClasses(element);
1268
- elementInfo.subject.next(null);
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
- * @param {?} _elementRef
1294
- * @param {?} _focusMonitor
1295
- * @param {?} renderer
1267
+ * @return {?}
1296
1268
  */
1297
- function CdkMonitorFocus(_elementRef, _focusMonitor, renderer) {
1298
- var _this = this;
1299
- this._elementRef = _elementRef;
1300
- this._focusMonitor = _focusMonitor;
1301
- this.cdkFocusChange = new EventEmitter();
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
- CdkMonitorFocus.prototype.ngOnDestroy = function () {
1309
- this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);
1310
- this._monitorSubscription.unsubscribe();
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
- return CdkMonitorFocus;
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 {?} ngZone
1300
+ * @param {?} liveElement
1334
1301
  * @param {?} platform
1335
1302
  * @return {?}
1336
1303
  */
1337
- function FOCUS_MONITOR_PROVIDER_FACTORY(parentDispatcher, ngZone, platform) {
1338
- return parentDispatcher || new FocusMonitor(ngZone, platform);
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 FOCUS_MONITOR_PROVIDER = {
1344
- // If there is already a FocusMonitor available, use that. Otherwise, provide a new one.
1345
- provide: FocusMonitor,
1346
- deps: [[new Optional(), new SkipSelf(), FocusMonitor], NgZone, Platform],
1347
- useFactory: FOCUS_MONITOR_PROVIDER_FACTORY
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
- * This class manages keyboard events for selectable lists. If you pass it a query list
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 ListKeyManager = (function () {
1327
+ var FocusMonitor = (function () {
1354
1328
  /**
1355
- * @param {?} _items
1329
+ * @param {?} _ngZone
1330
+ * @param {?} _platform
1356
1331
  */
1357
- function ListKeyManager(_items) {
1358
- this._items = _items;
1359
- this._activeItemIndex = -1;
1360
- this._wrap = false;
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
- * Stream that emits any time the TAB key is pressed, so components can react
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.tabOut = new Subject();
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
- * Turns on wrapping mode, which ensures that the active item will wrap to
1372
- * the other end of list when there are no more items in the given direction.
1373
- * @return {?}
1374
- */
1375
- ListKeyManager.prototype.withWrap = function () {
1376
- this._wrap = true;
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
- ListKeyManager.prototype.withTypeAhead = function (debounceInterval) {
1358
+ FocusMonitor.prototype.monitor = function (element, renderer, checkChildren) {
1385
1359
  var _this = this;
1386
- if (debounceInterval === void 0) { debounceInterval = 200; }
1387
- if (this._items.length && this._items.some(function (item) { return typeof item.getLabel !== 'function'; })) {
1388
- throw Error('ListKeyManager items in typeahead mode must implement the `getLabel` method.');
1360
+ // Do nothing if we're not on the browser platform.
1361
+ if (!this._platform.isBrowser) {
1362
+ return of(null);
1389
1363
  }
1390
- this._typeaheadSubscription.unsubscribe();
1391
- // Debounce the presses of non-navigational keys, collect the ones that correspond to letters
1392
- // and convert those letters back into a string. Afterwards find the first item that starts
1393
- // with that string and select it.
1394
- this._typeaheadSubscription = RxChain.from(this._letterKeyStream)
1395
- .call(doOperator, function (keyCode) { return _this._pressedLetters.push(keyCode); })
1396
- .call(debounceTime, debounceInterval)
1397
- .call(filter, function () { return _this._pressedLetters.length > 0; })
1398
- .call(map, function () { return _this._pressedLetters.join(''); })
1399
- .subscribe(function (inputString) {
1400
- var /** @type {?} */ items = _this._items.toArray();
1401
- for (var /** @type {?} */ i = 0; i < items.length; i++) {
1402
- if (((items[i].getLabel))().toUpperCase().trim().indexOf(inputString) === 0) {
1403
- _this.setActiveItem(i);
1404
- break;
1405
- }
1406
- }
1407
- _this._pressedLetters = [];
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
- return this;
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
- * Sets the active item to the item at the index specified.
1413
- * @param {?} index The index of the item to be set as active.
1393
+ * Stops monitoring an element and removes all focus classes.
1394
+ * @param {?} element The element to stop monitoring.
1414
1395
  * @return {?}
1415
1396
  */
1416
- ListKeyManager.prototype.setActiveItem = function (index) {
1417
- this._activeItemIndex = index;
1418
- this._activeItem = this._items.toArray()[index];
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
- * Sets the active item depending on the key event passed in.
1422
- * @param {?} event Keyboard event to be used for determining which element should be active.
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
- ListKeyManager.prototype.onKeydown = function (event) {
1426
- switch (event.keyCode) {
1427
- case DOWN_ARROW:
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
- * Sets the active item to the first enabled item in the list.
1417
+ * Register necessary event listeners on the document and window.
1477
1418
  * @return {?}
1478
1419
  */
1479
- ListKeyManager.prototype.setFirstItemActive = function () {
1480
- this._setActiveItemByIndex(0, 1);
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 active item to the last enabled item in the list.
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
- ListKeyManager.prototype.setLastItemActive = function () {
1487
- this._setActiveItemByIndex(this._items.length - 1, -1);
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 active item to the next enabled item in the list.
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
- ListKeyManager.prototype.setNextItemActive = function () {
1494
- this._activeItemIndex < 0 ? this.setFirstItemActive() : this._setActiveItemByDelta(1);
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
- * Sets the active item to a previous enabled item in the list.
1498
- * @return {?}
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
- ListKeyManager.prototype.setPreviousItemActive = function () {
1501
- this._activeItemIndex < 0 && this._wrap ? this.setLastItemActive()
1502
- : this._setActiveItemByDelta(-1);
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
- * Allows setting of the activeItemIndex without any other effects.
1506
- * @param {?} index The new activeItemIndex.
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
- ListKeyManager.prototype.updateActiveItemIndex = function (index) {
1510
- this._activeItemIndex = index;
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
- * This method sets the active item, given a list of items and the delta between the
1514
- * currently active item and the new active item. It will calculate differently
1515
- * depending on whether wrap mode is turned on.
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
- ListKeyManager.prototype._setActiveItemByDelta = function (delta, items) {
1521
- if (items === void 0) { items = this._items.toArray(); }
1522
- this._wrap ? this._setActiveInWrapMode(delta, items)
1523
- : this._setActiveInDefaultMode(delta, items);
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
- * Sets the active item properly given "wrap" mode. In other words, it will continue to move
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
- ListKeyManager.prototype._setActiveInWrapMode = function (delta, items) {
1534
- // when active item would leave menu, wrap to beginning or end
1535
- this._activeItemIndex =
1536
- (this._activeItemIndex + delta + items.length) % items.length;
1537
- // skip all disabled menu items recursively until an enabled one is reached
1538
- if (items[this._activeItemIndex].disabled) {
1539
- this._setActiveInWrapMode(delta, items);
1540
- }
1541
- else {
1542
- this.setActiveItem(this._activeItemIndex);
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
- * Sets the active item properly given the default mode. In other words, it will
1547
- * continue to move down the list until it finds an item that is not disabled. If
1548
- * it encounters either end of the list, it will stop and not wrap.
1549
- * @param {?} delta
1550
- * @param {?} items
1551
- * @return {?}
1593
+ * @param {?} _elementRef
1594
+ * @param {?} _focusMonitor
1595
+ * @param {?} renderer
1552
1596
  */
1553
- ListKeyManager.prototype._setActiveInDefaultMode = function (delta, items) {
1554
- this._setActiveItemByIndex(this._activeItemIndex + delta, delta, items);
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
- ListKeyManager.prototype._setActiveItemByIndex = function (index, fallbackDelta, items) {
1566
- if (items === void 0) { items = this._items.toArray(); }
1567
- if (!items[index]) {
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
- return ListKeyManager;
1579
- }());
1580
- var ActiveDescendantKeyManager = (function (_super) {
1581
- tslib_1.__extends(ActiveDescendantKeyManager, _super);
1582
- function ActiveDescendantKeyManager() {
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
- * This method sets the active item to the item at the specified index.
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
- ActiveDescendantKeyManager.prototype.setActiveItem = function (index) {
1593
- var _this = this;
1594
- Promise.resolve().then(function () {
1595
- if (_this.activeItem) {
1596
- _this.activeItem.setInactiveStyles();
1597
- }
1598
- _super.prototype.setActiveItem.call(_this, index);
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 ActiveDescendantKeyManager;
1605
- }(ListKeyManager));
1628
+ return CdkMonitorFocus;
1629
+ }());
1606
1630
  /**
1607
- * Screenreaders will often fire fake mousedown events when a focusable element
1608
- * is activated using the keyboard. We can typically distinguish between these faked
1609
- * mousedown events and real mousedown events using the "buttons" property. While
1610
- * real mousedowns will indicate the mouse button that was pressed (e.g. "1" for
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 isFakeMousedownFromScreenReader(event) {
1616
- return event.buttons === 0;
1637
+ function FOCUS_MONITOR_PROVIDER_FACTORY(parentDispatcher, ngZone, platform) {
1638
+ return parentDispatcher || new FocusMonitor(ngZone, platform);
1617
1639
  }
1618
- var FocusKeyManager = (function (_super) {
1619
- tslib_1.__extends(FocusKeyManager, _super);
1620
- function FocusKeyManager() {
1621
- return _super !== null && _super.apply(this, arguments) || this;
1622
- }
1623
- /**
1624
- * This method sets the active item to the item at the specified index.
1625
- * It also adds focuses the newly active item.
1626
- * @param {?} index
1627
- * @return {?}
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
- A11yModule.decorators = [
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
- export { A11yModule, 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 };
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