UNPKG

43.1 kBJavaScriptView Raw
1import { EventEmitter } from '@angular/core';
2import { getCss, isTextInput } from '../util/dom';
3import { QueryParams } from './query-params';
4import { removeArrayItem } from '../util/util';
5/**
6 * \@name Platform
7 * \@description
8 * The Platform service can be used to get information about your current device.
9 * You can get all of the platforms associated with the device using the [platforms](#platforms)
10 * method, including whether the app is being viewed from a tablet, if it's
11 * on a mobile device or browser, and the exact platform (iOS, Android, etc).
12 * You can also get the orientation of the device, if it uses right-to-left
13 * language direction, and much much more. With this information you can completely
14 * customize your app to fit any device.
15 *
16 * \@usage
17 * ```ts
18 * import { Platform } from 'ionic-angular';
19 *
20 * \@Component({...})
21 * export MyPage {
22 * constructor(public platform: Platform) {
23 *
24 * }
25 * }
26 * ```
27 * \@demo /docs/demos/src/platform/
28 */
29var Platform = (function () {
30 function Platform() {
31 var _this = this;
32 this._versions = {};
33 this._qp = new QueryParams();
34 this._bbActions = [];
35 this._pW = 0;
36 this._pH = 0;
37 this._lW = 0;
38 this._lH = 0;
39 this._isPortrait = null;
40 this._uiEvtOpts = false;
41 /**
42 * \@internal
43 */
44 this._platforms = [];
45 /**
46 * @hidden
47 */
48 this.backButton = new EventEmitter();
49 /**
50 * The pause event emits when the native platform puts the application
51 * into the background, typically when the user switches to a different
52 * application. This event would emit when a Cordova app is put into
53 * the background, however, it would not fire on a standard web browser.
54 */
55 this.pause = new EventEmitter();
56 /**
57 * The resume event emits when the native platform pulls the application
58 * out from the background. This event would emit when a Cordova app comes
59 * out from the background, however, it would not fire on a standard web browser.
60 */
61 this.resume = new EventEmitter();
62 /**
63 * The resize event emits when the native platform pulls the application
64 * out from the background. This event would emit when a Cordova app comes
65 * out from the background, however, it would not fire on a standard web browser.
66 */
67 this.resize = new EventEmitter();
68 this._readyPromise = new Promise(function (res) { _this._readyResolve = res; });
69 this.backButton.subscribe(function () {
70 // the hardware back button event has been fired
71 (void 0) /* console.debug */;
72 // decide which backbutton action should run
73 _this.runBackButtonAction();
74 });
75 }
76 /**
77 * @hidden
78 * @param {?} win
79 * @return {?}
80 */
81 Platform.prototype.setWindow = function (win) {
82 this._win = win;
83 };
84 /**
85 * @hidden
86 * @return {?}
87 */
88 Platform.prototype.win = function () {
89 return this._win;
90 };
91 /**
92 * @hidden
93 * @param {?} doc
94 * @return {?}
95 */
96 Platform.prototype.setDocument = function (doc) {
97 this._doc = doc;
98 };
99 /**
100 * @hidden
101 * @return {?}
102 */
103 Platform.prototype.doc = function () {
104 return this._doc;
105 };
106 /**
107 * @hidden
108 * @param {?} zone
109 * @return {?}
110 */
111 Platform.prototype.setZone = function (zone) {
112 this.zone = zone;
113 };
114 /**
115 * @hidden
116 * @param {?} docElement
117 * @return {?}
118 */
119 Platform.prototype.setCssProps = function (docElement) {
120 this.Css = getCss(docElement);
121 };
122 /**
123 * \@description
124 * Depending on the platform the user is on, `is(platformName)` will
125 * return `true` or `false`. Note that the same app can return `true`
126 * for more than one platform name. For example, an app running from
127 * an iPad would return `true` for the platform names: `mobile`,
128 * `ios`, `ipad`, and `tablet`. Additionally, if the app was running
129 * from Cordova then `cordova` would be true, and if it was running
130 * from a web browser on the iPad then `mobileweb` would be `true`.
131 *
132 * ```
133 * import { Platform } from 'ionic-angular';
134 *
135 * \@Component({...})
136 * export MyPage {
137 * constructor(public platform: Platform) {
138 * if (this.platform.is('ios')) {
139 * // This will only print when on iOS
140 * console.log('I am an iOS device!');
141 * }
142 * }
143 * }
144 * ```
145 *
146 * | Platform Name | Description |
147 * |-----------------|------------------------------------|
148 * | android | on a device running Android. |
149 * | cordova | on a device running Cordova. |
150 * | core | on a desktop device. |
151 * | ios | on a device running iOS. |
152 * | ipad | on an iPad device. |
153 * | iphone | on an iPhone device. |
154 * | mobile | on a mobile device. |
155 * | mobileweb | in a browser on a mobile device. |
156 * | phablet | on a phablet device. |
157 * | tablet | on a tablet device. |
158 * | windows | on a device running Windows. |
159 *
160 * @param {?} platformName
161 * @return {?}
162 */
163 Platform.prototype.is = function (platformName) {
164 return (this._platforms.indexOf(platformName) > -1);
165 };
166 /**
167 * \@description
168 * Depending on what device you are on, `platforms` can return multiple values.
169 * Each possible value is a hierarchy of platforms. For example, on an iPhone,
170 * it would return `mobile`, `ios`, and `iphone`.
171 *
172 * ```
173 * import { Platform } from 'ionic-angular';
174 *
175 * \@Component({...})
176 * export MyPage {
177 * constructor(public platform: Platform) {
178 * // This will print an array of the current platforms
179 * console.log(this.platform.platforms());
180 * }
181 * }
182 * ```
183 * @return {?}
184 */
185 Platform.prototype.platforms = function () {
186 // get the array of active platforms, which also knows the hierarchy,
187 // with the last one the most important
188 return this._platforms;
189 };
190 /**
191 * Returns an object containing version information about all of the platforms.
192 *
193 * ```
194 * import { Platform } from 'ionic-angular';
195 *
196 * \@Component({...})
197 * export MyPage {
198 * constructor(public platform: Platform) {
199 * // This will print an object containing
200 * // all of the platforms and their versions
201 * console.log(platform.versions());
202 * }
203 * }
204 * ```
205 *
206 * @return {?}
207 */
208 Platform.prototype.versions = function () {
209 // get all the platforms that have a valid parsed version
210 return this._versions;
211 };
212 /**
213 * @hidden
214 * @return {?}
215 */
216 Platform.prototype.version = function () {
217 for (var /** @type {?} */ platformName in this._versions) {
218 if (this._versions[platformName]) {
219 return this._versions[platformName];
220 }
221 }
222 return {};
223 };
224 /**
225 * Returns a promise when the platform is ready and native functionality
226 * can be called. If the app is running from within a web browser, then
227 * the promise will resolve when the DOM is ready. When the app is running
228 * from an application engine such as Cordova, then the promise will
229 * resolve when Cordova triggers the `deviceready` event.
230 *
231 * The resolved value is the `readySource`, which states which platform
232 * ready was used. For example, when Cordova is ready, the resolved ready
233 * source is `cordova`. The default ready source value will be `dom`. The
234 * `readySource` is useful if different logic should run depending on the
235 * platform the app is running from. For example, only Cordova can execute
236 * the status bar plugin, so the web should not run status bar plugin logic.
237 *
238 * ```
239 * import { Component } from '\@angular/core';
240 * import { Platform } from 'ionic-angular';
241 *
242 * \@Component({...})
243 * export MyApp {
244 * constructor(public platform: Platform) {
245 * this.platform.ready().then((readySource) => {
246 * console.log('Platform ready from', readySource);
247 * // Platform now ready, execute any required native code
248 * });
249 * }
250 * }
251 * ```
252 * @return {?}
253 */
254 Platform.prototype.ready = function () {
255 return this._readyPromise;
256 };
257 /**
258 * @hidden
259 * This should be triggered by the engine when the platform is
260 * ready. If there was no custom prepareReady method from the engine,
261 * such as Cordova or Electron, then it uses the default DOM ready.
262 * @param {?} readySource
263 * @return {?}
264 */
265 Platform.prototype.triggerReady = function (readySource) {
266 var _this = this;
267 this.zone.run(function () {
268 _this._readyResolve(readySource);
269 });
270 };
271 /**
272 * @hidden
273 * This is the default prepareReady if it's not replaced by an engine,
274 * such as Cordova or Electron. If there was no custom prepareReady
275 * method from an engine then it uses the method below, which triggers
276 * the platform ready on the DOM ready event, and the default resolved
277 * value is `dom`.
278 * @return {?}
279 */
280 Platform.prototype.prepareReady = function () {
281 var /** @type {?} */ self = this;
282 if (self._doc.readyState === 'complete' || self._doc.readyState === 'interactive') {
283 self.triggerReady('dom');
284 }
285 else {
286 self._doc.addEventListener('DOMContentLoaded', completed, false);
287 self._win.addEventListener('load', completed, false);
288 }
289 /**
290 * @return {?}
291 */
292 function completed() {
293 self._doc.removeEventListener('DOMContentLoaded', completed, false);
294 self._win.removeEventListener('load', completed, false);
295 self.triggerReady('dom');
296 }
297 };
298 /**
299 * Set the app's language direction, which will update the `dir` attribute
300 * on the app's root `<html>` element. We recommend the app's `index.html`
301 * file already has the correct `dir` attribute value set, such as
302 * `<html dir="ltr">` or `<html dir="rtl">`. This method is useful if the
303 * direction needs to be dynamically changed per user/session.
304 * [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
305 * @param {?} dir
306 * @param {?} updateDocument
307 * @return {?}
308 */
309 Platform.prototype.setDir = function (dir, updateDocument) {
310 this._dir = dir;
311 this.isRTL = (dir === 'rtl');
312 if (updateDocument !== false) {
313 this._doc['documentElement'].setAttribute('dir', dir);
314 }
315 };
316 /**
317 * Returns app's language direction.
318 * We recommend the app's `index.html` file already has the correct `dir`
319 * attribute value set, such as `<html dir="ltr">` or `<html dir="rtl">`.
320 * [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
321 * @return {?}
322 */
323 Platform.prototype.dir = function () {
324 return this._dir;
325 };
326 /**
327 * Set the app's language and optionally the country code, which will update
328 * the `lang` attribute on the app's root `<html>` element.
329 * We recommend the app's `index.html` file already has the correct `lang`
330 * attribute value set, such as `<html lang="en">`. This method is useful if
331 * the language needs to be dynamically changed per user/session.
332 * [W3C: Declaring language in HTML](http://www.w3.org/International/questions/qa-html-language-declarations)
333 * @param {?} language
334 * @param {?} updateDocument
335 * @return {?}
336 */
337 Platform.prototype.setLang = function (language, updateDocument) {
338 this._lang = language;
339 if (updateDocument !== false) {
340 this._doc['documentElement'].setAttribute('lang', language);
341 }
342 };
343 /**
344 * Returns app's language and optional country code.
345 * We recommend the app's `index.html` file already has the correct `lang`
346 * attribute value set, such as `<html lang="en">`.
347 * [W3C: Declaring language in HTML](http://www.w3.org/International/questions/qa-html-language-declarations)
348 * @return {?}
349 */
350 Platform.prototype.lang = function () {
351 return this._lang;
352 };
353 /**
354 * @hidden
355 * @return {?}
356 */
357 Platform.prototype.exitApp = function () { };
358 /**
359 * The back button event is triggered when the user presses the native
360 * platform's back button, also referred to as the "hardware" back button.
361 * This event is only used within Cordova apps running on Android and
362 * Windows platforms. This event is not fired on iOS since iOS doesn't come
363 * with a hardware back button in the same sense an Android or Windows device
364 * does.
365 *
366 * Registering a hardware back button action and setting a priority allows
367 * apps to control which action should be called when the hardware back
368 * button is pressed. This method decides which of the registered back button
369 * actions has the highest priority and should be called.
370 *
371 * if this registered action has the highest priority.
372 * the back button action.
373 * @param {?} fn
374 * @param {?=} priority
375 * @return {?}
376 */
377 Platform.prototype.registerBackButtonAction = function (fn, priority) {
378 var _this = this;
379 if (priority === void 0) { priority = 0; }
380 var /** @type {?} */ action = { fn: fn, priority: priority };
381 this._bbActions.push(action);
382 // return a function to unregister this back button action
383 return function () {
384 removeArrayItem(_this._bbActions, action);
385 };
386 };
387 /**
388 * @hidden
389 * @return {?}
390 */
391 Platform.prototype.runBackButtonAction = function () {
392 // decide which one back button action should run
393 var /** @type {?} */ winner = null;
394 this._bbActions.forEach(function (action) {
395 if (!winner || action.priority >= winner.priority) {
396 winner = action;
397 }
398 });
399 // run the winning action if there is one
400 winner && winner.fn && winner.fn();
401 };
402 /**
403 * @hidden
404 * @param {?} userAgent
405 * @return {?}
406 */
407 Platform.prototype.setUserAgent = function (userAgent) {
408 this._ua = userAgent;
409 };
410 /**
411 * @hidden
412 * @param {?} url
413 * @return {?}
414 */
415 Platform.prototype.setQueryParams = function (url) {
416 this._qp.parseUrl(url);
417 };
418 /**
419 * Get the query string parameter
420 * @param {?} key
421 * @return {?}
422 */
423 Platform.prototype.getQueryParam = function (key) {
424 return this._qp.get(key);
425 };
426 /**
427 * Get the current url.
428 * @return {?}
429 */
430 Platform.prototype.url = function () {
431 return this._win['location']['href'];
432 };
433 /**
434 * @hidden
435 * @return {?}
436 */
437 Platform.prototype.userAgent = function () {
438 return this._ua || '';
439 };
440 /**
441 * @hidden
442 * @param {?} navigatorPlt
443 * @return {?}
444 */
445 Platform.prototype.setNavigatorPlatform = function (navigatorPlt) {
446 this._nPlt = navigatorPlt;
447 };
448 /**
449 * @hidden
450 * @return {?}
451 */
452 Platform.prototype.navigatorPlatform = function () {
453 return this._nPlt || '';
454 };
455 /**
456 * Gets the width of the platform's viewport using `window.innerWidth`.
457 * Using this method is preferred since the dimension is a cached value,
458 * which reduces the chance of multiple and expensive DOM reads.
459 * @return {?}
460 */
461 Platform.prototype.width = function () {
462 this._calcDim();
463 return this._isPortrait ? this._pW : this._lW;
464 };
465 /**
466 * Gets the height of the platform's viewport using `window.innerHeight`.
467 * Using this method is preferred since the dimension is a cached value,
468 * which reduces the chance of multiple and expensive DOM reads.
469 * @return {?}
470 */
471 Platform.prototype.height = function () {
472 this._calcDim();
473 return this._isPortrait ? this._pH : this._lH;
474 };
475 /**
476 * @hidden
477 * @param {?} ele
478 * @param {?=} pseudoEle
479 * @return {?}
480 */
481 Platform.prototype.getElementComputedStyle = function (ele, pseudoEle) {
482 return this._win['getComputedStyle'](ele, pseudoEle);
483 };
484 /**
485 * @hidden
486 * @param {?} x
487 * @param {?} y
488 * @return {?}
489 */
490 Platform.prototype.getElementFromPoint = function (x, y) {
491 return (this._doc['elementFromPoint'](x, y));
492 };
493 /**
494 * @hidden
495 * @param {?} ele
496 * @return {?}
497 */
498 Platform.prototype.getElementBoundingClientRect = function (ele) {
499 return ele['getBoundingClientRect']();
500 };
501 /**
502 * Returns `true` if the app is in portait mode.
503 * @return {?}
504 */
505 Platform.prototype.isPortrait = function () {
506 this._calcDim();
507 return this._isPortrait;
508 };
509 /**
510 * Returns `true` if the app is in landscape mode.
511 * @return {?}
512 */
513 Platform.prototype.isLandscape = function () {
514 return !this.isPortrait();
515 };
516 /**
517 * @return {?}
518 */
519 Platform.prototype._calcDim = function () {
520 // we're caching window dimensions so that
521 // we're not forcing many layouts
522 // if _isPortrait is null then that means
523 // the dimensions needs to be looked up again
524 // this also has to cover an edge case that only
525 // happens on iOS 10 (not other versions of iOS)
526 // where window.innerWidth is always bigger than
527 // window.innerHeight when it is first measured,
528 // even when the device is in portrait but
529 // the second time it is measured it is correct.
530 // Hopefully this check will not be needed in the future
531 if (this._isPortrait === null || this._isPortrait === false && this._win['innerWidth'] < this._win['innerHeight']) {
532 var /** @type {?} */ win = this._win;
533 var /** @type {?} */ innerWidth = win['innerWidth'];
534 var /** @type {?} */ innerHeight = win['innerHeight'];
535 // we're keeping track of portrait and landscape dimensions
536 // separately because the virtual keyboard can really mess
537 // up accurate values when the keyboard is up
538 if (win.screen.width > 0 && win.screen.height > 0) {
539 if (innerWidth < innerHeight) {
540 // the device is in portrait
541 // we have to do fancier checking here
542 // because of the virtual keyboard resizing
543 // the window
544 if (this._pW <= innerWidth) {
545 (void 0) /* console.debug */;
546 this._isPortrait = true;
547 this._pW = innerWidth;
548 }
549 if (this._pH <= innerHeight) {
550 (void 0) /* console.debug */;
551 this._isPortrait = true;
552 this._pH = innerHeight;
553 }
554 }
555 else {
556 // the device is in landscape
557 if (this._lW !== innerWidth) {
558 (void 0) /* console.debug */;
559 this._isPortrait = false;
560 this._lW = innerWidth;
561 }
562 if (this._lH !== innerHeight) {
563 (void 0) /* console.debug */;
564 this._isPortrait = false;
565 this._lH = innerHeight;
566 }
567 }
568 }
569 }
570 };
571 /**
572 * @hidden
573 * This requestAnimationFrame will NOT be wrapped by zone.
574 * @param {?} callback
575 * @return {?}
576 */
577 Platform.prototype.raf = function (callback) {
578 var /** @type {?} */ win = this._win;
579 return win['__zone_symbol__requestAnimationFrame'](callback);
580 };
581 /**
582 * @hidden
583 * @param {?} rafId
584 * @return {?}
585 */
586 Platform.prototype.cancelRaf = function (rafId) {
587 var /** @type {?} */ win = this._win;
588 return win['__zone_symbol__cancelAnimationFrame'](rafId);
589 };
590 /**
591 * @hidden
592 * This setTimeout will NOT be wrapped by zone.
593 * @param {?} callback
594 * @param {?=} timeout
595 * @return {?}
596 */
597 Platform.prototype.timeout = function (callback, timeout) {
598 var /** @type {?} */ win = this._win;
599 return win['__zone_symbol__setTimeout'](callback, timeout);
600 };
601 /**
602 * @hidden
603 * This setTimeout will NOT be wrapped by zone.
604 * @param {?} timeoutId
605 * @return {?}
606 */
607 Platform.prototype.cancelTimeout = function (timeoutId) {
608 var /** @type {?} */ win = this._win;
609 win['__zone_symbol__clearTimeout'](timeoutId);
610 };
611 /**
612 * @hidden
613 * Built to use modern event listener options, like "passive".
614 * If options are not supported, then just return a boolean which
615 * represents "capture". Returns a method to remove the listener.
616 * @param {?} ele
617 * @param {?} eventName
618 * @param {?} callback
619 * @param {?} opts
620 * @param {?=} unregisterListenersCollection
621 * @return {?}
622 */
623 Platform.prototype.registerListener = function (ele, eventName, callback, opts, unregisterListenersCollection) {
624 // use event listener options when supported
625 // otherwise it's just a boolean for the "capture" arg
626 var /** @type {?} */ listenerOpts = this._uiEvtOpts ? {
627 'capture': !!opts.capture,
628 'passive': !!opts.passive,
629 } : !!opts.capture;
630 var /** @type {?} */ unReg;
631 if (!opts.zone && ele['__zone_symbol__addEventListener']) {
632 // do not wrap this event in zone and we've verified we can use the raw addEventListener
633 ele['__zone_symbol__addEventListener'](eventName, callback, listenerOpts);
634 unReg = function unregisterListener() {
635 ele['__zone_symbol__removeEventListener'](eventName, callback, listenerOpts);
636 };
637 }
638 else {
639 // use the native addEventListener, which is wrapped with zone
640 ele['addEventListener'](eventName, callback, listenerOpts);
641 unReg = function unregisterListener() {
642 ele['removeEventListener'](eventName, callback, listenerOpts);
643 };
644 }
645 if (unregisterListenersCollection) {
646 unregisterListenersCollection.push(unReg);
647 }
648 return unReg;
649 };
650 /**
651 * @hidden
652 * @param {?} el
653 * @param {?} callback
654 * @param {?=} zone
655 * @return {?}
656 */
657 Platform.prototype.transitionEnd = function (el, callback, zone) {
658 if (zone === void 0) { zone = true; }
659 var /** @type {?} */ unRegs = [];
660 /**
661 * @return {?}
662 */
663 function unregister() {
664 unRegs.forEach(function (unReg) {
665 unReg();
666 });
667 }
668 /**
669 * @param {?} ev
670 * @return {?}
671 */
672 function onTransitionEnd(ev) {
673 if (el === ev.target) {
674 unregister();
675 callback(ev);
676 }
677 }
678 if (el) {
679 this.registerListener(el, 'webkitTransitionEnd', /** @type {?} */ (onTransitionEnd), { zone: zone }, unRegs);
680 this.registerListener(el, 'transitionend', /** @type {?} */ (onTransitionEnd), { zone: zone }, unRegs);
681 }
682 return unregister;
683 };
684 /**
685 * @hidden
686 * @param {?} callback
687 * @return {?}
688 */
689 Platform.prototype.windowLoad = function (callback) {
690 var /** @type {?} */ win = this._win;
691 var /** @type {?} */ doc = this._doc;
692 var /** @type {?} */ unreg;
693 if (doc.readyState === 'complete') {
694 callback(win, doc);
695 }
696 else {
697 unreg = this.registerListener(win, 'load', function () {
698 unreg && unreg();
699 callback(win, doc);
700 }, { zone: false });
701 }
702 };
703 /**
704 * @hidden
705 * @param {?} ele
706 * @return {?}
707 */
708 Platform.prototype.isActiveElement = function (ele) {
709 return !!(ele && (this.getActiveElement() === ele));
710 };
711 /**
712 * @hidden
713 * @return {?}
714 */
715 Platform.prototype.getActiveElement = function () {
716 return this._doc['activeElement'];
717 };
718 /**
719 * @hidden
720 * @param {?} ele
721 * @return {?}
722 */
723 Platform.prototype.hasFocus = function (ele) {
724 return !!((ele && (this.getActiveElement() === ele)) && (ele.parentElement.querySelector(':focus') === ele));
725 };
726 /**
727 * @hidden
728 * @return {?}
729 */
730 Platform.prototype.hasFocusedTextInput = function () {
731 var /** @type {?} */ ele = this.getActiveElement();
732 if (isTextInput(ele)) {
733 return (ele.parentElement.querySelector(':focus') === ele);
734 }
735 return false;
736 };
737 /**
738 * @hidden
739 * @return {?}
740 */
741 Platform.prototype.focusOutActiveElement = function () {
742 var /** @type {?} */ activeElement = this.getActiveElement();
743 activeElement && activeElement.blur && activeElement.blur();
744 };
745 /**
746 * @return {?}
747 */
748 Platform.prototype._initEvents = function () {
749 var _this = this;
750 // Test via a getter in the options object to see if the passive property is accessed
751 try {
752 var /** @type {?} */ opts = Object.defineProperty({}, 'passive', {
753 get: function () {
754 _this._uiEvtOpts = true;
755 }
756 });
757 this._win.addEventListener('optsTest', null, opts);
758 }
759 catch (e) { }
760 // add the window resize event listener XXms after
761 this.timeout(function () {
762 var /** @type {?} */ timerId;
763 _this.registerListener(_this._win, 'resize', function () {
764 clearTimeout(timerId);
765 timerId = setTimeout(function () {
766 // setting _isPortrait to null means the
767 // dimensions will need to be looked up again
768 if (_this.hasFocusedTextInput() === false) {
769 _this._isPortrait = null;
770 }
771 _this.zone.run(function () { return _this.resize.emit(); });
772 }, 200);
773 }, { passive: true, zone: false });
774 }, 2000);
775 };
776 /**
777 * @hidden
778 * @param {?} platformConfigs
779 * @return {?}
780 */
781 Platform.prototype.setPlatformConfigs = function (platformConfigs) {
782 this._registry = platformConfigs || {};
783 };
784 /**
785 * @hidden
786 * @param {?} platformName
787 * @return {?}
788 */
789 Platform.prototype.getPlatformConfig = function (platformName) {
790 return this._registry[platformName] || {};
791 };
792 /**
793 * @hidden
794 * @return {?}
795 */
796 Platform.prototype.registry = function () {
797 return this._registry;
798 };
799 /**
800 * @hidden
801 * @param {?} platformName
802 * @return {?}
803 */
804 Platform.prototype.setDefault = function (platformName) {
805 this._default = platformName;
806 };
807 /**
808 * @hidden
809 * @param {?} queryValue
810 * @param {?} queryTestValue
811 * @return {?}
812 */
813 Platform.prototype.testQuery = function (queryValue, queryTestValue) {
814 var /** @type {?} */ valueSplit = queryValue.toLowerCase().split(';');
815 return valueSplit.indexOf(queryTestValue) > -1;
816 };
817 /**
818 * @hidden
819 * @param {?} navigatorPlatformExpression
820 * @return {?}
821 */
822 Platform.prototype.testNavigatorPlatform = function (navigatorPlatformExpression) {
823 var /** @type {?} */ rgx = new RegExp(navigatorPlatformExpression, 'i');
824 return rgx.test(this._nPlt);
825 };
826 /**
827 * @hidden
828 * @param {?} userAgentExpression
829 * @return {?}
830 */
831 Platform.prototype.matchUserAgentVersion = function (userAgentExpression) {
832 if (this._ua && userAgentExpression) {
833 var /** @type {?} */ val = this._ua.match(userAgentExpression);
834 if (val) {
835 return {
836 major: val[1],
837 minor: val[2]
838 };
839 }
840 }
841 };
842 /**
843 * @param {?} expression
844 * @return {?}
845 */
846 Platform.prototype.testUserAgent = function (expression) {
847 if (this._ua) {
848 return this._ua.indexOf(expression) >= 0;
849 }
850 return false;
851 };
852 /**
853 * @hidden
854 * @param {?} queryStringName
855 * @param {?=} userAgentAtLeastHas
856 * @param {?=} userAgentMustNotHave
857 * @return {?}
858 */
859 Platform.prototype.isPlatformMatch = function (queryStringName, userAgentAtLeastHas, userAgentMustNotHave) {
860 if (userAgentMustNotHave === void 0) { userAgentMustNotHave = []; }
861 var /** @type {?} */ queryValue = this._qp.get('ionicplatform');
862 if (queryValue) {
863 return this.testQuery(queryValue, queryStringName);
864 }
865 userAgentAtLeastHas = userAgentAtLeastHas || [queryStringName];
866 var /** @type {?} */ userAgent = this._ua.toLowerCase();
867 for (var /** @type {?} */ i = 0; i < userAgentAtLeastHas.length; i++) {
868 if (userAgent.indexOf(userAgentAtLeastHas[i]) > -1) {
869 for (var /** @type {?} */ j = 0; j < userAgentMustNotHave.length; j++) {
870 if (userAgent.indexOf(userAgentMustNotHave[j]) > -1) {
871 return false;
872 }
873 }
874 return true;
875 }
876 }
877 return false;
878 };
879 /**
880 * @hidden
881 * @return {?}
882 */
883 Platform.prototype.init = function () {
884 this._initEvents();
885 var /** @type {?} */ rootPlatformNode;
886 var /** @type {?} */ enginePlatformNode;
887 // figure out the most specific platform and active engine
888 var /** @type {?} */ tmpPlt;
889 for (var /** @type {?} */ platformName in this._registry) {
890 tmpPlt = this.matchPlatform(platformName);
891 if (tmpPlt) {
892 // we found a platform match!
893 // check if its more specific than the one we already have
894 if (tmpPlt.isEngine) {
895 // because it matched then this should be the active engine
896 // you cannot have more than one active engine
897 enginePlatformNode = tmpPlt;
898 }
899 else if (!rootPlatformNode || tmpPlt.depth > rootPlatformNode.depth) {
900 // only find the root node for platforms that are not engines
901 // set this node as the root since we either don't already
902 // have one, or this one is more specific that the current one
903 rootPlatformNode = tmpPlt;
904 }
905 }
906 }
907 if (!rootPlatformNode) {
908 rootPlatformNode = new PlatformNode(this._registry, this._default);
909 }
910 // build a Platform instance filled with the
911 // hierarchy of active platforms and settings
912 if (rootPlatformNode) {
913 // check if we found an engine node (cordova/node-webkit/etc)
914 if (enginePlatformNode) {
915 // add the engine to the first in the platform hierarchy
916 // the original rootPlatformNode now becomes a child
917 // of the engineNode, which is not the new root
918 enginePlatformNode.child = rootPlatformNode;
919 rootPlatformNode.parent = enginePlatformNode;
920 rootPlatformNode = enginePlatformNode;
921 }
922 var /** @type {?} */ platformNode = rootPlatformNode;
923 while (platformNode) {
924 insertSuperset(this._registry, platformNode);
925 platformNode = platformNode.child;
926 }
927 // make sure the root noot is actually the root
928 // incase a node was inserted before the root
929 platformNode = rootPlatformNode.parent;
930 while (platformNode) {
931 rootPlatformNode = platformNode;
932 platformNode = platformNode.parent;
933 }
934 platformNode = rootPlatformNode;
935 while (platformNode) {
936 platformNode.initialize(this);
937 // extra check for ipad pro issue
938 // https://forums.developer.apple.com/thread/25948
939 if (platformNode.name === 'iphone' && this.navigatorPlatform() === 'iPad') {
940 // this is an ipad pro so push ipad and tablet to platforms
941 // and then return as we are done
942 this._platforms.push('tablet');
943 this._platforms.push('ipad');
944 return;
945 }
946 // set the array of active platforms with
947 // the last one in the array the most important
948 this._platforms.push(platformNode.name);
949 // get the platforms version if a version parser was provided
950 this._versions[platformNode.name] = platformNode.version(this);
951 // go to the next platform child
952 platformNode = platformNode.child;
953 }
954 }
955 if (this._platforms.indexOf('mobile') > -1 && this._platforms.indexOf('cordova') === -1) {
956 this._platforms.push('mobileweb');
957 }
958 };
959 /**
960 * @hidden
961 * @param {?} platformName
962 * @return {?}
963 */
964 Platform.prototype.matchPlatform = function (platformName) {
965 // build a PlatformNode and assign config data to it
966 // use it's getRoot method to build up its hierarchy
967 // depending on which platforms match
968 var /** @type {?} */ platformNode = new PlatformNode(this._registry, platformName);
969 var /** @type {?} */ rootNode = platformNode.getRoot(this);
970 if (rootNode) {
971 rootNode.depth = 0;
972 var /** @type {?} */ childPlatform = rootNode.child;
973 while (childPlatform) {
974 rootNode.depth++;
975 childPlatform = childPlatform.child;
976 }
977 }
978 return rootNode;
979 };
980 return Platform;
981}());
982export { Platform };
983function Platform_tsickle_Closure_declarations() {
984 /** @type {?} */
985 Platform.prototype._win;
986 /** @type {?} */
987 Platform.prototype._doc;
988 /** @type {?} */
989 Platform.prototype._versions;
990 /** @type {?} */
991 Platform.prototype._dir;
992 /** @type {?} */
993 Platform.prototype._lang;
994 /** @type {?} */
995 Platform.prototype._ua;
996 /** @type {?} */
997 Platform.prototype._qp;
998 /** @type {?} */
999 Platform.prototype._nPlt;
1000 /** @type {?} */
1001 Platform.prototype._readyPromise;
1002 /** @type {?} */
1003 Platform.prototype._readyResolve;
1004 /** @type {?} */
1005 Platform.prototype._bbActions;
1006 /** @type {?} */
1007 Platform.prototype._registry;
1008 /** @type {?} */
1009 Platform.prototype._default;
1010 /** @type {?} */
1011 Platform.prototype._pW;
1012 /** @type {?} */
1013 Platform.prototype._pH;
1014 /** @type {?} */
1015 Platform.prototype._lW;
1016 /** @type {?} */
1017 Platform.prototype._lH;
1018 /** @type {?} */
1019 Platform.prototype._isPortrait;
1020 /** @type {?} */
1021 Platform.prototype._uiEvtOpts;
1022 /**
1023 * @hidden
1024 * @type {?}
1025 */
1026 Platform.prototype.zone;
1027 /**
1028 * \@internal
1029 * @type {?}
1030 */
1031 Platform.prototype.Css;
1032 /**
1033 * \@internal
1034 * @type {?}
1035 */
1036 Platform.prototype._platforms;
1037 /**
1038 * Returns if this app is using right-to-left language direction or not.
1039 * We recommend the app's `index.html` file already has the correct `dir`
1040 * attribute value set, such as `<html dir="ltr">` or `<html dir="rtl">`.
1041 * [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
1042 * @type {?}
1043 */
1044 Platform.prototype.isRTL;
1045 /**
1046 * @hidden
1047 * @type {?}
1048 */
1049 Platform.prototype.backButton;
1050 /**
1051 * The pause event emits when the native platform puts the application
1052 * into the background, typically when the user switches to a different
1053 * application. This event would emit when a Cordova app is put into
1054 * the background, however, it would not fire on a standard web browser.
1055 * @type {?}
1056 */
1057 Platform.prototype.pause;
1058 /**
1059 * The resume event emits when the native platform pulls the application
1060 * out from the background. This event would emit when a Cordova app comes
1061 * out from the background, however, it would not fire on a standard web browser.
1062 * @type {?}
1063 */
1064 Platform.prototype.resume;
1065 /**
1066 * The resize event emits when the native platform pulls the application
1067 * out from the background. This event would emit when a Cordova app comes
1068 * out from the background, however, it would not fire on a standard web browser.
1069 * @type {?}
1070 */
1071 Platform.prototype.resize;
1072}
1073/**
1074 * @param {?} registry
1075 * @param {?} platformNode
1076 * @return {?}
1077 */
1078function insertSuperset(registry, platformNode) {
1079 var /** @type {?} */ supersetPlaformName = platformNode.superset();
1080 if (supersetPlaformName) {
1081 // add a platform in between two exist platforms
1082 // so we can build the correct hierarchy of active platforms
1083 var /** @type {?} */ supersetPlatform = new PlatformNode(registry, supersetPlaformName);
1084 supersetPlatform.parent = platformNode.parent;
1085 supersetPlatform.child = platformNode;
1086 if (supersetPlatform.parent) {
1087 supersetPlatform.parent.child = supersetPlatform;
1088 }
1089 platformNode.parent = supersetPlatform;
1090 }
1091}
1092/**
1093 * @hidden
1094 */
1095var PlatformNode = (function () {
1096 /**
1097 * @param {?} registry
1098 * @param {?} platformName
1099 */
1100 function PlatformNode(registry, platformName) {
1101 this.registry = registry;
1102 this.c = registry[platformName];
1103 this.name = platformName;
1104 this.isEngine = this.c.isEngine;
1105 }
1106 /**
1107 * @return {?}
1108 */
1109 PlatformNode.prototype.settings = function () {
1110 return this.c.settings || {};
1111 };
1112 /**
1113 * @return {?}
1114 */
1115 PlatformNode.prototype.superset = function () {
1116 return this.c.superset;
1117 };
1118 /**
1119 * @param {?} p
1120 * @return {?}
1121 */
1122 PlatformNode.prototype.isMatch = function (p) {
1123 return this.c.isMatch && this.c.isMatch(p) || false;
1124 };
1125 /**
1126 * @param {?} plt
1127 * @return {?}
1128 */
1129 PlatformNode.prototype.initialize = function (plt) {
1130 this.c.initialize && this.c.initialize(plt);
1131 };
1132 /**
1133 * @param {?} plt
1134 * @return {?}
1135 */
1136 PlatformNode.prototype.version = function (plt) {
1137 if (this.c.versionParser) {
1138 var /** @type {?} */ v = this.c.versionParser(plt);
1139 if (v) {
1140 var /** @type {?} */ str = v.major + '.' + v.minor;
1141 return {
1142 str: str,
1143 num: parseFloat(str),
1144 major: parseInt(v.major, 10),
1145 minor: parseInt(v.minor, 10)
1146 };
1147 }
1148 }
1149 };
1150 /**
1151 * @param {?} plt
1152 * @return {?}
1153 */
1154 PlatformNode.prototype.getRoot = function (plt) {
1155 if (this.isMatch(plt)) {
1156 var /** @type {?} */ parents = this.getSubsetParents(this.name);
1157 if (!parents.length) {
1158 return this;
1159 }
1160 var /** @type {?} */ platformNode = null;
1161 var /** @type {?} */ rootPlatformNode = null;
1162 for (var /** @type {?} */ i = 0; i < parents.length; i++) {
1163 platformNode = new PlatformNode(this.registry, parents[i]);
1164 platformNode.child = this;
1165 rootPlatformNode = platformNode.getRoot(plt);
1166 if (rootPlatformNode) {
1167 this.parent = platformNode;
1168 return rootPlatformNode;
1169 }
1170 }
1171 }
1172 return null;
1173 };
1174 /**
1175 * @param {?} subsetPlatformName
1176 * @return {?}
1177 */
1178 PlatformNode.prototype.getSubsetParents = function (subsetPlatformName) {
1179 var /** @type {?} */ parentPlatformNames = [];
1180 var /** @type {?} */ pltConfig = null;
1181 for (var /** @type {?} */ platformName in this.registry) {
1182 pltConfig = this.registry[platformName];
1183 if (pltConfig.subsets && pltConfig.subsets.indexOf(subsetPlatformName) > -1) {
1184 parentPlatformNames.push(platformName);
1185 }
1186 }
1187 return parentPlatformNames;
1188 };
1189 return PlatformNode;
1190}());
1191function PlatformNode_tsickle_Closure_declarations() {
1192 /** @type {?} */
1193 PlatformNode.prototype.c;
1194 /** @type {?} */
1195 PlatformNode.prototype.parent;
1196 /** @type {?} */
1197 PlatformNode.prototype.child;
1198 /** @type {?} */
1199 PlatformNode.prototype.name;
1200 /** @type {?} */
1201 PlatformNode.prototype.isEngine;
1202 /** @type {?} */
1203 PlatformNode.prototype.depth;
1204 /** @type {?} */
1205 PlatformNode.prototype.registry;
1206}
1207/**
1208 * @hidden
1209 * @param {?} doc
1210 * @param {?} platformConfigs
1211 * @param {?} zone
1212 * @return {?}
1213 */
1214export function setupPlatform(doc, platformConfigs, zone) {
1215 var /** @type {?} */ plt = new Platform();
1216 plt.setDefault('core');
1217 plt.setPlatformConfigs(platformConfigs);
1218 plt.setZone(zone);
1219 // set values from "document"
1220 var /** @type {?} */ docElement = doc.documentElement;
1221 plt.setDocument(doc);
1222 var /** @type {?} */ dir = docElement.dir;
1223 plt.setDir(dir === 'rtl' ? 'rtl' : 'ltr', !dir);
1224 plt.setLang(docElement.lang, false);
1225 // set css properties
1226 plt.setCssProps(docElement);
1227 // set values from "window"
1228 var /** @type {?} */ win = doc.defaultView;
1229 plt.setWindow(win);
1230 plt.setNavigatorPlatform(win.navigator.platform);
1231 plt.setUserAgent(win.navigator.userAgent);
1232 // set location values
1233 plt.setQueryParams(win.location.href);
1234 plt.init();
1235 // add the platform obj to the window
1236 ((win))['Ionic'] = ((win))['Ionic'] || {};
1237 ((win))['Ionic']['platform'] = plt;
1238 return plt;
1239}
1240//# sourceMappingURL=platform.js.map
\No newline at end of file