123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- import { EventEmitter, Injectable, Optional } from '@angular/core';
- import { DOCUMENT, Title } from '@angular/platform-browser';
- import * as Constants from './app-constants';
- import { Config } from '../../config/config';
- import { DIRECTION_BACK, DIRECTION_FORWARD, isTabs } from '../../navigation/nav-util';
- import { MenuController } from './menu-controller';
- import { Platform } from '../../platform/platform';
- import { IOSTransition } from '../../transitions/transition-ios';
- import { MDTransition } from '../../transitions/transition-md';
- import { WPTransition } from '../../transitions/transition-wp';
- /**
- * @name App
- * @description
- * App is a utility class used in Ionic to get information about various aspects of an app
- */
- export class App {
- constructor(_config, _plt, _menuCtrl) {
- this._config = _config;
- this._plt = _plt;
- this._menuCtrl = _menuCtrl;
- this._disTime = 0;
- this._scrollTime = 0;
- this._title = '';
- this._titleSrv = new Title(DOCUMENT);
- this._rootNavs = new Map();
- this._didScroll = false;
- /**
- * Observable that emits whenever a view loads in the app.
- * @returns {Observable} Returns an observable
- */
- this.viewDidLoad = new EventEmitter();
- /**
- * Observable that emits before any view is entered in the app.
- * @returns {Observable} Returns an observable
- */
- this.viewWillEnter = new EventEmitter();
- /**
- * Observable that emits after any view is entered in the app.
- * @returns {Observable} Returns an observable
- */
- this.viewDidEnter = new EventEmitter();
- /**
- * Observable that emits before any view is exited in the app.
- * @returns {Observable} Returns an observable
- */
- this.viewWillLeave = new EventEmitter();
- /**
- * Observable that emits after any view is exited in the app.
- * @returns {Observable} Returns an observable
- */
- this.viewDidLeave = new EventEmitter();
- /**
- * Observable that emits before any view unloads in the app.
- * @returns {Observable} Returns an observable
- */
- this.viewWillUnload = new EventEmitter();
- // listen for hardware back button events
- // register this back button action with a default priority
- _plt.registerBackButtonAction(this.goBack.bind(this));
- this._disableScrollAssist = _config.getBoolean('disableScrollAssist', false);
- const blurring = _config.getBoolean('inputBlurring', false);
- if (blurring) {
- this._enableInputBlurring();
- }
- (void 0) /* runInDev */;
- _config.setTransition('ios-transition', IOSTransition);
- _config.setTransition('md-transition', MDTransition);
- _config.setTransition('wp-transition', WPTransition);
- }
- /**
- * Sets the document title.
- * @param {string} val Value to set the document title to.
- */
- setTitle(val) {
- if (val !== this._title) {
- this._title = val;
- this._titleSrv.setTitle(val);
- }
- }
- /**
- * @hidden
- */
- setElementClass(className, isAdd) {
- this._appRoot.setElementClass(className, isAdd);
- }
- /**
- * @hidden
- * Sets if the app is currently enabled or not, meaning if it's
- * available to accept new user commands. For example, this is set to `false`
- * while views transition, a modal slides up, an action-sheet
- * slides up, etc. After the transition completes it is set back to `true`.
- * @param {boolean} isEnabled `true` for enabled, `false` for disabled
- * @param {number} duration When `isEnabled` is set to `false`, this argument
- * is used to set the maximum number of milliseconds that app will wait until
- * it will automatically enable the app again. It's basically a fallback incase
- * something goes wrong during a transition and the app wasn't re-enabled correctly.
- */
- setEnabled(isEnabled, duration = 700, minDuration = 0) {
- this._disTime = (isEnabled ? 0 : Date.now() + duration);
- if (this._clickBlock) {
- if (isEnabled) {
- // disable the click block if it's enabled, or the duration is tiny
- this._clickBlock.activate(false, CLICK_BLOCK_BUFFER_IN_MILLIS, minDuration);
- }
- else {
- // show the click block for duration + some number
- this._clickBlock.activate(true, duration + CLICK_BLOCK_BUFFER_IN_MILLIS, minDuration);
- }
- }
- }
- /**
- * @hidden
- * Toggles whether an application can be scrolled
- * @param {boolean} disableScroll when set to `false`, the application's
- * scrolling is enabled. When set to `true`, scrolling is disabled.
- */
- _setDisableScroll(disableScroll) {
- if (this._disableScrollAssist) {
- this._appRoot._disableScroll(disableScroll);
- }
- }
- /**
- * @hidden
- * Boolean if the app is actively enabled or not.
- * @return {boolean}
- */
- isEnabled() {
- const disTime = this._disTime;
- if (disTime === 0) {
- return true;
- }
- return (disTime < Date.now());
- }
- /**
- * @hidden
- */
- setScrolling() {
- this._scrollTime = Date.now() + ACTIVE_SCROLLING_TIME;
- this._didScroll = true;
- }
- /**
- * Boolean if the app is actively scrolling or not.
- * @return {boolean} returns true or false
- */
- isScrolling() {
- const scrollTime = this._scrollTime;
- if (scrollTime === 0) {
- return false;
- }
- if (scrollTime < Date.now()) {
- this._scrollTime = 0;
- return false;
- }
- return true;
- }
- /**
- * @return {NavController} Returns the first Active Nav Controller from the list. This method is deprecated
- */
- getActiveNav() {
- console.warn('(getActiveNav) is deprecated and will be removed in the next major release. Use getActiveNavs instead.');
- const navs = this.getActiveNavs();
- if (navs && navs.length) {
- return navs[0];
- }
- return null;
- }
- /**
- * @return {NavController[]} Returns the active NavControllers. Using this method is preferred when we need access to the top-level navigation controller while on the outside views and handlers like `registerBackButtonAction()`
- */
- getActiveNavs(rootNavId) {
- const portal = this._appRoot._getPortal(Constants.PORTAL_MODAL);
- if (portal.length() > 0) {
- return findTopNavs(portal);
- }
- if (!this._rootNavs || !this._rootNavs.size) {
- return [];
- }
- if (this._rootNavs.size === 1) {
- return findTopNavs(this._rootNavs.values().next().value);
- }
- if (rootNavId) {
- return findTopNavs(this._rootNavs.get(rootNavId));
- }
- // fallback to just using all root names
- let activeNavs = [];
- this._rootNavs.forEach(nav => {
- const topNavs = findTopNavs(nav);
- activeNavs = activeNavs.concat(topNavs);
- });
- return activeNavs;
- }
- getRootNav() {
- console.warn('(getRootNav) is deprecated and will be removed in the next major release. Use getRootNavById instead.');
- const rootNavs = this.getRootNavs();
- if (rootNavs.length === 0) {
- return null;
- }
- else if (rootNavs.length > 1) {
- console.warn('(getRootNav) there are multiple root navs, use getRootNavs instead');
- }
- return rootNavs[0];
- }
- getRootNavs() {
- const navs = [];
- this._rootNavs.forEach(nav => navs.push(nav));
- return navs;
- }
- /**
- * @return {NavController} Returns the root NavController
- */
- getRootNavById(navId) {
- return this._rootNavs.get(navId);
- }
- /**
- * @hidden
- */
- registerRootNav(nav) {
- this._rootNavs.set(nav.id, nav);
- }
- /**
- * @hidden
- */
- unregisterRootNav(nav) {
- this._rootNavs.delete(nav.id);
- }
- getActiveNavContainers() {
- // for each root nav container, get it's active nav
- let list = [];
- this._rootNavs.forEach((container) => {
- list = list.concat(findTopNavs(container));
- });
- return list;
- }
- /**
- * @hidden
- */
- present(enteringView, opts, appPortal) {
- (void 0) /* assert */;
- const portal = this._appRoot._getPortal(appPortal);
- // Set Nav must be set here in order to dimiss() work synchnously.
- // TODO: move _setNav() to the earlier stages of NavController. _queueTrns()
- enteringView._setNav(portal);
- opts.direction = DIRECTION_FORWARD;
- if (!opts.animation) {
- opts.animation = enteringView.getTransitionName(DIRECTION_FORWARD);
- }
- enteringView.setLeavingOpts({
- keyboardClose: opts.keyboardClose,
- direction: DIRECTION_BACK,
- animation: enteringView.getTransitionName(DIRECTION_BACK),
- ev: opts.ev
- });
- return portal.insertPages(-1, [enteringView], opts);
- }
- /**
- * @hidden
- */
- goBack() {
- if (this._menuCtrl && this._menuCtrl.isOpen()) {
- return this._menuCtrl.close();
- }
- const navPromise = this.navPop();
- if (!navPromise) {
- // no views to go back to
- // let's exit the app
- if (this._config.getBoolean('navExitApp', true)) {
- (void 0) /* console.debug */;
- this._plt.exitApp();
- }
- }
- return navPromise;
- }
- /**
- * @hidden
- */
- navPop() {
- if (!this._rootNavs || this._rootNavs.size === 0 || !this.isEnabled()) {
- return Promise.resolve();
- }
- // If there are any alert/actionsheet open, let's do nothing
- const portal = this._appRoot._getPortal(Constants.PORTAL_DEFAULT);
- if (portal.length() > 0) {
- return Promise.resolve();
- }
- let navToPop = null;
- let mostRecentVC = null;
- this._rootNavs.forEach((navContainer) => {
- const activeNavs = this.getActiveNavs(navContainer.id);
- const poppableNavs = activeNavs.map(activeNav => getPoppableNav(activeNav)).filter(nav => !!nav);
- poppableNavs.forEach(poppable => {
- const topViewController = poppable.last();
- if (poppable._isPortal || (topViewController && poppable.length() > 1 && (!mostRecentVC || topViewController._ts >= mostRecentVC._ts))) {
- mostRecentVC = topViewController;
- navToPop = poppable;
- }
- });
- });
- if (navToPop) {
- return navToPop.pop();
- }
- }
- /**
- * @hidden
- */
- _enableInputBlurring() {
- (void 0) /* console.debug */;
- let focused = true;
- const self = this;
- const platform = this._plt;
- platform.registerListener(platform.doc(), 'focusin', onFocusin, { capture: true, zone: false, passive: true });
- platform.registerListener(platform.doc(), 'touchend', onTouchend, { capture: false, zone: false, passive: true });
- function onFocusin() {
- focused = true;
- }
- function onTouchend(ev) {
- // if app did scroll return early
- if (self._didScroll) {
- self._didScroll = false;
- return;
- }
- const active = self._plt.getActiveElement();
- if (!active) {
- return;
- }
- // only blur if the active element is a text-input or a textarea
- if (SKIP_BLURRING.indexOf(active.tagName) === -1) {
- return;
- }
- // if the selected target is the active element, do not blur
- const tapped = ev.target;
- if (tapped === active) {
- return;
- }
- if (SKIP_BLURRING.indexOf(tapped.tagName) >= 0) {
- return;
- }
- // skip if div is a cover
- if (tapped.classList.contains('input-cover')) {
- return;
- }
- focused = false;
- // TODO: find a better way, why 50ms?
- platform.timeout(() => {
- if (!focused) {
- active.blur();
- }
- }, 50);
- }
- }
- getNavByIdOrName(id) {
- const navs = Array.from(this._rootNavs.values());
- for (const navContainer of navs) {
- const match = getNavByIdOrName(navContainer, id);
- if (match) {
- return match;
- }
- }
- return null;
- }
- }
- App.decorators = [
- { type: Injectable },
- ];
- /** @nocollapse */
- App.ctorParameters = () => [
- { type: Config, },
- { type: Platform, },
- { type: MenuController, decorators: [{ type: Optional },] },
- ];
- export function getNavByIdOrName(nav, id) {
- if (nav.id === id || nav.name === id) {
- return nav;
- }
- for (const child of nav.getAllChildNavs()) {
- const tmp = getNavByIdOrName(child, id);
- if (tmp) {
- return tmp;
- }
- }
- return null;
- }
- function getPoppableNav(nav) {
- if (!nav) {
- return null;
- }
- if (isTabs(nav)) {
- // tabs aren't a nav, so just call this function again immediately on the parent on tabs
- return getPoppableNav(nav.parent);
- }
- const len = nav.length();
- if (len > 1 || (nav._isPortal && len > 0)) {
- // this nav controller has more than one view
- // use this nav!
- return nav;
- }
- // try again using the parent nav (if there is one)
- return getPoppableNav(nav.parent);
- }
- export function findTopNavs(nav) {
- let containers = [];
- const childNavs = nav.getActiveChildNavs();
- if (!childNavs || !childNavs.length) {
- containers.push(nav);
- }
- else {
- childNavs.forEach(childNav => {
- const topNavs = findTopNavs(childNav);
- containers = containers.concat(topNavs);
- });
- }
- return containers;
- }
- const SKIP_BLURRING = ['INPUT', 'TEXTAREA', 'ION-INPUT', 'ION-TEXTAREA'];
- const ACTIVE_SCROLLING_TIME = 100;
- const CLICK_BLOCK_BUFFER_IN_MILLIS = 64;
- //# sourceMappingURL=app.js.map
|