1 | import { EventEmitter } from '@angular/core';
|
2 | import { getCss, isTextInput } from '../util/dom';
|
3 | import { QueryParams } from './query-params';
|
4 | import { 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 | */
|
29 | var 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 | }());
|
982 | export { Platform };
|
983 | function 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 | */
|
1078 | function 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 | */
|
1095 | var 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 | }());
|
1191 | function 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 | */
|
1214 | export 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 |