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