platform.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. /**
  2. * @license
  3. * Copyright Google LLC All Rights Reserved.
  4. *
  5. * Use of this source code is governed by an MIT-style license that can be
  6. * found in the LICENSE file at https://angular.io/license
  7. */
  8. import { Inject, Injectable, Optional, PLATFORM_ID, NgModule, ɵɵdefineInjectable, ɵɵinject } from '@angular/core';
  9. import { isPlatformBrowser } from '@angular/common';
  10. /**
  11. * @fileoverview added by tsickle
  12. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  13. */
  14. // Whether the current platform supports the V8 Break Iterator. The V8 check
  15. // is necessary to detect all Blink based browsers.
  16. /** @type {?} */
  17. let hasV8BreakIterator;
  18. // We need a try/catch around the reference to `Intl`, because accessing it in some cases can
  19. // cause IE to throw. These cases are tied to particular versions of Windows and can happen if
  20. // the consumer is providing a polyfilled `Map`. See:
  21. // https://github.com/Microsoft/ChakraCore/issues/3189
  22. // https://github.com/angular/components/issues/15687
  23. try {
  24. hasV8BreakIterator = (typeof Intl !== 'undefined' && ((/** @type {?} */ (Intl))).v8BreakIterator);
  25. }
  26. catch (_a) {
  27. hasV8BreakIterator = false;
  28. }
  29. /**
  30. * Service to detect the current platform by comparing the userAgent strings and
  31. * checking browser-specific global properties.
  32. */
  33. class Platform {
  34. /**
  35. * \@breaking-change 8.0.0 remove optional decorator
  36. * @param {?=} _platformId
  37. */
  38. constructor(_platformId) {
  39. this._platformId = _platformId;
  40. /**
  41. * Whether the Angular application is being rendered in the browser.
  42. * We want to use the Angular platform check because if the Document is shimmed
  43. * without the navigator, the following checks will fail. This is preferred because
  44. * sometimes the Document may be shimmed without the user's knowledge or intention
  45. */
  46. this.isBrowser = this._platformId ?
  47. isPlatformBrowser(this._platformId) : typeof document === 'object' && !!document;
  48. /**
  49. * Whether the current browser is Microsoft Edge.
  50. */
  51. this.EDGE = this.isBrowser && /(edge)/i.test(navigator.userAgent);
  52. /**
  53. * Whether the current rendering engine is Microsoft Trident.
  54. */
  55. this.TRIDENT = this.isBrowser && /(msie|trident)/i.test(navigator.userAgent);
  56. /**
  57. * Whether the current rendering engine is Blink.
  58. */
  59. // EdgeHTML and Trident mock Blink specific things and need to be excluded from this check.
  60. this.BLINK = this.isBrowser && (!!(((/** @type {?} */ (window))).chrome || hasV8BreakIterator) &&
  61. typeof CSS !== 'undefined' && !this.EDGE && !this.TRIDENT);
  62. /**
  63. * Whether the current rendering engine is WebKit.
  64. */
  65. // Webkit is part of the userAgent in EdgeHTML, Blink and Trident. Therefore we need to
  66. // ensure that Webkit runs standalone and is not used as another engine's base.
  67. this.WEBKIT = this.isBrowser &&
  68. /AppleWebKit/i.test(navigator.userAgent) && !this.BLINK && !this.EDGE && !this.TRIDENT;
  69. /**
  70. * Whether the current platform is Apple iOS.
  71. */
  72. this.IOS = this.isBrowser && /iPad|iPhone|iPod/.test(navigator.userAgent) &&
  73. !('MSStream' in window);
  74. /**
  75. * Whether the current browser is Firefox.
  76. */
  77. // It's difficult to detect the plain Gecko engine, because most of the browsers identify
  78. // them self as Gecko-like browsers and modify the userAgent's according to that.
  79. // Since we only cover one explicit Firefox case, we can simply check for Firefox
  80. // instead of having an unstable check for Gecko.
  81. this.FIREFOX = this.isBrowser && /(firefox|minefield)/i.test(navigator.userAgent);
  82. /**
  83. * Whether the current platform is Android.
  84. */
  85. // Trident on mobile adds the android platform to the userAgent to trick detections.
  86. this.ANDROID = this.isBrowser && /android/i.test(navigator.userAgent) && !this.TRIDENT;
  87. /**
  88. * Whether the current browser is Safari.
  89. */
  90. // Safari browsers will include the Safari keyword in their userAgent. Some browsers may fake
  91. // this and just place the Safari keyword in the userAgent. To be more safe about Safari every
  92. // Safari browser should also use Webkit as its layout engine.
  93. this.SAFARI = this.isBrowser && /safari/i.test(navigator.userAgent) && this.WEBKIT;
  94. }
  95. }
  96. Platform.decorators = [
  97. { type: Injectable, args: [{ providedIn: 'root' },] },
  98. ];
  99. /** @nocollapse */
  100. Platform.ctorParameters = () => [
  101. { type: Object, decorators: [{ type: Optional }, { type: Inject, args: [PLATFORM_ID,] }] }
  102. ];
  103. /** @nocollapse */ Platform.ngInjectableDef = ɵɵdefineInjectable({ factory: function Platform_Factory() { return new Platform(ɵɵinject(PLATFORM_ID, 8)); }, token: Platform, providedIn: "root" });
  104. /**
  105. * @fileoverview added by tsickle
  106. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  107. */
  108. class PlatformModule {
  109. }
  110. PlatformModule.decorators = [
  111. { type: NgModule, args: [{},] },
  112. ];
  113. /**
  114. * @fileoverview added by tsickle
  115. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  116. */
  117. /**
  118. * Cached result Set of input types support by the current browser.
  119. * @type {?}
  120. */
  121. let supportedInputTypes;
  122. /**
  123. * Types of `<input>` that *might* be supported.
  124. * @type {?}
  125. */
  126. const candidateInputTypes = [
  127. // `color` must come first. Chrome 56 shows a warning if we change the type to `color` after
  128. // first changing it to something else:
  129. // The specified value "" does not conform to the required format.
  130. // The format is "#rrggbb" where rr, gg, bb are two-digit hexadecimal numbers.
  131. 'color',
  132. 'button',
  133. 'checkbox',
  134. 'date',
  135. 'datetime-local',
  136. 'email',
  137. 'file',
  138. 'hidden',
  139. 'image',
  140. 'month',
  141. 'number',
  142. 'password',
  143. 'radio',
  144. 'range',
  145. 'reset',
  146. 'search',
  147. 'submit',
  148. 'tel',
  149. 'text',
  150. 'time',
  151. 'url',
  152. 'week',
  153. ];
  154. /**
  155. * @return {?} The input types supported by this browser.
  156. */
  157. function getSupportedInputTypes() {
  158. // Result is cached.
  159. if (supportedInputTypes) {
  160. return supportedInputTypes;
  161. }
  162. // We can't check if an input type is not supported until we're on the browser, so say that
  163. // everything is supported when not on the browser. We don't use `Platform` here since it's
  164. // just a helper function and can't inject it.
  165. if (typeof document !== 'object' || !document) {
  166. supportedInputTypes = new Set(candidateInputTypes);
  167. return supportedInputTypes;
  168. }
  169. /** @type {?} */
  170. let featureTestInput = document.createElement('input');
  171. supportedInputTypes = new Set(candidateInputTypes.filter((/**
  172. * @param {?} value
  173. * @return {?}
  174. */
  175. value => {
  176. featureTestInput.setAttribute('type', value);
  177. return featureTestInput.type === value;
  178. })));
  179. return supportedInputTypes;
  180. }
  181. /**
  182. * @fileoverview added by tsickle
  183. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  184. */
  185. /**
  186. * Cached result of whether the user's browser supports passive event listeners.
  187. * @type {?}
  188. */
  189. let supportsPassiveEvents;
  190. /**
  191. * Checks whether the user's browser supports passive event listeners.
  192. * See: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
  193. * @return {?}
  194. */
  195. function supportsPassiveEventListeners() {
  196. if (supportsPassiveEvents == null && typeof window !== 'undefined') {
  197. try {
  198. window.addEventListener('test', (/** @type {?} */ (null)), Object.defineProperty({}, 'passive', {
  199. get: (/**
  200. * @return {?}
  201. */
  202. () => supportsPassiveEvents = true)
  203. }));
  204. }
  205. finally {
  206. supportsPassiveEvents = supportsPassiveEvents || false;
  207. }
  208. }
  209. return supportsPassiveEvents;
  210. }
  211. /**
  212. * Normalizes an `AddEventListener` object to something that can be passed
  213. * to `addEventListener` on any browser, no matter whether it supports the
  214. * `options` parameter.
  215. * @param {?} options Object to be normalized.
  216. * @return {?}
  217. */
  218. function normalizePassiveListenerOptions(options) {
  219. return supportsPassiveEventListeners() ? options : !!options.capture;
  220. }
  221. /**
  222. * @fileoverview added by tsickle
  223. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  224. */
  225. /** @enum {number} */
  226. const RtlScrollAxisType = {
  227. /**
  228. * scrollLeft is 0 when scrolled all the way left and (scrollWidth - clientWidth) when scrolled
  229. * all the way right.
  230. */
  231. NORMAL: 0,
  232. /**
  233. * scrollLeft is -(scrollWidth - clientWidth) when scrolled all the way left and 0 when scrolled
  234. * all the way right.
  235. */
  236. NEGATED: 1,
  237. /**
  238. * scrollLeft is (scrollWidth - clientWidth) when scrolled all the way left and 0 when scrolled
  239. * all the way right.
  240. */
  241. INVERTED: 2,
  242. };
  243. RtlScrollAxisType[RtlScrollAxisType.NORMAL] = 'NORMAL';
  244. RtlScrollAxisType[RtlScrollAxisType.NEGATED] = 'NEGATED';
  245. RtlScrollAxisType[RtlScrollAxisType.INVERTED] = 'INVERTED';
  246. /**
  247. * Cached result of the way the browser handles the horizontal scroll axis in RTL mode.
  248. * @type {?}
  249. */
  250. let rtlScrollAxisType;
  251. /**
  252. * Check whether the browser supports scroll behaviors.
  253. * @return {?}
  254. */
  255. function supportsScrollBehavior() {
  256. return !!(typeof document == 'object' && 'scrollBehavior' in (/** @type {?} */ (document.documentElement)).style);
  257. }
  258. /**
  259. * Checks the type of RTL scroll axis used by this browser. As of time of writing, Chrome is NORMAL,
  260. * Firefox & Safari are NEGATED, and IE & Edge are INVERTED.
  261. * @return {?}
  262. */
  263. function getRtlScrollAxisType() {
  264. // We can't check unless we're on the browser. Just assume 'normal' if we're not.
  265. if (typeof document !== 'object' || !document) {
  266. return RtlScrollAxisType.NORMAL;
  267. }
  268. if (!rtlScrollAxisType) {
  269. // Create a 1px wide scrolling container and a 2px wide content element.
  270. /** @type {?} */
  271. const scrollContainer = document.createElement('div');
  272. /** @type {?} */
  273. const containerStyle = scrollContainer.style;
  274. scrollContainer.dir = 'rtl';
  275. containerStyle.height = '1px';
  276. containerStyle.width = '1px';
  277. containerStyle.overflow = 'auto';
  278. containerStyle.visibility = 'hidden';
  279. containerStyle.pointerEvents = 'none';
  280. containerStyle.position = 'absolute';
  281. /** @type {?} */
  282. const content = document.createElement('div');
  283. /** @type {?} */
  284. const contentStyle = content.style;
  285. contentStyle.width = '2px';
  286. contentStyle.height = '1px';
  287. scrollContainer.appendChild(content);
  288. document.body.appendChild(scrollContainer);
  289. rtlScrollAxisType = RtlScrollAxisType.NORMAL;
  290. // The viewport starts scrolled all the way to the right in RTL mode. If we are in a NORMAL
  291. // browser this would mean that the scrollLeft should be 1. If it's zero instead we know we're
  292. // dealing with one of the other two types of browsers.
  293. if (scrollContainer.scrollLeft === 0) {
  294. // In a NEGATED browser the scrollLeft is always somewhere in [-maxScrollAmount, 0]. For an
  295. // INVERTED browser it is always somewhere in [0, maxScrollAmount]. We can determine which by
  296. // setting to the scrollLeft to 1. This is past the max for a NEGATED browser, so it will
  297. // return 0 when we read it again.
  298. scrollContainer.scrollLeft = 1;
  299. rtlScrollAxisType =
  300. scrollContainer.scrollLeft === 0 ? RtlScrollAxisType.NEGATED : RtlScrollAxisType.INVERTED;
  301. }
  302. (/** @type {?} */ (scrollContainer.parentNode)).removeChild(scrollContainer);
  303. }
  304. return rtlScrollAxisType;
  305. }
  306. /**
  307. * @fileoverview added by tsickle
  308. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  309. */
  310. /** @type {?} */
  311. let shadowDomIsSupported;
  312. /**
  313. * Checks whether the user's browser support Shadow DOM.
  314. * @return {?}
  315. */
  316. function _supportsShadowDom() {
  317. if (shadowDomIsSupported == null) {
  318. /** @type {?} */
  319. const head = typeof document !== 'undefined' ? document.head : null;
  320. shadowDomIsSupported = !!(head && (((/** @type {?} */ (head))).createShadowRoot || head.attachShadow));
  321. }
  322. return shadowDomIsSupported;
  323. }
  324. /**
  325. * @fileoverview added by tsickle
  326. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  327. */
  328. /**
  329. * @fileoverview added by tsickle
  330. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  331. */
  332. export { Platform, PlatformModule, getSupportedInputTypes, supportsPassiveEventListeners, normalizePassiveListenerOptions, supportsScrollBehavior, getRtlScrollAxisType, RtlScrollAxisType, _supportsShadowDom };
  333. //# sourceMappingURL=platform.js.map