ionic-angular
Version:
A powerful framework for building mobile and progressive web apps with JavaScript and Angular
423 lines • 15.4 kB
JavaScript
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
*/
var App = (function () {
function App(_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);
var 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.
*/
App.prototype.setTitle = function (val) {
if (val !== this._title) {
this._title = val;
this._titleSrv.setTitle(val);
}
};
/**
* @hidden
*/
App.prototype.setElementClass = function (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.
*/
App.prototype.setEnabled = function (isEnabled, duration, minDuration) {
if (duration === void 0) { duration = 700; }
if (minDuration === void 0) { 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.
*/
App.prototype._setDisableScroll = function (disableScroll) {
if (this._disableScrollAssist) {
this._appRoot._disableScroll(disableScroll);
}
};
/**
* @hidden
* Boolean if the app is actively enabled or not.
* @return {boolean}
*/
App.prototype.isEnabled = function () {
var disTime = this._disTime;
if (disTime === 0) {
return true;
}
return (disTime < Date.now());
};
/**
* @hidden
*/
App.prototype.setScrolling = function () {
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
*/
App.prototype.isScrolling = function () {
var 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
*/
App.prototype.getActiveNav = function () {
console.warn('(getActiveNav) is deprecated and will be removed in the next major release. Use getActiveNavs instead.');
var 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()`
*/
App.prototype.getActiveNavs = function (rootNavId) {
var 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
var activeNavs = [];
this._rootNavs.forEach(function (nav) {
var topNavs = findTopNavs(nav);
activeNavs = activeNavs.concat(topNavs);
});
return activeNavs;
};
App.prototype.getRootNav = function () {
console.warn('(getRootNav) is deprecated and will be removed in the next major release. Use getRootNavById instead.');
var 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];
};
App.prototype.getRootNavs = function () {
var navs = [];
this._rootNavs.forEach(function (nav) { return navs.push(nav); });
return navs;
};
/**
* @return {NavController} Returns the root NavController
*/
App.prototype.getRootNavById = function (navId) {
return this._rootNavs.get(navId);
};
/**
* @hidden
*/
App.prototype.registerRootNav = function (nav) {
this._rootNavs.set(nav.id, nav);
};
/**
* @hidden
*/
App.prototype.unregisterRootNav = function (nav) {
this._rootNavs.delete(nav.id);
};
App.prototype.getActiveNavContainers = function () {
// for each root nav container, get it's active nav
var list = [];
this._rootNavs.forEach(function (container) {
list = list.concat(findTopNavs(container));
});
return list;
};
/**
* @hidden
*/
App.prototype.present = function (enteringView, opts, appPortal) {
(void 0) /* assert */;
var 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
*/
App.prototype.goBack = function () {
if (this._menuCtrl && this._menuCtrl.isOpen()) {
return this._menuCtrl.close();
}
var 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
*/
App.prototype.navPop = function () {
var _this = this;
if (!this._rootNavs || this._rootNavs.size === 0 || !this.isEnabled()) {
return Promise.resolve();
}
// If there are any alert/actionsheet open, let's do nothing
var portal = this._appRoot._getPortal(Constants.PORTAL_DEFAULT);
if (portal.length() > 0) {
return Promise.resolve();
}
var navToPop = null;
var mostRecentVC = null;
this._rootNavs.forEach(function (navContainer) {
var activeNavs = _this.getActiveNavs(navContainer.id);
var poppableNavs = activeNavs.map(function (activeNav) { return getPoppableNav(activeNav); }).filter(function (nav) { return !!nav; });
poppableNavs.forEach(function (poppable) {
var 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
*/
App.prototype._enableInputBlurring = function () {
(void 0) /* console.debug */;
var focused = true;
var self = this;
var 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;
}
var 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
var 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(function () {
if (!focused) {
active.blur();
}
}, 50);
}
};
App.prototype.getNavByIdOrName = function (id) {
var navs = Array.from(this._rootNavs.values());
for (var _i = 0, navs_1 = navs; _i < navs_1.length; _i++) {
var navContainer = navs_1[_i];
var match = getNavByIdOrName(navContainer, id);
if (match) {
return match;
}
}
return null;
};
App.decorators = [
{ type: Injectable },
];
/** @nocollapse */
App.ctorParameters = function () { return [
{ type: Config, },
{ type: Platform, },
{ type: MenuController, decorators: [{ type: Optional },] },
]; };
return App;
}());
export { App };
export function getNavByIdOrName(nav, id) {
if (nav.id === id || nav.name === id) {
return nav;
}
for (var _i = 0, _a = nav.getAllChildNavs(); _i < _a.length; _i++) {
var child = _a[_i];
var 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);
}
var 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) {
var containers = [];
var childNavs = nav.getActiveChildNavs();
if (!childNavs || !childNavs.length) {
containers.push(nav);
}
else {
childNavs.forEach(function (childNav) {
var topNavs = findTopNavs(childNav);
containers = containers.concat(topNavs);
});
}
return containers;
}
var SKIP_BLURRING = ['INPUT', 'TEXTAREA', 'ION-INPUT', 'ION-TEXTAREA'];
var ACTIVE_SCROLLING_TIME = 100;
var CLICK_BLOCK_BUFFER_IN_MILLIS = 64;
//# sourceMappingURL=app.js.map