| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628 | /** * <div id="app" class="mui-views">	<div class="mui-view">		<div class="mui-navbar">		</div>		<div class="mui-pages">		</div>	</div></div> * @param {Object} $ * @param {Object} window */(function($, window) {	var CLASS_LEFT = $.className('left');	var CLASS_CENTER = $.className('center');	var CLASS_RIGHT = $.className('right');	var CLASS_PAGE = $.className('page');	var CLASS_PAGE_LEFT = $.className('page-left');	var CLASS_PAGE_CENTER = $.className('page-center');	var CLASS_NAVBAR_LEFT = $.className('navbar-left');	var CLASS_NAVBAR_CENTER = $.className('navbar-center');	var CLASS_PAGE_SHADOW = $.className('page-shadow');	var CLASS_TRANSITIONING = $.className('transitioning');	var SELECTOR_LEFT = '.' + CLASS_LEFT;	var SELECTOR_CENTER = '.' + CLASS_CENTER;	var SELECTOR_RIGHT = '.' + CLASS_RIGHT;	var SELECTOR_ICON = $.classSelector('.icon');	var SELECTOR_NAVBAR = $.classSelector('.navbar');	var SELECTOR_NAVBAR_INNER = $.classSelector('.navbar-inner');	var SELECTOR_PAGES = $.classSelector('.pages');	var SELECTOR_BTN_NAV = $.classSelector('.btn-nav');	var SELECTOR_PAGE_LEFT = '.' + CLASS_PAGE_LEFT;	var SELECTOR_PAGE_CENTER = '.' + CLASS_PAGE_CENTER;	var SELECTOR_NAVBAR_LEFT = '.' + CLASS_NAVBAR_LEFT;	var SELECTOR_NAVBAR_CENTER = '.' + CLASS_NAVBAR_CENTER;	var View = $.Class.extend({		init: function(element, options) {			this.view = this.element = element;			this.options = $.extend({				animateNavbar: 'ios', //ios				swipeBackPageActiveArea: 30,				hardwareAccelerated: true			}, options);			this.navbars = this.view.querySelector(SELECTOR_NAVBAR);			this.pages = this.view.querySelector(SELECTOR_PAGES);			this.history = []; //history			this.maxScrollX = this.view.offsetWidth;			this.x = this.y = 0;			this.translateZ = this.options.hardwareAccelerated ? ' translateZ(0)' : '';			this.ratio = 0;			this.isBack = true;			this.moved = this.dragging = false;			this.activeNavbar = this.previousNavbar = null;			this.activePage = this.previousPage = null;			this._initPageEventMethod();			this._initDefaultPage();			this.navbars && this._initNavBar();			this.initEvent();		},		_initPageEventMethod: function() {			var self = this;			$.each(['onPageBeforeShow', 'onPageShow', 'onPageBeforeBack', 'onPageBack'], function(index, event) {				self[event + 'Callbacks'] = {};				self[event] = function(page, callback) {					var eventCallbacks = event + 'Callbacks';					if (!self[eventCallbacks].hasOwnProperty(page)) {						self[eventCallbacks][page] = [callback];					} else {						self[eventCallbacks][page].push(callback);					}				};			});		},		_initDefaultPage: function() {			var defaultPage = document.querySelector(this.options.defaultPage);			if (defaultPage) {				this._appendPage(defaultPage);			} else {				throw new Error('defaultPage[' + this.options.defaultPage + '] does not exist');			}		},		initEvent: function() {			this.view.addEventListener('click', this);			this.view.addEventListener('tap', this);			this.pages.addEventListener('drag', this);			this.pages.addEventListener('dragend', this);			this.pages.addEventListener('webkitTransitionEnd', this);		},		handleEvent: function(event) {			switch (event.type) {				case 'click':					this._click(event);					break;				case 'tap':					this._tap(event);					break;				case 'drag':					this._drag(event);					break;				case 'dragend':					this._dragend(event);					break;				case 'webkitTransitionEnd':					this._webkitTransitionEnd(event);					break;			}		},		shadow: function() {			var shadow = document.createElement('div');			shadow.className = CLASS_PAGE_SHADOW;			return shadow;		}(),		_removePage: function(page, navbar) {			navbar && this._removeNavbar(page, navbar);			document.body.appendChild(page);			this._cleanPageClass(page);		},		_prependPage: function(page) {			var navbar = page.querySelector(SELECTOR_NAVBAR_INNER);			navbar && this._prependNavbar(navbar);			page.classList.add(CLASS_PAGE_LEFT);			this.pages.insertBefore(page, this.pages.firstElementChild);		},		_appendPage: function(page) {			var navbar = page.querySelector(SELECTOR_NAVBAR_INNER);			navbar && this._appendNavbar(navbar);			page.classList.add(CLASS_PAGE_CENTER);			this.pages.appendChild(page);		},		_removeNavbar: function(page, navbar) {			page.insertBefore(navbar, page.firstElementChild);			this._cleanNavbarClass(navbar);		},		_prependNavbar: function(navbar) {			navbar.classList.add(CLASS_NAVBAR_LEFT);			this.navbars.insertBefore(navbar, this.navbars.firstElementChild);		},		_appendNavbar: function(navbar) {			navbar.classList.add(CLASS_NAVBAR_CENTER);			this.navbars.appendChild(navbar);		},		_cleanPageClass: function(page) {			page.classList.remove(CLASS_PAGE_CENTER);			page.classList.remove(CLASS_PAGE_LEFT);		},		_cleanNavbarClass: function(navbar) {			navbar.classList.remove(CLASS_NAVBAR_CENTER);			navbar.classList.remove(CLASS_NAVBAR_LEFT);		},		_tap: function(event) {			var target = event.target;			for (; target && target !== document; target = target.parentNode) {				if (target.tagName === 'A' && target.hash) {					var page = document.getElementById(target.hash.replace('#', ''));					if (page && page.classList.contains(CLASS_PAGE)) {						event.stopPropagation();						event.detail.gesture.preventDefault();						this.go(target.hash);						break;					}				}			}		},		_click: function(event) {			var target = event.target;			for (; target && target !== document; target = target.parentNode) {				if (target.tagName === 'A' && target.hash) {					var page = document.getElementById(target.hash.replace('#', ''));					if (page && page.classList.contains(CLASS_PAGE)) {						event.preventDefault();						break;					}				}			}		},		_cleanStyle: function(el) {			if (el) {				el.style.webkitTransform = '';				el.style.opacity = '';			}		},		_isAnimateNavbarIOS: function() {			return !$.os.android && this.options.animateNavbar === 'ios';		},		_webkitTransitionEnd: function(event) {			this.dragging = this.moved = false;			if (this.activePage !== event.target) {				return;			}			this.isInTransition = false;			this.shadow.parentNode === this.activePage && this.activePage.removeChild(this.shadow);			this.previousPageClassList.remove(CLASS_TRANSITIONING);			this.activePageClassList.remove(CLASS_TRANSITIONING);			var self = this;			if (this._isAnimateNavbarIOS() && this.previousNavElements && this.activeNavElements) {				var isBack = this.isBack;				$.each(this.previousNavElements, function(i, el) {					el.classList.remove(CLASS_TRANSITIONING);					isBack && self._cleanStyle(el);				});				$.each(this.activeNavElements, function(i, el) {					el.classList.remove(CLASS_TRANSITIONING);					self._cleanStyle(el);				});				if (this.previousNavBackIcon) {					this.previousNavBackIcon.classList.remove(CLASS_TRANSITIONING);					isBack && this._cleanStyle(this.previousNavBackIcon);				}				if (this.activeNavBackIcon) {					this.activeNavBackIcon.classList.remove(CLASS_TRANSITIONING);					this._cleanStyle(this.activeNavBackIcon);				}			} else {				this.previousNavbar && this.previousNavbar.classList.remove(CLASS_TRANSITIONING);				this.activeNavbar && this.activeNavbar.classList.remove(CLASS_TRANSITIONING);				this._cleanStyle(this.previousNavbar);				this._cleanStyle(this.activeNavbar);			}			this._cleanStyle(this.previousPage);			this._cleanStyle(this.activePage);			if (this.ratio <= 0.5) {				return;			}			if (this.isBack) {				this._removePage(this.activePage, this.activeNavbar);				this.previousPageClassList.remove(CLASS_PAGE_LEFT);				this.previousPageClassList.add(CLASS_PAGE_CENTER);				if (this.previousNavbar) {					this.previousNavbar.classList.remove(CLASS_NAVBAR_LEFT);					this.previousNavbar.classList.add(CLASS_NAVBAR_CENTER);				}				if (this.history.length > 0) {					this._prependPage(this.history.pop());				}				this.navbars && this._initNavBar();				this._trigger('pageBack', this.activePage);				this._trigger('pageShow', this.previousPage);			} else {				this.previousPageClassList.add(CLASS_PAGE_LEFT);				this.activePageClassList.add(CLASS_PAGE_CENTER);				this._trigger('pageShow', this.activePage);			}		},		_trigger: function(eventType, page) {			var eventCallbacks = 'on' + eventType.charAt(0).toUpperCase() + eventType.slice(1) + 'Callbacks';			if (this[eventCallbacks].hasOwnProperty(page.id)) {				var callbacks = this[eventCallbacks][page.id];				var event = new CustomEvent(eventType, {					detail: {						page: page					},					bubbles: true,					cancelable: true				});				for (var len = callbacks.length; len--;) {					callbacks[len].call(this, event);				}			}			$.trigger(this.view, eventType, {				page: page			});		},		_initPageTransform: function() {			this.previousPage = this.pages.querySelector(SELECTOR_PAGE_LEFT);			this.activePage = this.pages.querySelector(SELECTOR_PAGE_CENTER);			if (this.previousPage && this.activePage) {				this.activePage.appendChild(this.shadow);				this.previousPageClassList = this.previousPage.classList;				this.activePageClassList = this.activePage.classList;				this.previousPageStyle = this.previousPage.style;				this.activePageStyle = this.activePage.style;				this.previousPageClassList.remove(CLASS_TRANSITIONING);				this.activePageClassList.remove(CLASS_TRANSITIONING);				if (this.navbars) {					this.previousNavbar = this.navbars.querySelector(SELECTOR_NAVBAR_LEFT);					this.activeNavbar = this.navbars.querySelector(SELECTOR_NAVBAR_CENTER);					if (this._isAnimateNavbarIOS() && this.previousNavbar && this.activeNavbar) {						this.previousNavElements = this.previousNavbar.querySelectorAll(SELECTOR_LEFT + ',' + SELECTOR_CENTER + ',' + SELECTOR_RIGHT);						this.activeNavElements = this.activeNavbar.querySelectorAll(SELECTOR_LEFT + ',' + SELECTOR_CENTER + ',' + SELECTOR_RIGHT);						this.previousNavBackIcon = this.previousNavbar.querySelector(SELECTOR_LEFT + SELECTOR_BTN_NAV + ' ' + SELECTOR_ICON);						this.activeNavBackIcon = this.activeNavbar.querySelector(SELECTOR_LEFT + SELECTOR_BTN_NAV + ' ' + SELECTOR_ICON);					}				}				this.x = 0;				this.dragging = true;				return true;			}			return false;		},		_initNavBar: function() {			if (this._isAnimateNavbarIOS() && this.navbars) {				var inners = this.navbars.querySelectorAll(SELECTOR_NAVBAR_INNER);				var inner, left, right, center, leftWidth, rightWidth, centerWidth, noLeft, onRight, currLeft, diff, navbarWidth;				for (var i = 0, len = inners.length; i < len; i++) {					inner = inners[i];					left = inner.querySelector(SELECTOR_LEFT);					right = inner.querySelector(SELECTOR_RIGHT);					center = inner.querySelector(SELECTOR_CENTER);					noLeft = !left;					noRight = !right;					leftWidth = noLeft ? 0 : left.offsetWidth;					rightWidth = noRight ? 0 : right.offsetWidth;					centerWidth = center ? center.offsetWidth : 0;					navbarWidth = this.maxScrollX;					onLeft = inner.classList.contains('navbar-left');					if (noRight) {						currLeft = navbarWidth - centerWidth;					}					if (noLeft) {						currLeft = 0;					}					if (!noLeft && !noRight) {						currLeft = (navbarWidth - rightWidth - centerWidth + leftWidth) / 2;					}					var requiredLeft = (navbarWidth - centerWidth) / 2;					if (navbarWidth - leftWidth - rightWidth > centerWidth) {						if (requiredLeft < leftWidth) {							requiredLeft = leftWidth;						}						if (requiredLeft + centerWidth > navbarWidth - rightWidth) {							requiredLeft = navbarWidth - rightWidth - centerWidth;						}						diff = requiredLeft - currLeft;					} else {						diff = 0;					}					var centerLeft = diff;					if (center) {						center.style.marginLeft = -leftWidth + 'px';						center.mNavbarLeftOffset = -(currLeft + diff) + 30; //这个30是测出来的。后续要实际计算一下						center.mNavbarRightOffset = navbarWidth - currLeft - diff - centerWidth;					}					if (onLeft) center.style.webkitTransform = ('translate3d(' + center.mNavbarLeftOffset + 'px, 0, 0)');					if (!noLeft) {						left.mNavbarLeftOffset = -leftWidth;						left.mNavbarRightOffset = (navbarWidth - leftWidth) / 2;						if (onLeft) left.style.webkitTransform = ('translate3d(' + left[0].mNavbarLeftOffset + 'px, 0, 0)');					}					if (!noRight) {						right.mNavbarLeftOffset = -(navbarWidth - rightWidth) / 2;						right.mNavbarRightOffset = rightWidth;						if (onLeft) right.style.webkitTransform = ('translate3d(' + right[0].mNavbarLeftOffset + 'px, 0, 0)');					}				}			}		},		_drag: function(event) {			if (this.isInTransition) {				return;			}			var detail = event.detail;			if (!this.dragging) {				if (($.gestures.session.firstTouch.center.x - this.view.offsetLeft) < this.options.swipeBackPageActiveArea) {					this.isBack = true;					this._initPageTransform();				}			}			if (this.dragging) {				var deltaX = 0;				if (!this.moved) { //start					deltaX = detail.deltaX;					$.gestures.session.lockDirection = true; //锁定方向					$.gestures.session.startDirection = detail.direction;				} else { //move					deltaX = detail.deltaX - ($.gestures.session.prevTouch && $.gestures.session.prevTouch.deltaX || 0);				}				var newX = this.x + deltaX;				if (newX < 0 || newX > this.maxScrollX) {					newX = newX < 0 ? 0 : this.maxScrollX;				}				event.stopPropagation();				detail.gesture.preventDefault();				if (!this.requestAnimationFrame) {					this._updateTranslate();				}				this.moved = true;				this.x = newX;				this.y = 0;			}		},		_dragend: function(event) {			if (!this.moved) {				return;			}			event.stopPropagation();			var detail = event.detail;			this._clearRequestAnimationFrame();			this._prepareTransition();			this.ratio = this.x / this.maxScrollX;			if (this.ratio === 1 || this.ratio === 0) {				$.trigger(this.activePage, 'webkitTransitionEnd');				return;			}			if (this.ratio > 0.5) {				this.setTranslate(this.maxScrollX, 0);			} else {				this._cleanStyle(this.previousPage);				this._cleanStyle(this.activePage);			}		},		_prepareTransition: function() {			this.isInTransition = true;			this.previousPageClassList.add(CLASS_TRANSITIONING);			this.activePageClassList.add(CLASS_TRANSITIONING);			var self = this;			if (this.previousNavbar && this.activeNavbar) {				this.previousNavbar.classList.add(CLASS_TRANSITIONING);				this.activeNavbar.classList.add(CLASS_TRANSITIONING);				if (this._isAnimateNavbarIOS() && this.previousNavElements && this.activeNavElements) {					$.each(this.previousNavElements, function(i, el) {						el.classList.add(CLASS_TRANSITIONING);						self._cleanStyle(el);					});					$.each(this.activeNavElements, function(i, el) {						el.classList.add(CLASS_TRANSITIONING);						self._cleanStyle(el);					});					if (this.previousNavBackIcon) {						this._cleanStyle(this.previousNavBackIcon);						this.previousNavBackIcon.classList.add(CLASS_TRANSITIONING);					}					if (this.activeNavBackIcon) {						this._cleanStyle(this.activeNavBackIcon);						this.activeNavBackIcon.classList.add(CLASS_TRANSITIONING);					}				}			}		},		_clearRequestAnimationFrame: function() {			if (this.requestAnimationFrame) {				cancelAnimationFrame(this.requestAnimationFrame);				this.requestAnimationFrame = null;			}		},		_getTranslateStr: function(x, y) {			if (this.options.hardwareAccelerated) {				return 'translate3d(' + x + 'px,' + y + 'px,0px) ' + this.translateZ;			}			return 'translate(' + x + 'px,' + y + 'px) ';		},		_updateTranslate: function() {			var self = this;			if (self.x !== self.lastX || self.y !== self.lastY) {				self.setTranslate(self.x, self.y);			}			self.requestAnimationFrame = requestAnimationFrame(function() {				self._updateTranslate();			});		},		_setNavbarTranslate: function(x, y) {			var percentage = x / this.maxScrollX;			//only for ios			if (this._isAnimateNavbarIOS()) {				if (this.previousNavElements && this.activeNavElements) {					this.animateNavbarByIOS(percentage);				}			} else { //pop-in				this.activeNavbar.style.opacity = 1 - percentage * 1.3;				this.previousNavbar.style.opacity = percentage * 1.3 - 0.3;			}		},		animateNavbarByIOS: function(percentage) {			var i, len, style, el;			for (i = 0, len = this.activeNavElements.length; i < len; i++) {				el = this.activeNavElements[i];				style = el.style;				style.opacity = (1 - percentage * (el.classList.contains(CLASS_LEFT) ? 3.5 : 1.3));				if (!el.classList.contains(CLASS_RIGHT)) {					var activeNavTranslate = percentage * el.mNavbarRightOffset;					el.style.webkitTransform = ('translate3d(' + activeNavTranslate + 'px,0,0)');					if (el.classList.contains(CLASS_LEFT) && this.activeNavBackIcon) {						this.activeNavBackIcon.style.webkitTransform = ('translate3d(' + -activeNavTranslate + 'px,0,0)');					}				}			}			for (i = 0, len = this.previousNavElements.length; i < len; i++) {				el = this.previousNavElements[i];				style = el.style;				style.opacity = percentage * 1.3 - 0.3;				if (!el.classList.contains(CLASS_RIGHT)) {					var previousNavTranslate = el.mNavbarLeftOffset * (1 - percentage);					el.style.webkitTransform = ('translate3d(' + previousNavTranslate + 'px,0,0)');					if (el.classList.contains(CLASS_LEFT) && this.previousNavBackIcon) {						this.previousNavBackIcon.style.webkitTransform = ('translate3d(' + -previousNavTranslate + 'px,0,0)');					}				}			}		},		setTranslate: function(x, y) {			this.x = x;			this.y = y;			this.previousPage.style.opacity = 0.9 + 0.1 * x / this.maxScrollX;			this.previousPage.style['webkitTransform'] = this._getTranslateStr((x / 6 - this.maxScrollX / 6), y);			this.activePage.style['webkitTransform'] = this._getTranslateStr(x, y);			this.navbars && this._setNavbarTranslate(x, y);			this.lastX = this.x;			this.lastY = this.y;		},		canBack: function() {			return this.pages.querySelector(SELECTOR_PAGE_LEFT);		},		back: function() {			if (this.isInTransition) {				return;			}			this.isBack = true;			this.ratio = 1;			if (this._initPageTransform()) {				this._trigger('pageBeforeBack', this.activePage);				this._trigger('pageBeforeShow', this.previousPage);				this._prepareTransition();				this.previousPage.offsetHeight;				this.activePage.offsetHeight;				this.setTranslate(this.maxScrollX, 0);			}		},		go: function(pageSelector) {			if (this.isInTransition) {				return;			}			var nextPage = document.querySelector(pageSelector);			if (nextPage) {				var previousPage = this.pages.querySelector(SELECTOR_PAGE_LEFT);				var activePage = this.pages.querySelector(SELECTOR_PAGE_CENTER);				var previousNavbar;				var activeNavbar;				if (this.navbars) {					previousNavbar = this.navbars.querySelector(SELECTOR_NAVBAR_LEFT);					activeNavbar = this.navbars.querySelector(SELECTOR_NAVBAR_CENTER);				}				if (activeNavbar) {					activeNavbar.classList.remove(CLASS_NAVBAR_CENTER);					activeNavbar.classList.add(CLASS_NAVBAR_LEFT);				}				if (previousPage) {					this._removePage(previousPage, previousNavbar);					this.history.push(previousPage); //add to history				}				if (activePage) {					activePage.classList.remove(CLASS_PAGE_CENTER);					activePage.style.webkitTransform = 'translate3d(0,0,0)';					activePage.classList.add(CLASS_PAGE_LEFT);				}				nextPage.style.webkitTransform = 'translate3d(100%,0,0)';				this._appendPage(nextPage);				nextPage.appendChild(this.shadow); //shadow				nextPage.offsetHeight; //force				this.isBack = false;				this.ratio = 1;				this._initPageTransform();				this.navbars && this._initNavBar();				this.navbars && this._setNavbarTranslate(this.maxScrollX, 0);				//force				this.previousPage.offsetHeight;				this.activePage.offsetHeight;				if (this.navbars) {					this.previousNavbar.offsetHeight;					this.activeNavbar.offsetHeight;				}				this._trigger('pageBeforeShow', this.activePage);				this._prepareTransition();				this.setTranslate(0, 0);			}		}	});	$.fn.view = function(options) {		var self = this[0];		var viewApi = null;		var id = self.getAttribute('data-view');		if (!id) {			id = ++$.uuid;			$.data[id] = viewApi = new View(self, options);			self.setAttribute('data-view', id);		} else {			viewApi = $.data[id];		}		return viewApi;	}})(mui, window);
 |