UNPKG

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