mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
422 lines
13 KiB
JavaScript
422 lines
13 KiB
JavaScript
angular.module('ionic.service.view', ['ui.router'])
|
|
|
|
|
|
.run( ['$rootScope', '$state', '$location', '$document',
|
|
function( $rootScope, $state, $location, $document) {
|
|
|
|
// init the variables that keep track of the view history
|
|
$rootScope.$viewHistory = {
|
|
histories: { root: { historyId: 'root', parentHistoryId: null, stack: [], cursor: -1 } },
|
|
backView: null,
|
|
forwardView: null,
|
|
currentView: null
|
|
};
|
|
|
|
$rootScope.$on('viewState.changeHistory', function(e, data) {
|
|
if(!data) return;
|
|
|
|
var hist = (data.historyId ? $rootScope.$viewHistory.histories[ data.historyId ] : null );
|
|
if(hist && hist.cursor > -1 && hist.cursor < hist.stack.length) {
|
|
// the history they're going to already exists
|
|
// go to it's last view in its stack
|
|
var view = hist.stack[ hist.cursor ];
|
|
return view.go(data);
|
|
}
|
|
|
|
// this history does not have a URL, but it does have a uiSref
|
|
// figure out its URL from the uiSref
|
|
if(!data.url && data.uiSref) {
|
|
data.url = $state.href(data.uiSref);
|
|
}
|
|
|
|
if(data.url) {
|
|
// don't let it start with a #, messes with $location.url()
|
|
if(data.url.indexOf('#') === 0) {
|
|
data.url = data.url.replace('#', '');
|
|
}
|
|
if(data.url !== $location.url()) {
|
|
// we've got a good URL, ready GO!
|
|
$location.url(data.url);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Set the document title when a new view is shown
|
|
$rootScope.$on('viewState.viewEnter', function(e, data) {
|
|
if(data && data.title) {
|
|
$document[0].title = data.title;
|
|
}
|
|
});
|
|
|
|
}])
|
|
|
|
.factory('$ionicViewService', ['$rootScope', '$state', '$location', '$window', '$injector',
|
|
function( $rootScope, $state, $location, $window, $injector) {
|
|
var $animate = $injector.has('$animate') ? $injector.get('$animate') : false;
|
|
|
|
var View = function(){};
|
|
View.prototype.initialize = function(data) {
|
|
if(data) {
|
|
for(var name in data) this[name] = data[name];
|
|
return this;
|
|
}
|
|
return null;
|
|
};
|
|
View.prototype.go = function(opts) {
|
|
|
|
if(this.stateName) {
|
|
return $state.go(this.stateName, this.stateParams);
|
|
}
|
|
|
|
if(this.url && this.url !== $location.url()) {
|
|
|
|
if($rootScope.$viewHistory.backView === this) {
|
|
return $window.history.go(-1);
|
|
} else if($rootScope.$viewHistory.forwardView === this) {
|
|
return $window.history.go(1);
|
|
}
|
|
|
|
$location.url(this.url);
|
|
return;
|
|
}
|
|
|
|
return null;
|
|
};
|
|
View.prototype.destory = function() {
|
|
if(this.scope) {
|
|
this.scope.destory && this.scope.destory();
|
|
this.scope = null;
|
|
}
|
|
};
|
|
|
|
function createViewId(stateId) {
|
|
return ('_' + stateId + '_' + Math.round(Math.random() * 99999999)).replace(/\./g, '_');
|
|
}
|
|
|
|
return {
|
|
|
|
register: function(containerScope) {
|
|
var viewHistory = $rootScope.$viewHistory,
|
|
currentStateId = this.getCurrentStateId(),
|
|
hist = this._getHistory(containerScope),
|
|
currentView = viewHistory.currentView,
|
|
backView = viewHistory.backView,
|
|
forwardView = viewHistory.forwardView,
|
|
rsp = {
|
|
viewId: null,
|
|
navAction: null,
|
|
navDirection: null,
|
|
historyId: hist.historyId
|
|
};
|
|
|
|
if(currentView &&
|
|
currentView.stateId === currentStateId &&
|
|
currentView.historyId === hist.historyId) {
|
|
// do nothing if its the same stateId in the same history
|
|
rsp.navAction = 'noChange';
|
|
return rsp;
|
|
}
|
|
|
|
if(backView && backView.stateId === currentStateId) {
|
|
// they went back one, set the old current view as a forward view
|
|
rsp.viewId = backView.viewId;
|
|
rsp.navAction = 'moveBack';
|
|
if(backView.historyId === currentView.historyId) {
|
|
// went back in the same history
|
|
rsp.navDirection = 'back';
|
|
}
|
|
|
|
} else if(forwardView && forwardView.stateId === currentStateId) {
|
|
// they went to the forward one, set the forward view to no longer a forward view
|
|
rsp.viewId = forwardView.viewId;
|
|
rsp.navAction = 'moveForward';
|
|
if(forwardView.historyId === currentView.historyId) {
|
|
rsp.navDirection = 'forward';
|
|
}
|
|
|
|
var parentHistory = this._getParentHistoryObj(containerScope);
|
|
if(forwardView.historyId && parentHistory.scope) {
|
|
// if a history has already been created by the forward view then make sure it stays the same
|
|
parentHistory.scope.$historyId = forwardView.historyId;
|
|
rsp.historyId = forwardView.historyId;
|
|
}
|
|
|
|
} else if(currentView && currentView.historyId !== hist.historyId &&
|
|
hist.cursor > -1 && hist.stack.length > 0 && hist.cursor < hist.stack.length &&
|
|
hist.stack[hist.cursor].stateId === currentStateId) {
|
|
// they just changed to a different history and the history already has views in it
|
|
rsp.viewId = hist.stack[hist.cursor].viewId;
|
|
rsp.navAction = 'moveBack';
|
|
|
|
} else {
|
|
|
|
// set a new unique viewId
|
|
rsp.viewId = createViewId(currentStateId);
|
|
|
|
if(currentView) {
|
|
// set the forward view if there is a current view (ie: if its not the first view)
|
|
currentView.forwardViewId = rsp.viewId;
|
|
|
|
// its only moving forward if its in the same history
|
|
if(hist.historyId === currentView.historyId) {
|
|
rsp.navDirection = 'forward';
|
|
}
|
|
rsp.navAction = 'newView';
|
|
|
|
// check if there is a new forward view
|
|
if(forwardView && currentView.stateId !== forwardView.stateId) {
|
|
// they navigated to a new view but the stack already has a forward view
|
|
// since its a new view remove any forwards that existed
|
|
var forwardsHistory = this._getView(forwardView.historyId);
|
|
if(forwardsHistory) {
|
|
// the forward has a history
|
|
for(var x=forwardsHistory.stack.length - 1; x >= forwardView.index; x--) {
|
|
// starting from the end destory all forwards in this history from this point
|
|
forwardsHistory.stack[x].destory();
|
|
forwardsHistory.stack.splice(x);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// there's no current view, so this must be the initial view
|
|
rsp.navAction = 'initialView';
|
|
}
|
|
|
|
// add the new view to the stack
|
|
viewHistory.histories[rsp.viewId] = this.createView({
|
|
viewId: rsp.viewId,
|
|
index: hist.stack.length,
|
|
historyId: hist.historyId,
|
|
backViewId: (currentView && currentView.viewId ? currentView.viewId : null),
|
|
forwardViewId: null,
|
|
stateId: currentStateId,
|
|
stateName: this.getCurrentStateName(),
|
|
stateParams: this.getCurrentStateParams(),
|
|
url: $location.url()
|
|
});
|
|
|
|
// add the new view to this history's stack
|
|
hist.stack.push(viewHistory.histories[rsp.viewId]);
|
|
}
|
|
|
|
this.setNavViews(rsp.viewId);
|
|
|
|
hist.cursor = viewHistory.currentView.index;
|
|
|
|
return rsp;
|
|
},
|
|
|
|
setNavViews: function(viewId) {
|
|
var viewHistory = $rootScope.$viewHistory;
|
|
|
|
viewHistory.currentView = this._getView(viewId);
|
|
viewHistory.backView = this._getBackView(viewHistory.currentView);
|
|
viewHistory.forwardView = this._getForwardView(viewHistory.currentView);
|
|
|
|
$rootScope.$broadcast('$viewHistory.historyChange', {
|
|
showBack: (viewHistory.backView && viewHistory.backView.historyId === viewHistory.currentView.historyId)
|
|
});
|
|
},
|
|
|
|
registerHistory: function(scope) {
|
|
scope.$historyId = 'h' + Math.round(Math.random() * 99999999999);
|
|
},
|
|
|
|
createView: function(data) {
|
|
var newView = new View();
|
|
return newView.initialize(data);
|
|
},
|
|
|
|
getCurrentView: function() {
|
|
return $rootScope.$viewHistory.currentView;
|
|
},
|
|
|
|
getBackView: function() {
|
|
return $rootScope.$viewHistory.backView;
|
|
},
|
|
|
|
getForwardView: function() {
|
|
return $rootScope.$viewHistory.forwardView;
|
|
},
|
|
|
|
getNavDirection: function() {
|
|
return $rootScope.$viewHistory.navDirection;
|
|
},
|
|
|
|
getCurrentStateName: function() {
|
|
return ($state && $state.current ? $state.current.name : null);
|
|
},
|
|
|
|
isCurrentStateNavView: function(navView) {
|
|
return ($state &&
|
|
$state.current &&
|
|
$state.current.views &&
|
|
$state.current.views[navView] ? true : false);
|
|
},
|
|
|
|
getCurrentStateParams: function() {
|
|
var rtn;
|
|
if ($state && $state.params) {
|
|
for(var key in $state.params) {
|
|
if($state.params.hasOwnProperty(key)) {
|
|
rtn = rtn || {};
|
|
rtn[key] = $state.params[key];
|
|
}
|
|
}
|
|
}
|
|
return rtn;
|
|
},
|
|
|
|
getCurrentStateId: function() {
|
|
var id;
|
|
if($state && $state.current && $state.current.name) {
|
|
id = $state.current.name;
|
|
if($state.params) {
|
|
for(var key in $state.params) {
|
|
if($state.params.hasOwnProperty(key) && $state.params[key]) {
|
|
id += "_" + key + "=" + $state.params[key];
|
|
}
|
|
}
|
|
}
|
|
return id;
|
|
}
|
|
// if something goes wrong make sure its got a unique stateId
|
|
return 'r' + Math.round(Math.random() * 9999999);
|
|
},
|
|
|
|
_getView: function(viewId) {
|
|
return (viewId ? $rootScope.$viewHistory.histories[ viewId ] : null );
|
|
},
|
|
|
|
_getBackView: function(view) {
|
|
return (view ? this._getView(view.backViewId) : null );
|
|
},
|
|
|
|
_getForwardView: function(view) {
|
|
return (view ? this._getView(view.forwardViewId) : null );
|
|
},
|
|
|
|
_getHistory: function(scope) {
|
|
var histObj = this._getParentHistoryObj(scope);
|
|
|
|
if( !$rootScope.$viewHistory.histories[ histObj.historyId ] ) {
|
|
// this history object exists in parent scope, but doesn't
|
|
// exist in the history data yet
|
|
$rootScope.$viewHistory.histories[ histObj.historyId ] = {
|
|
historyId: histObj.historyId,
|
|
parentHistoryId: this._getParentHistoryObj(histObj.scope.$parent).historyId,
|
|
stack: [],
|
|
cursor: -1
|
|
};
|
|
}
|
|
|
|
return $rootScope.$viewHistory.histories[ histObj.historyId ];
|
|
},
|
|
|
|
_getParentHistoryObj: function(scope) {
|
|
var parentScope = scope;
|
|
while(parentScope) {
|
|
if(parentScope.hasOwnProperty('$historyId')) {
|
|
// this parent scope has a historyId
|
|
return { historyId: parentScope.$historyId, scope: parentScope };
|
|
}
|
|
// nothing found keep climbing up
|
|
parentScope = parentScope.$parent;
|
|
}
|
|
// no history for for the parent, use the root
|
|
return { historyId: 'root', scope: $rootScope };
|
|
},
|
|
|
|
transition: function(opts) {
|
|
if(!opts || !opts.enteringElement) return;
|
|
|
|
if (opts.leavingScope) {
|
|
opts.leavingScope.$destroy();
|
|
opts.leavingScope = null;
|
|
}
|
|
|
|
// use the directive's animation attribute first
|
|
// if it doesn't exist, then use the given animation
|
|
var animationClass = opts.animation || getAnimationClass();
|
|
|
|
if($animate && animationClass && opts.doAnimation !== false && opts.navDirection) {
|
|
// set the animation we're gonna use
|
|
this.setAnimationClass(opts.parentElement, animationClass, opts.navDirection);
|
|
opts.enteringElement.addClass('ng-enter');
|
|
|
|
// disable any pointer-events from being able to fire
|
|
document.body.classList.add('disable-pointer-events');
|
|
|
|
// start the animations
|
|
if(opts.leavingElement) {
|
|
$animate.leave(opts.leavingElement, function() {
|
|
// re-enable pointer-events
|
|
document.body.classList.remove('disable-pointer-events');
|
|
});
|
|
}
|
|
$animate.enter(opts.enteringElement, opts.parentElement);
|
|
|
|
} else {
|
|
// no animation, just plain ol' add/remove DOM elements
|
|
if(opts.leavingElement) {
|
|
opts.leavingElement.remove();
|
|
}
|
|
opts.parentElement.append(opts.enteringElement);
|
|
}
|
|
|
|
function getAnimationClass(){
|
|
// go up the ancestors looking for an animation value
|
|
var climbScope = opts.enteringScope;
|
|
while(climbScope) {
|
|
if(climbScope.animation) {
|
|
return climbScope.animation;
|
|
}
|
|
climbScope = climbScope.$parent;
|
|
}
|
|
}
|
|
},
|
|
|
|
setAnimationClass: function(element, animationClass, navDirection) {
|
|
// add the animation we're gonna use
|
|
element[0].classList.add(animationClass);
|
|
|
|
if(navDirection === 'back') {
|
|
// animate backward
|
|
element[0].classList.add('reverse');
|
|
} else {
|
|
// defaults to animate forward
|
|
// make sure the reverse class isn't already added
|
|
element[0].classList.remove('reverse');
|
|
}
|
|
},
|
|
|
|
clearHistory: function() {
|
|
var historyId, x, view,
|
|
histories = $rootScope.$viewHistory.histories,
|
|
currentView = $rootScope.$viewHistory.currentView;
|
|
|
|
for(historyId in histories) {
|
|
|
|
if(histories[historyId].stack) {
|
|
histories[historyId].stack = [];
|
|
histories[historyId].cursor = -1;
|
|
}
|
|
|
|
if(currentView.historyId === historyId) {
|
|
currentView.backViewId = null;
|
|
currentView.forwardViewId = null;
|
|
histories[historyId].stack.push(currentView);
|
|
} else if(histories[historyId].destroy) {
|
|
histories[historyId].destroy();
|
|
}
|
|
|
|
}
|
|
|
|
this.setNavViews(currentView.viewId);
|
|
}
|
|
|
|
};
|
|
|
|
}]);
|