@@ -298,32 +298,67 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
298298
299299 } ,
300300
301- emit : function ( step , enteringData , leavingData ) {
302- var enteringScope = enteringEle . scope ( ) ,
303- leavingScope = leavingEle && leavingEle . scope ( ) ;
301+ emit : function ( step , enteringData , leavingData ) {
302+ var enteringScope = getScopeForElement ( enteringEle , enteringData ) ;
303+ var leavingScope = getScopeForElement ( leavingEle , leavingData ) ;
304304
305- if ( step == 'after' ) {
306- if ( enteringScope ) {
307- enteringScope . $emit ( '$ionicView.enter' , enteringData ) ;
308- }
305+ var prefixesAreEqual ;
309306
310- if ( leavingScope ) {
311- leavingScope . $emit ( '$ionicView.leave' , leavingData ) ;
307+ if ( ! enteringData . viewId || enteringData . abstractView ) {
308+ // it's an abstract view, so treat it accordingly
312309
313- } else if ( enteringScope && leavingData && leavingData . viewId ) {
314- enteringScope . $emit ( '$ionicNavView.leave' , leavingData ) ;
310+ // we only get access to the leaving scope once in the transition,
311+ // so dispatch all events right away if it exists
312+ if ( leavingScope ) {
313+ leavingScope . $emit ( '$ionicView.beforeLeave' , leavingData ) ;
314+ leavingScope . $emit ( '$ionicView.leave' , leavingData ) ;
315+ leavingScope . $emit ( '$ionicView.afterLeave' , leavingData ) ;
316+ leavingScope . $broadcast ( '$ionicParentView.beforeLeave' , leavingData ) ;
317+ leavingScope . $broadcast ( '$ionicParentView.leave' , leavingData ) ;
318+ leavingScope . $broadcast ( '$ionicParentView.afterLeave' , leavingData ) ;
315319 }
316320 }
321+ else {
322+ // it's a regular view, so do the normal process
323+ if ( step == 'after' ) {
324+ if ( enteringScope ) {
325+ enteringScope . $emit ( '$ionicView.enter' , enteringData ) ;
326+ enteringScope . $broadcast ( '$ionicParentView.enter' , enteringData ) ;
327+ }
317328
318- if ( enteringScope ) {
319- enteringScope . $emit ( '$ionicView.' + step + 'Enter' , enteringData ) ;
320- }
329+ if ( leavingScope ) {
330+ leavingScope . $emit ( '$ionicView.leave' , leavingData ) ;
331+ leavingScope . $broadcast ( '$ionicParentView.leave' , leavingData ) ;
332+ }
333+ else if ( enteringScope && leavingData && leavingData . viewId && enteringData . stateName !== leavingData . stateName ) {
334+ // we only want to dispatch this when we are doing a single-tier
335+ // state change such as changing a tab, so compare the state
336+ // for the same state-prefix but different suffix
337+ prefixesAreEqual = compareStatePrefixes ( enteringData . stateName , leavingData . stateName ) ;
338+ if ( prefixesAreEqual ) {
339+ enteringScope . $emit ( '$ionicNavView.leave' , leavingData ) ;
340+ }
341+ }
342+ }
321343
322- if ( leavingScope ) {
323- leavingScope . $emit ( '$ionicView.' + step + 'Leave' , leavingData ) ;
344+ if ( enteringScope ) {
345+ enteringScope . $emit ( '$ionicView.' + step + 'Enter' , enteringData ) ;
346+ enteringScope . $broadcast ( '$ionicParentView.' + step + 'Enter' , enteringData ) ;
347+ }
324348
325- } else if ( enteringScope && leavingData && leavingData . viewId ) {
326- enteringScope . $emit ( '$ionicNavView.' + step + 'Leave' , leavingData ) ;
349+ if ( leavingScope ) {
350+ leavingScope . $emit ( '$ionicView.' + step + 'Leave' , leavingData ) ;
351+ leavingScope . $broadcast ( '$ionicParentView.' + step + 'Leave' , leavingData ) ;
352+
353+ } else if ( enteringScope && leavingData && leavingData . viewId && enteringData . stateName !== leavingData . stateName ) {
354+ // we only want to dispatch this when we are doing a single-tier
355+ // state change such as changing a tab, so compare the state
356+ // for the same state-prefix but different suffix
357+ prefixesAreEqual = compareStatePrefixes ( enteringData . stateName , leavingData . stateName ) ;
358+ if ( prefixesAreEqual ) {
359+ enteringScope . $emit ( '$ionicNavView.' + step + 'Leave' , leavingData ) ;
360+ }
361+ }
327362 }
328363 } ,
329364
@@ -407,6 +442,15 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
407442 containerEle . innerHTML = viewLocals . $template ;
408443 if ( containerEle . children . length === 1 ) {
409444 containerEle . children [ 0 ] . classList . add ( 'pane' ) ;
445+ if ( viewLocals . $$state && viewLocals . $$state . self && viewLocals . $$state . self [ 'abstract' ] ) {
446+ angular . element ( containerEle . children [ 0 ] ) . attr ( "abstract" , "true" ) ;
447+ }
448+ else {
449+ if ( viewLocals . $$state && viewLocals . $$state . self ) {
450+ angular . element ( containerEle . children [ 0 ] ) . attr ( "state" , viewLocals . $$state . self . name ) ;
451+ }
452+
453+ }
410454 return jqLite ( containerEle . children [ 0 ] ) ;
411455 }
412456 }
@@ -491,4 +535,67 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
491535 }
492536 }
493537
538+ function compareStatePrefixes ( enteringStateName , exitingStateName ) {
539+ var enteringStateSuffixIndex = enteringStateName . lastIndexOf ( '.' ) ;
540+ var exitingStateSuffixIndex = exitingStateName . lastIndexOf ( '.' ) ;
541+
542+ // if either of the prefixes are empty, just return false
543+ if ( enteringStateSuffixIndex < 0 || exitingStateSuffixIndex < 0 ) {
544+ return false ;
545+ }
546+
547+ var enteringPrefix = enteringStateName . substring ( 0 , enteringStateSuffixIndex ) ;
548+ var exitingPrefix = exitingStateName . substring ( 0 , exitingStateSuffixIndex ) ;
549+
550+ return enteringPrefix === exitingPrefix ;
551+ }
552+
553+ function getScopeForElement ( element , stateData ) {
554+ if ( ! element ) {
555+ return null ;
556+ }
557+ // check if it's abstract
558+ var attributeValue = angular . element ( element ) . attr ( "abstract" ) ;
559+ var stateValue = angular . element ( element ) . attr ( "state" ) ;
560+
561+ if ( attributeValue !== "true" ) {
562+ // it's not an abstract view, so make sure the element
563+ // matches the state. Due to abstract view weirdness,
564+ // sometimes it doesn't. If it doesn't, don't dispatch events
565+ // so leave the scope undefined
566+ if ( stateValue === stateData . stateName ) {
567+ return angular . element ( element ) . scope ( ) ;
568+ }
569+ return null ;
570+ }
571+ else {
572+ // it is an abstract element, so look for element with the "state" attributeValue
573+ // set to the name of the stateData state
574+ var elements = aggregateNavViewChildren ( element ) ;
575+ for ( var i = 0 ; i < elements . length ; i ++ ) {
576+ var state = angular . element ( elements [ i ] ) . attr ( "state" ) ;
577+ if ( state === stateData . stateName ) {
578+ stateData . abstractView = true ;
579+ return angular . element ( elements [ i ] ) . scope ( ) ;
580+ }
581+ }
582+ // we didn't find a match, so return null
583+ return null ;
584+ }
585+ }
586+
587+ function aggregateNavViewChildren ( element ) {
588+ var aggregate = [ ] ;
589+ var navViews = angular . element ( element ) . find ( "ion-nav-view" ) ;
590+ for ( var i = 0 ; i < navViews . length ; i ++ ) {
591+ var children = angular . element ( navViews [ i ] ) . children ( ) ;
592+ var childrenAggregated = [ ] ;
593+ for ( var j = 0 ; j < children . length ; j ++ ) {
594+ childrenAggregated = childrenAggregated . concat ( children [ j ] ) ;
595+ }
596+ aggregate = aggregate . concat ( childrenAggregated ) ;
597+ }
598+ return aggregate ;
599+ }
600+
494601} ] ) ;
0 commit comments