@angular/cdk 2.0.0-beta.10 → 2.0.0-beta.11

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 (170) hide show
  1. package/a11y/package.json +2 -2
  2. package/a11y/typings/aria-describer.d.ts +51 -0
  3. package/a11y/typings/aria-reference.d.ts +15 -0
  4. package/a11y/typings/focus-monitor.d.ts +106 -0
  5. package/a11y/typings/index.metadata.json +1 -1
  6. package/a11y/typings/public_api.d.ts +5 -3
  7. package/bidi/package.json +2 -2
  8. package/bundles/cdk-a11y.umd.js +639 -25
  9. package/bundles/cdk-a11y.umd.js.map +1 -1
  10. package/bundles/cdk-a11y.umd.min.js +9 -0
  11. package/bundles/cdk-a11y.umd.min.js.map +1 -0
  12. package/bundles/cdk-bidi.umd.min.js +9 -0
  13. package/bundles/cdk-bidi.umd.min.js.map +1 -0
  14. package/bundles/cdk-coercion.umd.min.js +9 -0
  15. package/bundles/cdk-coercion.umd.min.js.map +1 -0
  16. package/bundles/cdk-collections.umd.min.js +9 -0
  17. package/bundles/cdk-collections.umd.min.js.map +1 -0
  18. package/bundles/cdk-keycodes.umd.min.js +9 -0
  19. package/bundles/cdk-keycodes.umd.min.js.map +1 -0
  20. package/bundles/cdk-observers.umd.min.js +9 -0
  21. package/bundles/cdk-observers.umd.min.js.map +1 -0
  22. package/bundles/cdk-overlay.umd.js +49 -75
  23. package/bundles/cdk-overlay.umd.js.map +1 -1
  24. package/bundles/cdk-overlay.umd.min.js +9 -0
  25. package/bundles/cdk-overlay.umd.min.js.map +1 -0
  26. package/bundles/cdk-platform.umd.min.js +9 -0
  27. package/bundles/cdk-platform.umd.min.js.map +1 -0
  28. package/bundles/cdk-portal.umd.min.js +9 -0
  29. package/bundles/cdk-portal.umd.min.js.map +1 -0
  30. package/bundles/cdk-rxjs.umd.js.map +1 -1
  31. package/bundles/cdk-rxjs.umd.min.js +9 -0
  32. package/bundles/cdk-rxjs.umd.min.js.map +1 -0
  33. package/bundles/cdk-scrolling.umd.min.js +9 -0
  34. package/bundles/cdk-scrolling.umd.min.js.map +1 -0
  35. package/bundles/cdk-stepper.umd.js +460 -0
  36. package/bundles/cdk-stepper.umd.js.map +1 -0
  37. package/bundles/cdk-stepper.umd.min.js +9 -0
  38. package/bundles/cdk-stepper.umd.min.js.map +1 -0
  39. package/bundles/cdk-table.umd.js +8 -8
  40. package/bundles/cdk-table.umd.js.map +1 -1
  41. package/bundles/cdk-table.umd.min.js +9 -0
  42. package/bundles/cdk-table.umd.min.js.map +1 -0
  43. package/bundles/cdk.umd.js +1 -1
  44. package/bundles/cdk.umd.js.map +1 -1
  45. package/bundles/cdk.umd.min.js +9 -0
  46. package/bundles/cdk.umd.min.js.map +1 -0
  47. package/coercion/package.json +2 -2
  48. package/collections/package.json +2 -2
  49. package/collections/typings/selection.d.ts +3 -3
  50. package/{@angular/cdk → esm2015}/a11y.js +617 -17
  51. package/esm2015/a11y.js.map +1 -0
  52. package/{@angular/cdk → esm2015}/bidi.js +0 -0
  53. package/{@angular/cdk → esm2015}/bidi.js.map +0 -0
  54. package/{@angular → esm2015}/cdk.js +1 -1
  55. package/{@angular → esm2015}/cdk.js.map +1 -1
  56. package/{@angular/cdk → esm2015}/coercion.js +0 -0
  57. package/{@angular/cdk → esm2015}/coercion.js.map +0 -0
  58. package/{@angular/cdk → esm2015}/collections.js +0 -0
  59. package/{@angular/cdk → esm2015}/collections.js.map +0 -0
  60. package/{@angular/cdk → esm2015}/keycodes.js +0 -0
  61. package/{@angular/cdk → esm2015}/keycodes.js.map +0 -0
  62. package/{@angular/cdk → esm2015}/observers.js +0 -0
  63. package/{@angular/cdk → esm2015}/observers.js.map +0 -0
  64. package/{@angular/cdk → esm2015}/overlay.js +44 -66
  65. package/esm2015/overlay.js.map +1 -0
  66. package/{@angular/cdk → esm2015}/platform.js +0 -0
  67. package/{@angular/cdk → esm2015}/platform.js.map +0 -0
  68. package/{@angular/cdk → esm2015}/portal.js +0 -0
  69. package/{@angular/cdk → esm2015}/portal.js.map +0 -0
  70. package/{@angular/cdk → esm2015}/rxjs.js +0 -0
  71. package/esm2015/rxjs.js.map +1 -0
  72. package/{@angular/cdk → esm2015}/scrolling.js +0 -0
  73. package/{@angular/cdk → esm2015}/scrolling.js.map +0 -0
  74. package/esm2015/stepper.js +418 -0
  75. package/esm2015/stepper.js.map +1 -0
  76. package/{@angular/cdk → esm2015}/table.js +8 -8
  77. package/esm2015/table.js.map +1 -0
  78. package/{@angular/cdk → esm5}/a11y.es5.js +622 -17
  79. package/esm5/a11y.es5.js.map +1 -0
  80. package/{@angular/cdk → esm5}/bidi.es5.js +0 -0
  81. package/{@angular/cdk → esm5}/bidi.es5.js.map +0 -0
  82. package/{@angular → esm5}/cdk.es5.js +1 -1
  83. package/{@angular → esm5}/cdk.es5.js.map +1 -1
  84. package/{@angular/cdk → esm5}/coercion.es5.js +0 -0
  85. package/{@angular/cdk → esm5}/coercion.es5.js.map +0 -0
  86. package/{@angular/cdk → esm5}/collections.es5.js +0 -0
  87. package/{@angular/cdk → esm5}/collections.es5.js.map +0 -0
  88. package/{@angular/cdk → esm5}/keycodes.es5.js +0 -0
  89. package/{@angular/cdk → esm5}/keycodes.es5.js.map +0 -0
  90. package/{@angular/cdk → esm5}/observers.es5.js +0 -0
  91. package/{@angular/cdk → esm5}/observers.es5.js.map +0 -0
  92. package/{@angular/cdk → esm5}/overlay.es5.js +46 -71
  93. package/esm5/overlay.es5.js.map +1 -0
  94. package/{@angular/cdk → esm5}/platform.es5.js +0 -0
  95. package/{@angular/cdk → esm5}/platform.es5.js.map +0 -0
  96. package/{@angular/cdk → esm5}/portal.es5.js +0 -0
  97. package/{@angular/cdk → esm5}/portal.es5.js.map +0 -0
  98. package/{@angular/cdk → esm5}/rxjs.es5.js +0 -0
  99. package/esm5/rxjs.es5.js.map +1 -0
  100. package/{@angular/cdk → esm5}/scrolling.es5.js +0 -0
  101. package/{@angular/cdk → esm5}/scrolling.es5.js.map +0 -0
  102. package/esm5/stepper.es5.js +451 -0
  103. package/esm5/stepper.es5.js.map +1 -0
  104. package/{@angular/cdk → esm5}/table.es5.js +8 -8
  105. package/esm5/table.es5.js.map +1 -0
  106. package/keycodes/package.json +2 -2
  107. package/observers/package.json +2 -2
  108. package/overlay/package.json +2 -2
  109. package/overlay/typings/index.metadata.json +1 -1
  110. package/overlay/typings/{overlay-state.d.ts → overlay-config.d.ts} +5 -7
  111. package/overlay/typings/overlay-container.d.ts +1 -6
  112. package/overlay/typings/overlay-ref.d.ts +3 -3
  113. package/overlay/typings/overlay.d.ts +2 -2
  114. package/overlay/typings/position/connected-position-strategy.d.ts +1 -3
  115. package/overlay/typings/public_api.d.ts +1 -1
  116. package/overlay/typings/scroll/reposition-scroll-strategy.d.ts +1 -1
  117. package/package.json +5 -5
  118. package/platform/package.json +2 -2
  119. package/portal/package.json +2 -2
  120. package/portal/typings/portal-injector.d.ts +19 -0
  121. package/rxjs/package.json +2 -2
  122. package/rxjs/typings/rx-chain.d.ts +1 -1
  123. package/rxjs/typings/rx-operators.d.ts +1 -1
  124. package/scrolling/package.json +2 -2
  125. package/stepper/index.d.ts +8 -0
  126. package/stepper/index.metadata.json +11 -0
  127. package/stepper/package.json +7 -0
  128. package/stepper/typings/index.d.ts +4 -0
  129. package/stepper/typings/index.metadata.json +1 -0
  130. package/stepper/typings/public_api.d.ts +5 -0
  131. package/stepper/typings/step-label.d.ts +12 -0
  132. package/stepper/typings/stepper-button.d.ts +11 -0
  133. package/stepper/typings/stepper.d.ts +93 -0
  134. package/stepper.d.ts +8 -0
  135. package/stepper.metadata.json +11 -0
  136. package/table/package.json +2 -2
  137. package/table/typings/index.metadata.json +1 -1
  138. package/typings/a11y/aria-describer.d.ts +51 -0
  139. package/typings/a11y/aria-reference.d.ts +15 -0
  140. package/typings/a11y/focus-monitor.d.ts +106 -0
  141. package/typings/a11y/index.metadata.json +1 -1
  142. package/typings/a11y/public_api.d.ts +5 -3
  143. package/typings/collections/selection.d.ts +3 -3
  144. package/typings/index.metadata.json +1 -1
  145. package/typings/overlay/index.metadata.json +1 -1
  146. package/typings/overlay/{overlay-state.d.ts → overlay-config.d.ts} +5 -7
  147. package/typings/overlay/overlay-container.d.ts +1 -6
  148. package/typings/overlay/overlay-ref.d.ts +3 -3
  149. package/typings/overlay/overlay.d.ts +2 -2
  150. package/typings/overlay/position/connected-position-strategy.d.ts +1 -3
  151. package/typings/overlay/public_api.d.ts +1 -1
  152. package/typings/overlay/scroll/reposition-scroll-strategy.d.ts +1 -1
  153. package/typings/portal/portal-injector.d.ts +19 -0
  154. package/typings/rxjs/rx-chain.d.ts +1 -1
  155. package/typings/rxjs/rx-operators.d.ts +1 -1
  156. package/typings/stepper/index.d.ts +4 -0
  157. package/typings/stepper/index.metadata.json +1 -0
  158. package/typings/stepper/public_api.d.ts +5 -0
  159. package/typings/stepper/step-label.d.ts +12 -0
  160. package/typings/stepper/stepper-button.d.ts +11 -0
  161. package/typings/stepper/stepper.d.ts +93 -0
  162. package/typings/table/index.metadata.json +1 -1
  163. package/@angular/cdk/a11y.es5.js.map +0 -1
  164. package/@angular/cdk/a11y.js.map +0 -1
  165. package/@angular/cdk/overlay.es5.js.map +0 -1
  166. package/@angular/cdk/overlay.js.map +0 -1
  167. package/@angular/cdk/rxjs.es5.js.map +0 -1
  168. package/@angular/cdk/rxjs.js.map +0 -1
  169. package/@angular/cdk/table.es5.js.map +0 -1
  170. package/@angular/cdk/table.js.map +0 -1
@@ -5,12 +5,14 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.io/license
7
7
  */
8
- import { Directive, ElementRef, Inject, Injectable, InjectionToken, Input, NgModule, NgZone, Optional, SkipSelf } from '@angular/core';
8
+ import { Directive, ElementRef, EventEmitter, Inject, Injectable, InjectionToken, Input, NgModule, NgZone, Optional, Output, Renderer2, SkipSelf } from '@angular/core';
9
9
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
10
10
  import { Platform, PlatformModule } from '@angular/cdk/platform';
11
11
  import { RxChain, debounceTime, doOperator, filter, first, map } from '@angular/cdk/rxjs';
12
12
  import { CommonModule } from '@angular/common';
13
13
  import { Subject } from 'rxjs/Subject';
14
+ import { of } from 'rxjs/observable/of';
15
+ import { Subscription } from 'rxjs/Subscription';
14
16
  import { A, DOWN_ARROW, NINE, TAB, UP_ARROW, Z, ZERO } from '@angular/cdk/keycodes';
15
17
 
16
18
  /**
@@ -493,7 +495,7 @@ class FocusTrap {
493
495
  fn();
494
496
  }
495
497
  else {
496
- first.call(this._ngZone.onStable).subscribe(fn);
498
+ first.call(this._ngZone.onStable.asObservable()).subscribe(fn);
497
499
  }
498
500
  }
499
501
  }
@@ -725,17 +727,596 @@ const LIVE_ANNOUNCER_PROVIDER = {
725
727
  };
726
728
 
727
729
  /**
728
- * Screenreaders will often fire fake mousedown events when a focusable element
729
- * is activated using the keyboard. We can typically distinguish between these faked
730
- * mousedown events and real mousedown events using the "buttons" property. While
731
- * real mousedowns will indicate the mouse button that was pressed (e.g. "1" for
732
- * the left mouse button), faked mousedowns will usually set the property value to 0.
733
- * @param {?} event
730
+ * IDs are deliminated by an empty space, as per the spec.
731
+ */
732
+ const ID_DELIMINATOR = ' ';
733
+ /**
734
+ * Adds the given ID to the specified ARIA attribute on an element.
735
+ * Used for attributes such as aria-labelledby, aria-owns, etc.
736
+ * @param {?} el
737
+ * @param {?} attr
738
+ * @param {?} id
734
739
  * @return {?}
735
740
  */
736
- function isFakeMousedownFromScreenReader(event) {
737
- return event.buttons === 0;
741
+ function addAriaReferencedId(el, attr, id) {
742
+ const /** @type {?} */ ids = getAriaReferenceIds(el, attr);
743
+ if (ids.some(existingId => existingId.trim() == id.trim())) {
744
+ return;
745
+ }
746
+ ids.push(id.trim());
747
+ el.setAttribute(attr, ids.join(ID_DELIMINATOR));
748
+ }
749
+ /**
750
+ * Removes the given ID from the specified ARIA attribute on an element.
751
+ * Used for attributes such as aria-labelledby, aria-owns, etc.
752
+ * @param {?} el
753
+ * @param {?} attr
754
+ * @param {?} id
755
+ * @return {?}
756
+ */
757
+ function removeAriaReferencedId(el, attr, id) {
758
+ const /** @type {?} */ ids = getAriaReferenceIds(el, attr);
759
+ const /** @type {?} */ filteredIds = ids.filter(val => val != id.trim());
760
+ el.setAttribute(attr, filteredIds.join(ID_DELIMINATOR));
761
+ }
762
+ /**
763
+ * Gets the list of IDs referenced by the given ARIA attribute on an element.
764
+ * Used for attributes such as aria-labelledby, aria-owns, etc.
765
+ * @param {?} el
766
+ * @param {?} attr
767
+ * @return {?}
768
+ */
769
+ function getAriaReferenceIds(el, attr) {
770
+ // Get string array of all individual ids (whitespace deliminated) in the attribute value
771
+ return (el.getAttribute(attr) || '').match(/\S+/g) || [];
772
+ }
773
+
774
+ /**
775
+ * ID used for the body container where all messages are appended.
776
+ */
777
+ const MESSAGES_CONTAINER_ID = 'cdk-describedby-message-container';
778
+ /**
779
+ * ID prefix used for each created message element.
780
+ */
781
+ const CDK_DESCRIBEDBY_ID_PREFIX = 'cdk-describedby-message';
782
+ /**
783
+ * Attribute given to each host element that is described by a message element.
784
+ */
785
+ const CDK_DESCRIBEDBY_HOST_ATTRIBUTE = 'cdk-describedby-host';
786
+ /**
787
+ * Global incremental identifier for each registered message element.
788
+ */
789
+ let nextId = 0;
790
+ /**
791
+ * Global map of all registered message elements that have been placed into the document.
792
+ */
793
+ const messageRegistry = new Map();
794
+ /**
795
+ * Container for all registered messages.
796
+ */
797
+ let messagesContainer = null;
798
+ /**
799
+ * Utility that creates visually hidden elements with a message content. Useful for elements that
800
+ * want to use aria-describedby to further describe themselves without adding additional visual
801
+ * content.
802
+ * \@docs-private
803
+ */
804
+ class AriaDescriber {
805
+ /**
806
+ * @param {?} _platform
807
+ */
808
+ constructor(_platform) {
809
+ this._platform = _platform;
810
+ }
811
+ /**
812
+ * Adds to the host element an aria-describedby reference to a hidden element that contains
813
+ * the message. If the same message has already been registered, then it will reuse the created
814
+ * message element.
815
+ * @param {?} hostElement
816
+ * @param {?} message
817
+ * @return {?}
818
+ */
819
+ describe(hostElement, message) {
820
+ if (!this._platform.isBrowser || !message.trim()) {
821
+ return;
822
+ }
823
+ if (!messageRegistry.has(message)) {
824
+ createMessageElement(message);
825
+ }
826
+ if (!isElementDescribedByMessage(hostElement, message)) {
827
+ addMessageReference(hostElement, message);
828
+ }
829
+ }
830
+ /**
831
+ * Removes the host element's aria-describedby reference to the message element.
832
+ * @param {?} hostElement
833
+ * @param {?} message
834
+ * @return {?}
835
+ */
836
+ removeDescription(hostElement, message) {
837
+ if (!this._platform.isBrowser || !message.trim()) {
838
+ return;
839
+ }
840
+ if (isElementDescribedByMessage(hostElement, message)) {
841
+ removeMessageReference(hostElement, message);
842
+ }
843
+ const /** @type {?} */ registeredMessage = messageRegistry.get(message);
844
+ if (registeredMessage && registeredMessage.referenceCount === 0) {
845
+ deleteMessageElement(message);
846
+ }
847
+ if (messagesContainer && messagesContainer.childNodes.length === 0) {
848
+ deleteMessagesContainer();
849
+ }
850
+ }
851
+ /**
852
+ * Unregisters all created message elements and removes the message container.
853
+ * @return {?}
854
+ */
855
+ ngOnDestroy() {
856
+ if (!this._platform.isBrowser) {
857
+ return;
858
+ }
859
+ const /** @type {?} */ describedElements = document.querySelectorAll(`[${CDK_DESCRIBEDBY_HOST_ATTRIBUTE}]`);
860
+ for (let /** @type {?} */ i = 0; i < describedElements.length; i++) {
861
+ removeCdkDescribedByReferenceIds(describedElements[i]);
862
+ describedElements[i].removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE);
863
+ }
864
+ if (messagesContainer) {
865
+ deleteMessagesContainer();
866
+ }
867
+ messageRegistry.clear();
868
+ }
869
+ }
870
+ AriaDescriber.decorators = [
871
+ { type: Injectable },
872
+ ];
873
+ /**
874
+ * @nocollapse
875
+ */
876
+ AriaDescriber.ctorParameters = () => [
877
+ { type: Platform, },
878
+ ];
879
+ /**
880
+ * Creates a new element in the visually hidden message container element with the message
881
+ * as its content and adds it to the message registry.
882
+ * @param {?} message
883
+ * @return {?}
884
+ */
885
+ function createMessageElement(message) {
886
+ const /** @type {?} */ messageElement = document.createElement('div');
887
+ messageElement.setAttribute('id', `${CDK_DESCRIBEDBY_ID_PREFIX}-${nextId++}`);
888
+ messageElement.appendChild(/** @type {?} */ ((document.createTextNode(message))));
889
+ if (!messagesContainer) {
890
+ createMessagesContainer();
891
+ } /** @type {?} */
892
+ ((messagesContainer)).appendChild(messageElement);
893
+ messageRegistry.set(message, { messageElement, referenceCount: 0 });
894
+ }
895
+ /**
896
+ * Deletes the message element from the global messages container.
897
+ * @param {?} message
898
+ * @return {?}
899
+ */
900
+ function deleteMessageElement(message) {
901
+ const /** @type {?} */ registeredMessage = messageRegistry.get(message);
902
+ const /** @type {?} */ messageElement = registeredMessage && registeredMessage.messageElement;
903
+ if (messagesContainer && messageElement) {
904
+ messagesContainer.removeChild(messageElement);
905
+ }
906
+ messageRegistry.delete(message);
907
+ }
908
+ /**
909
+ * Creates the global container for all aria-describedby messages.
910
+ * @return {?}
911
+ */
912
+ function createMessagesContainer() {
913
+ messagesContainer = document.createElement('div');
914
+ messagesContainer.setAttribute('id', MESSAGES_CONTAINER_ID);
915
+ messagesContainer.setAttribute('aria-hidden', 'true');
916
+ messagesContainer.style.display = 'none';
917
+ document.body.appendChild(messagesContainer);
918
+ }
919
+ /**
920
+ * Deletes the global messages container.
921
+ * @return {?}
922
+ */
923
+ function deleteMessagesContainer() {
924
+ document.body.removeChild(/** @type {?} */ ((messagesContainer)));
925
+ messagesContainer = null;
926
+ }
927
+ /**
928
+ * Removes all cdk-describedby messages that are hosted through the element.
929
+ * @param {?} element
930
+ * @return {?}
931
+ */
932
+ function removeCdkDescribedByReferenceIds(element) {
933
+ // Remove all aria-describedby reference IDs that are prefixed by CDK_DESCRIBEDBY_ID_PREFIX
934
+ const /** @type {?} */ originalReferenceIds = getAriaReferenceIds(element, 'aria-describedby')
935
+ .filter(id => id.indexOf(CDK_DESCRIBEDBY_ID_PREFIX) != 0);
936
+ element.setAttribute('aria-describedby', originalReferenceIds.join(' '));
937
+ }
938
+ /**
939
+ * Adds a message reference to the element using aria-describedby and increments the registered
940
+ * message's reference count.
941
+ * @param {?} element
942
+ * @param {?} message
943
+ * @return {?}
944
+ */
945
+ function addMessageReference(element, message) {
946
+ const /** @type {?} */ registeredMessage = ((messageRegistry.get(message)));
947
+ // Add the aria-describedby reference and set the describedby_host attribute to mark the element.
948
+ addAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id);
949
+ element.setAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE, '');
950
+ registeredMessage.referenceCount++;
951
+ }
952
+ /**
953
+ * Removes a message reference from the element using aria-describedby and decrements the registered
954
+ * message's reference count.
955
+ * @param {?} element
956
+ * @param {?} message
957
+ * @return {?}
958
+ */
959
+ function removeMessageReference(element, message) {
960
+ const /** @type {?} */ registeredMessage = ((messageRegistry.get(message)));
961
+ registeredMessage.referenceCount--;
962
+ removeAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id);
963
+ element.removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE);
964
+ }
965
+ /**
966
+ * Returns true if the element has been described by the provided message ID.
967
+ * @param {?} element
968
+ * @param {?} message
969
+ * @return {?}
970
+ */
971
+ function isElementDescribedByMessage(element, message) {
972
+ const /** @type {?} */ referenceIds = getAriaReferenceIds(element, 'aria-describedby');
973
+ const /** @type {?} */ registeredMessage = messageRegistry.get(message);
974
+ const /** @type {?} */ messageId = registeredMessage && registeredMessage.messageElement.id;
975
+ return !!messageId && referenceIds.indexOf(messageId) != -1;
976
+ }
977
+ /**
978
+ * \@docs-private
979
+ * @param {?} parentDispatcher
980
+ * @param {?} platform
981
+ * @return {?}
982
+ */
983
+ function ARIA_DESCRIBER_PROVIDER_FACTORY(parentDispatcher, platform) {
984
+ return parentDispatcher || new AriaDescriber(platform);
985
+ }
986
+ /**
987
+ * \@docs-private
988
+ */
989
+ const ARIA_DESCRIBER_PROVIDER = {
990
+ // If there is already an AriaDescriber available, use that. Otherwise, provide a new one.
991
+ provide: AriaDescriber,
992
+ deps: [
993
+ [new Optional(), new SkipSelf(), AriaDescriber],
994
+ Platform
995
+ ],
996
+ useFactory: ARIA_DESCRIBER_PROVIDER_FACTORY
997
+ };
998
+
999
+ // This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found
1000
+ // that a value of around 650ms seems appropriate.
1001
+ const TOUCH_BUFFER_MS = 650;
1002
+ /**
1003
+ * Monitors mouse and keyboard events to determine the cause of focus events.
1004
+ */
1005
+ class FocusMonitor {
1006
+ /**
1007
+ * @param {?} _ngZone
1008
+ * @param {?} _platform
1009
+ */
1010
+ constructor(_ngZone, _platform) {
1011
+ this._ngZone = _ngZone;
1012
+ this._platform = _platform;
1013
+ /**
1014
+ * The focus origin that the next focus event is a result of.
1015
+ */
1016
+ this._origin = null;
1017
+ /**
1018
+ * Whether the window has just been focused.
1019
+ */
1020
+ this._windowFocused = false;
1021
+ /**
1022
+ * Weak map of elements being monitored to their info.
1023
+ */
1024
+ this._elementInfo = new WeakMap();
1025
+ this._ngZone.runOutsideAngular(() => this._registerDocumentEvents());
1026
+ }
1027
+ /**
1028
+ * Monitors focus on an element and applies appropriate CSS classes.
1029
+ * @param {?} element The element to monitor
1030
+ * @param {?} renderer The renderer to use to apply CSS classes to the element.
1031
+ * @param {?} checkChildren Whether to count the element as focused when its children are focused.
1032
+ * @return {?} An observable that emits when the focus state of the element changes.
1033
+ * When the element is blurred, null will be emitted.
1034
+ */
1035
+ monitor(element, renderer, checkChildren) {
1036
+ // Do nothing if we're not on the browser platform.
1037
+ if (!this._platform.isBrowser) {
1038
+ return of(null);
1039
+ }
1040
+ // Check if we're already monitoring this element.
1041
+ if (this._elementInfo.has(element)) {
1042
+ let /** @type {?} */ cachedInfo = this._elementInfo.get(element); /** @type {?} */
1043
+ ((cachedInfo)).checkChildren = checkChildren;
1044
+ return ((cachedInfo)).subject.asObservable();
1045
+ }
1046
+ // Create monitored element info.
1047
+ let /** @type {?} */ info = {
1048
+ unlisten: () => { },
1049
+ checkChildren: checkChildren,
1050
+ renderer: renderer,
1051
+ subject: new Subject()
1052
+ };
1053
+ this._elementInfo.set(element, info);
1054
+ // Start listening. We need to listen in capture phase since focus events don't bubble.
1055
+ let /** @type {?} */ focusListener = (event) => this._onFocus(event, element);
1056
+ let /** @type {?} */ blurListener = (event) => this._onBlur(event, element);
1057
+ this._ngZone.runOutsideAngular(() => {
1058
+ element.addEventListener('focus', focusListener, true);
1059
+ element.addEventListener('blur', blurListener, true);
1060
+ });
1061
+ // Create an unlisten function for later.
1062
+ info.unlisten = () => {
1063
+ element.removeEventListener('focus', focusListener, true);
1064
+ element.removeEventListener('blur', blurListener, true);
1065
+ };
1066
+ return info.subject.asObservable();
1067
+ }
1068
+ /**
1069
+ * Stops monitoring an element and removes all focus classes.
1070
+ * @param {?} element The element to stop monitoring.
1071
+ * @return {?}
1072
+ */
1073
+ stopMonitoring(element) {
1074
+ let /** @type {?} */ elementInfo = this._elementInfo.get(element);
1075
+ if (elementInfo) {
1076
+ elementInfo.unlisten();
1077
+ elementInfo.subject.complete();
1078
+ this._setClasses(element);
1079
+ this._elementInfo.delete(element);
1080
+ }
1081
+ }
1082
+ /**
1083
+ * Focuses the element via the specified focus origin.
1084
+ * @param {?} element The element to focus.
1085
+ * @param {?} origin The focus origin.
1086
+ * @return {?}
1087
+ */
1088
+ focusVia(element, origin) {
1089
+ this._setOriginForCurrentEventQueue(origin);
1090
+ element.focus();
1091
+ }
1092
+ /**
1093
+ * Register necessary event listeners on the document and window.
1094
+ * @return {?}
1095
+ */
1096
+ _registerDocumentEvents() {
1097
+ // Do nothing if we're not on the browser platform.
1098
+ if (!this._platform.isBrowser) {
1099
+ return;
1100
+ }
1101
+ // Note: we listen to events in the capture phase so we can detect them even if the user stops
1102
+ // propagation.
1103
+ // On keydown record the origin and clear any touch event that may be in progress.
1104
+ document.addEventListener('keydown', () => {
1105
+ this._lastTouchTarget = null;
1106
+ this._setOriginForCurrentEventQueue('keyboard');
1107
+ }, true);
1108
+ // On mousedown record the origin only if there is not touch target, since a mousedown can
1109
+ // happen as a result of a touch event.
1110
+ document.addEventListener('mousedown', () => {
1111
+ if (!this._lastTouchTarget) {
1112
+ this._setOriginForCurrentEventQueue('mouse');
1113
+ }
1114
+ }, true);
1115
+ // When the touchstart event fires the focus event is not yet in the event queue. This means
1116
+ // we can't rely on the trick used above (setting timeout of 0ms). Instead we wait 650ms to
1117
+ // see if a focus happens.
1118
+ document.addEventListener('touchstart', (event) => {
1119
+ if (this._touchTimeout != null) {
1120
+ clearTimeout(this._touchTimeout);
1121
+ }
1122
+ this._lastTouchTarget = event.target;
1123
+ this._touchTimeout = setTimeout(() => this._lastTouchTarget = null, TOUCH_BUFFER_MS);
1124
+ }, true);
1125
+ // Make a note of when the window regains focus, so we can restore the origin info for the
1126
+ // focused element.
1127
+ window.addEventListener('focus', () => {
1128
+ this._windowFocused = true;
1129
+ setTimeout(() => this._windowFocused = false, 0);
1130
+ });
1131
+ }
1132
+ /**
1133
+ * Sets the focus classes on the element based on the given focus origin.
1134
+ * @param {?} element The element to update the classes on.
1135
+ * @param {?=} origin The focus origin.
1136
+ * @return {?}
1137
+ */
1138
+ _setClasses(element, origin) {
1139
+ const /** @type {?} */ elementInfo = this._elementInfo.get(element);
1140
+ if (elementInfo) {
1141
+ const /** @type {?} */ toggleClass = (className, shouldSet) => {
1142
+ shouldSet ? elementInfo.renderer.addClass(element, className) :
1143
+ elementInfo.renderer.removeClass(element, className);
1144
+ };
1145
+ toggleClass('cdk-focused', !!origin);
1146
+ toggleClass('cdk-touch-focused', origin === 'touch');
1147
+ toggleClass('cdk-keyboard-focused', origin === 'keyboard');
1148
+ toggleClass('cdk-mouse-focused', origin === 'mouse');
1149
+ toggleClass('cdk-program-focused', origin === 'program');
1150
+ }
1151
+ }
1152
+ /**
1153
+ * Sets the origin and schedules an async function to clear it at the end of the event queue.
1154
+ * @param {?} origin The origin to set.
1155
+ * @return {?}
1156
+ */
1157
+ _setOriginForCurrentEventQueue(origin) {
1158
+ this._origin = origin;
1159
+ setTimeout(() => this._origin = null, 0);
1160
+ }
1161
+ /**
1162
+ * Checks whether the given focus event was caused by a touchstart event.
1163
+ * @param {?} event The focus event to check.
1164
+ * @return {?} Whether the event was caused by a touch.
1165
+ */
1166
+ _wasCausedByTouch(event) {
1167
+ // Note(mmalerba): This implementation is not quite perfect, there is a small edge case.
1168
+ // Consider the following dom structure:
1169
+ //
1170
+ // <div #parent tabindex="0" cdkFocusClasses>
1171
+ // <div #child (click)="#parent.focus()"></div>
1172
+ // </div>
1173
+ //
1174
+ // If the user touches the #child element and the #parent is programmatically focused as a
1175
+ // result, this code will still consider it to have been caused by the touch event and will
1176
+ // apply the cdk-touch-focused class rather than the cdk-program-focused class. This is a
1177
+ // relatively small edge-case that can be worked around by using
1178
+ // focusVia(parentEl, renderer, 'program') to focus the parent element.
1179
+ //
1180
+ // If we decide that we absolutely must handle this case correctly, we can do so by listening
1181
+ // for the first focus event after the touchstart, and then the first blur event after that
1182
+ // focus event. When that blur event fires we know that whatever follows is not a result of the
1183
+ // touchstart.
1184
+ let /** @type {?} */ focusTarget = event.target;
1185
+ return this._lastTouchTarget instanceof Node && focusTarget instanceof Node &&
1186
+ (focusTarget === this._lastTouchTarget || focusTarget.contains(this._lastTouchTarget));
1187
+ }
1188
+ /**
1189
+ * Handles focus events on a registered element.
1190
+ * @param {?} event The focus event.
1191
+ * @param {?} element The monitored element.
1192
+ * @return {?}
1193
+ */
1194
+ _onFocus(event, element) {
1195
+ // NOTE(mmalerba): We currently set the classes based on the focus origin of the most recent
1196
+ // focus event affecting the monitored element. If we want to use the origin of the first event
1197
+ // instead we should check for the cdk-focused class here and return if the element already has
1198
+ // it. (This only matters for elements that have includesChildren = true).
1199
+ // If we are not counting child-element-focus as focused, make sure that the event target is the
1200
+ // monitored element itself.
1201
+ const /** @type {?} */ elementInfo = this._elementInfo.get(element);
1202
+ if (!elementInfo || (!elementInfo.checkChildren && element !== event.target)) {
1203
+ return;
1204
+ }
1205
+ // If we couldn't detect a cause for the focus event, it's due to one of three reasons:
1206
+ // 1) The window has just regained focus, in which case we want to restore the focused state of
1207
+ // the element from before the window blurred.
1208
+ // 2) It was caused by a touch event, in which case we mark the origin as 'touch'.
1209
+ // 3) The element was programmatically focused, in which case we should mark the origin as
1210
+ // 'program'.
1211
+ if (!this._origin) {
1212
+ if (this._windowFocused && this._lastFocusOrigin) {
1213
+ this._origin = this._lastFocusOrigin;
1214
+ }
1215
+ else if (this._wasCausedByTouch(event)) {
1216
+ this._origin = 'touch';
1217
+ }
1218
+ else {
1219
+ this._origin = 'program';
1220
+ }
1221
+ }
1222
+ this._setClasses(element, this._origin);
1223
+ elementInfo.subject.next(this._origin);
1224
+ this._lastFocusOrigin = this._origin;
1225
+ this._origin = null;
1226
+ }
1227
+ /**
1228
+ * Handles blur events on a registered element.
1229
+ * @param {?} event The blur event.
1230
+ * @param {?} element The monitored element.
1231
+ * @return {?}
1232
+ */
1233
+ _onBlur(event, element) {
1234
+ // If we are counting child-element-focus as focused, make sure that we aren't just blurring in
1235
+ // order to focus another child of the monitored element.
1236
+ const /** @type {?} */ elementInfo = this._elementInfo.get(element);
1237
+ if (!elementInfo || (elementInfo.checkChildren && event.relatedTarget instanceof Node &&
1238
+ element.contains(event.relatedTarget))) {
1239
+ return;
1240
+ }
1241
+ this._setClasses(element);
1242
+ elementInfo.subject.next(null);
1243
+ }
1244
+ }
1245
+ FocusMonitor.decorators = [
1246
+ { type: Injectable },
1247
+ ];
1248
+ /**
1249
+ * @nocollapse
1250
+ */
1251
+ FocusMonitor.ctorParameters = () => [
1252
+ { type: NgZone, },
1253
+ { type: Platform, },
1254
+ ];
1255
+ /**
1256
+ * Directive that determines how a particular element was focused (via keyboard, mouse, touch, or
1257
+ * programmatically) and adds corresponding classes to the element.
1258
+ *
1259
+ * There are two variants of this directive:
1260
+ * 1) cdkMonitorElementFocus: does not consider an element to be focused if one of its children is
1261
+ * focused.
1262
+ * 2) cdkMonitorSubtreeFocus: considers an element focused if it or any of its children are focused.
1263
+ */
1264
+ class CdkMonitorFocus {
1265
+ /**
1266
+ * @param {?} _elementRef
1267
+ * @param {?} _focusMonitor
1268
+ * @param {?} renderer
1269
+ */
1270
+ constructor(_elementRef, _focusMonitor, renderer) {
1271
+ this._elementRef = _elementRef;
1272
+ this._focusMonitor = _focusMonitor;
1273
+ this.cdkFocusChange = new EventEmitter();
1274
+ this._monitorSubscription = this._focusMonitor.monitor(this._elementRef.nativeElement, renderer, this._elementRef.nativeElement.hasAttribute('cdkMonitorSubtreeFocus'))
1275
+ .subscribe(origin => this.cdkFocusChange.emit(origin));
1276
+ }
1277
+ /**
1278
+ * @return {?}
1279
+ */
1280
+ ngOnDestroy() {
1281
+ this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);
1282
+ this._monitorSubscription.unsubscribe();
1283
+ }
738
1284
  }
1285
+ CdkMonitorFocus.decorators = [
1286
+ { type: Directive, args: [{
1287
+ selector: '[cdkMonitorElementFocus], [cdkMonitorSubtreeFocus]',
1288
+ },] },
1289
+ ];
1290
+ /**
1291
+ * @nocollapse
1292
+ */
1293
+ CdkMonitorFocus.ctorParameters = () => [
1294
+ { type: ElementRef, },
1295
+ { type: FocusMonitor, },
1296
+ { type: Renderer2, },
1297
+ ];
1298
+ CdkMonitorFocus.propDecorators = {
1299
+ 'cdkFocusChange': [{ type: Output },],
1300
+ };
1301
+ /**
1302
+ * \@docs-private
1303
+ * @param {?} parentDispatcher
1304
+ * @param {?} ngZone
1305
+ * @param {?} platform
1306
+ * @return {?}
1307
+ */
1308
+ function FOCUS_MONITOR_PROVIDER_FACTORY(parentDispatcher, ngZone, platform) {
1309
+ return parentDispatcher || new FocusMonitor(ngZone, platform);
1310
+ }
1311
+ /**
1312
+ * \@docs-private
1313
+ */
1314
+ const FOCUS_MONITOR_PROVIDER = {
1315
+ // If there is already a FocusMonitor available, use that. Otherwise, provide a new one.
1316
+ provide: FocusMonitor,
1317
+ deps: [[new Optional(), new SkipSelf(), FocusMonitor], NgZone, Platform],
1318
+ useFactory: FOCUS_MONITOR_PROVIDER_FACTORY
1319
+ };
739
1320
 
740
1321
  /**
741
1322
  * This class manages keyboard events for selectable lists. If you pass it a query list
@@ -750,6 +1331,7 @@ class ListKeyManager {
750
1331
  this._activeItemIndex = -1;
751
1332
  this._wrap = false;
752
1333
  this._letterKeyStream = new Subject();
1334
+ this._typeaheadSubscription = Subscription.EMPTY;
753
1335
  this._pressedLetters = [];
754
1336
  /**
755
1337
  * Stream that emits any time the TAB key is pressed, so components can react
@@ -775,9 +1357,7 @@ class ListKeyManager {
775
1357
  if (this._items.length && this._items.some(item => typeof item.getLabel !== 'function')) {
776
1358
  throw Error('ListKeyManager items in typeahead mode must implement the `getLabel` method.');
777
1359
  }
778
- if (this._typeaheadSubscription) {
779
- this._typeaheadSubscription.unsubscribe();
780
- }
1360
+ this._typeaheadSubscription.unsubscribe();
781
1361
  // Debounce the presses of non-navigational keys, collect the ones that correspond to letters
782
1362
  // and convert those letters back into a string. Afterwards find the first item that starts
783
1363
  // with that string and select it.
@@ -978,6 +1558,19 @@ class ActiveDescendantKeyManager extends ListKeyManager {
978
1558
  }
979
1559
  }
980
1560
 
1561
+ /**
1562
+ * Screenreaders will often fire fake mousedown events when a focusable element
1563
+ * is activated using the keyboard. We can typically distinguish between these faked
1564
+ * mousedown events and real mousedown events using the "buttons" property. While
1565
+ * real mousedowns will indicate the mouse button that was pressed (e.g. "1" for
1566
+ * the left mouse button), faked mousedowns will usually set the property value to 0.
1567
+ * @param {?} event
1568
+ * @return {?}
1569
+ */
1570
+ function isFakeMousedownFromScreenReader(event) {
1571
+ return event.buttons === 0;
1572
+ }
1573
+
981
1574
  class FocusKeyManager extends ListKeyManager {
982
1575
  /**
983
1576
  * This method sets the active item to the item at the specified index.
@@ -998,9 +1591,16 @@ class A11yModule {
998
1591
  A11yModule.decorators = [
999
1592
  { type: NgModule, args: [{
1000
1593
  imports: [CommonModule, PlatformModule],
1001
- declarations: [FocusTrapDirective, FocusTrapDeprecatedDirective],
1002
- exports: [FocusTrapDirective, FocusTrapDeprecatedDirective],
1003
- providers: [InteractivityChecker, FocusTrapFactory, LIVE_ANNOUNCER_PROVIDER]
1594
+ declarations: [FocusTrapDirective, FocusTrapDeprecatedDirective, CdkMonitorFocus],
1595
+ exports: [FocusTrapDirective, FocusTrapDeprecatedDirective, CdkMonitorFocus],
1596
+ providers: [
1597
+ InteractivityChecker,
1598
+ FocusTrapFactory,
1599
+ AriaDescriber,
1600
+ LIVE_ANNOUNCER_PROVIDER,
1601
+ ARIA_DESCRIBER_PROVIDER,
1602
+ FOCUS_MONITOR_PROVIDER,
1603
+ ]
1004
1604
  },] },
1005
1605
  ];
1006
1606
  /**
@@ -1012,5 +1612,5 @@ A11yModule.ctorParameters = () => [];
1012
1612
  * Generated bundle index. Do not edit.
1013
1613
  */
1014
1614
 
1015
- export { A11yModule, LIVE_ANNOUNCER_ELEMENT_TOKEN, LiveAnnouncer, LIVE_ANNOUNCER_PROVIDER_FACTORY, LIVE_ANNOUNCER_PROVIDER, isFakeMousedownFromScreenReader, FocusTrap, FocusTrapFactory, FocusTrapDeprecatedDirective, FocusTrapDirective, InteractivityChecker, ListKeyManager, ActiveDescendantKeyManager, FocusKeyManager };
1615
+ export { A11yModule, ActiveDescendantKeyManager, MESSAGES_CONTAINER_ID, CDK_DESCRIBEDBY_ID_PREFIX, CDK_DESCRIBEDBY_HOST_ATTRIBUTE, AriaDescriber, ARIA_DESCRIBER_PROVIDER_FACTORY, ARIA_DESCRIBER_PROVIDER, isFakeMousedownFromScreenReader, FocusKeyManager, FocusTrap, FocusTrapFactory, FocusTrapDeprecatedDirective, FocusTrapDirective, InteractivityChecker, ListKeyManager, LIVE_ANNOUNCER_ELEMENT_TOKEN, LiveAnnouncer, LIVE_ANNOUNCER_PROVIDER_FACTORY, LIVE_ANNOUNCER_PROVIDER, TOUCH_BUFFER_MS, FocusMonitor, CdkMonitorFocus, FOCUS_MONITOR_PROVIDER_FACTORY, FOCUS_MONITOR_PROVIDER };
1016
1616
  //# sourceMappingURL=a11y.js.map