import { EventEmitter , Injectable , Optional } from '@angular/core' ; import { DOCUMENT , Title } from '@angular/platform-browser' ; import { IonicApp } from './app-root' ; import * as Constants from './app-constants' ; import { ClickBlock } from './click-block' ; import { assert , runInDev } from '../../util/util' ; import { Config } from '../../config/config' ; import { DIRECTION_BACK , DIRECTION_FORWARD , NavOptions , isTabs } from '../../navigation/nav-util' ; import { MenuController } from './menu-controller' ; import { NavigationContainer } from '../../navigation/navigation-container' ; import { NavControllerBase } from '../../navigation/nav-controller-base' ; import { Platform } from '../../platform/platform' ; import { ViewController } from '../../navigation/view-controller' ; import { IOSTransition } from '../../transitions/transition-ios' ; import { MDTransition } from '../../transitions/transition-md' ; import { WPTransition } from '../../transitions/transition-wp' ; name description @ Injectable ( ) export class App { private _disTime : number = 0 ; private _scrollTime : number = 0 ; private _title : string = '' ; private _titleSrv : Title = new Title ( DOCUMENT ) ; private _rootNavs = new Map < string , NavigationContainer > ( ) ; private _disableScrollAssist : boolean ; private _didScroll = false ; hidden _clickBlock : ClickBlock ; hidden _appRoot : IonicApp ; returns { Observable } viewDidLoad : EventEmitter < ViewController > = new EventEmitter ( ) ; returns { Observable } viewWillEnter : EventEmitter < ViewController > = new EventEmitter ( ) ; returns { Observable } viewDidEnter : EventEmitter < ViewController > = new EventEmitter ( ) ; returns { Observable } viewWillLeave : EventEmitter < ViewController > = new EventEmitter ( ) ; returns { Observable } viewDidLeave : EventEmitter < ViewController > = new EventEmitter ( ) ; returns { Observable } viewWillUnload : EventEmitter < ViewController > = new EventEmitter ( ) ; constructor ( private _config , private _plt , @ Optional ( ) private _menuCtrl ? ) { _plt . registerBackButtonAction ( this . goBack . bind ( this ) ) ; this . _disableScrollAssist = _config . getBoolean ( 'disableScrollAssist' , false ) ; const blurring = _config . getBoolean ( 'inputBlurring' , false ) ; if ( blurring ) { this . _enableInputBlurring ( ) ; } runInDev ( ( ) => { const win = < any > _plt . win ( ) ; if ( ! win [ 'HWBackButton' ] ) { win [ 'HWBackButton' ] = ( ) => { let p = this . goBack ( ) ; p && p . catch ( ( ) => console . debug ( 'hardware go back cancelled' ) ) ; return p ; } ; } } ) ; _config . setTransition ( 'ios-transition' , IOSTransition ) ; _config . setTransition ( 'md-transition' , MDTransition ) ; _config . setTransition ( 'wp-transition' , WPTransition ) ; } param { string } val setTitle ( val ) { if ( val !== this . _title ) { this . _title = val ; this . _titleSrv . setTitle ( val ) ; } } hidden setElementClass ( className , isAdd ) { this . _appRoot . setElementClass ( className , isAdd ) ; } hidden param { boolean } isEnabled param { number } duration setEnabled ( isEnabled , duration = 700 , minDuration = 0 ) { this . _disTime = ( isEnabled ? 0 : Date . now ( ) + duration ) ; if ( this . _clickBlock ) { if ( isEnabled ) { this . _clickBlock . activate ( false , CLICK_BLOCK_BUFFER_IN_MILLIS , minDuration ) ; } else { this . _clickBlock . activate ( true , duration + CLICK_BLOCK_BUFFER_IN_MILLIS , minDuration ) ; } } } hidden param { boolean } disableScroll _setDisableScroll ( disableScroll ) { if ( this . _disableScrollAssist ) { this . _appRoot . _disableScroll ( disableScroll ) ; } } hidden 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 ; } return { boolean } isScrolling ( ) { const scrollTime = this . _scrollTime ; if ( scrollTime === 0 ) { return false ; } if ( scrollTime < Date . now ( ) ) { this . _scrollTime = 0 ; return false ; } return true ; } return { NavController } 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 [ ] } getActiveNavs ( rootNavId ? ) { const portal = this . _appRoot . _getPortal ( Constants . PORTAL_MODAL ) ; if ( portal . length ( ) > 0 ) { return < NavControllerBase [ ] > findTopNavs ( portal ) ; } if ( ! this . _rootNavs || ! this . _rootNavs . size ) { return [ ] ; } if ( this . _rootNavs . size === 1 ) { return < NavControllerBase [ ] > findTopNavs ( this . _rootNavs . values ( ) . next ( ) . value ) ; } if ( rootNavId ) { return < NavControllerBase [ ] > findTopNavs ( this . _rootNavs . get ( rootNavId ) ) ; } let activeNavs = [ ] ; this . _rootNavs . forEach ( nav => { const topNavs = findTopNavs ( nav ) ; activeNavs = activeNavs . concat ( topNavs ) ; } ) ; return < NavControllerBase [ ] > 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 } 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 ( ) { let list = [ ] ; this . _rootNavs . forEach ( ( container ) => { list = list . concat ( findTopNavs ( container ) ) ; } ) ; return list ; } hidden present ( enteringView , opts , appPortal ? ) { assert ( enteringView . isOverlay , 'presented view controller needs to be an overlay' ) ; const portal = this . _appRoot . _getPortal ( appPortal ) ; 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 ) { if ( this . _config . getBoolean ( 'navExitApp' , true ) ) { console . debug ( 'app, goBack exitApp' ) ; this . _plt . exitApp ( ) ; } } return navPromise ; } hidden navPop ( ) { if ( ! this . _rootNavs || this . _rootNavs . size === 0 || ! this . isEnabled ( ) ) { return Promise . resolve ( ) ; } 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 ( ) { console . debug ( 'App: _enableInputBlurring' ) ; 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 ( self . _didScroll ) { self . _didScroll = false ; return ; } const active = < HTMLElement > self . _plt . getActiveElement ( ) ; if ( ! active ) { return ; } if ( SKIP_BLURRING . indexOf ( active . tagName ) === - 1 ) { return ; } const tapped = ev . target ; if ( tapped === active ) { return ; } if ( SKIP_BLURRING . indexOf ( tapped . tagName ) >= 0 ) { return ; } if ( tapped . classList . contains ( 'input-cover' ) ) { return ; } focused = false ; 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 ; } } 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 ) ) { return getPoppableNav ( nav . parent ) ; } const len = nav . length ( ) ; if ( len > 1 || ( nav . _isPortal && len > 0 ) ) { return nav ; } 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 ;