<NME> mobilebone.js
<BEF> /**! 
 * @author @copyright zhangxinxu(.com) 
 * @since 2014-09-26
 * @description mobilebone.js(v2.8.1) - bone of switch for mobile web app - https://github.com/zhangxinxu/mobilebone
 * @license MIT
**/

(function(root, factory) {
	if (document.MBLOADED) {
		root.console && console.warn("Don\'t repeat load Mobilebone!");
		return;
	}
	// Set up Mobilebone appropriately for the environment.
	if (typeof define === "function" && (define.amd || define.cmd)) {
		define("mobilebone", function(exports) {
			return factory(root, exports);
		});
	} else if ( typeof module === "object" && typeof module.exports === "object" ) {
		module.exports = factory(root, {});
	} else {
		// Finally, as a browser global.
		root.Mobilebone = factory(root, {});
	}
})((typeof global !== "undefined") ? global
: ((typeof window !== "undefined") ? window
	: ((typeof self !== "undefined") ? self : this)), function(root, Mobilebone) {
	// Avoid repeated callbacks
	var store = {};

	// Create local references to array methods we'll want to use later.
	var array = [];
	var slice = array.slice;

	// Is it a id selector
	var isSimple = /^#?\w+(?:[\-_]\w+)*$/i;

	// Is it webkit
	var isWebkit = "WebkitAppearance" in document.documentElement.style || typeof document.webkitHidden != "undefined";

	// Is it suppory history API
	var supportHistory = "pushState" in history && "replaceState" in history;

	Mobilebone.support = supportHistory;

	 *
	 * @type string
	**/
	Mobilebone.VERSION = '0.0.0';
	
	/**
	 * Whether bind events when dom ready
	Mobilebone.VERSION = "2.8.1";

	/**
	 * Whether auto init Mobilebone
	 * If this value is false, you shou excute Mobilebone.init() in the right place
	 */
	Mobilebone.autoInit = true;

	/**
	 * Whether catch attribute of href from element with tag 'a'
	 * If the value set to false, jump links in a refresh form(not slide)
	 * In most cases, you do not need to care about this parameter.
	   Except some special pages that should refresh all links, as test/index.html show.
	   However, if your only want several links refesh, you can use data-ajax="false" or data-rel="external"
	 *
	 * @type boolean
	**/
	Mobilebone.captureLink = true;

	/**
	 * Whether catch events of 'submit' from <form> element
	 * If the value set to false, <form> is a normal form except data-ajax="true"
	 * If the value set to true, <form> will submit as a ajax request,
	   and the return value will be used to create a new page and transition into meanwhile.
	   However, if data-ajax="false", <form> won't submit as a ajax.
	 *
	 * @type boolean
	**/
	Mobilebone.captureForm = true;

	/**
	 * The root of transition-callback
	 * Default value is 'root', you can consider as window-object.
	   However, there are may many callbacks, it's impossible that all functions are global function.
	   We may custom a global object to store our callbacks, such as:
	   Callback = {
		 fun1: function() {},
		 fun2: function() {},
		 fun3: function() {},
	   }
	   In this case, the value of 'obilebone.rootTransition' should set Callback;
	 *
	 * @type object
	**/
	Mobilebone.rootTransition = root;

	/**
	 * Whether merge(vs cover) global callback and local callback
	 *
	 * @type boolean
	**/
	Mobilebone.mergeCallback = true;

	/**
	 *  className of animation
	 *
	 * @type string
	**/
	Mobilebone.classAnimation = "slide";
	/**
	 *  for mark page element
	 *
	 * @type string
	**/
	Mobilebone.classPage = "page";
	/**
	 * className for mark mask element
	 *
	 * @type string
	**/
	Mobilebone.classMask = "mask";
	/**
	 * Whether url changes when history changes
	 * If this value is false, the url will be no change.
	 *
	 * @type boolean
	**/
	Mobilebone.pushStateEnabled = true;
	/**
	 * Whether excute JavaScript when ajax HTML loaded
	 * If this value is true, the script will excute.
	 *
	 * @type boolean
	**/
	Mobilebone.evalScript = false;
	
	// When running inside a FF iframe, calling replaceState causes an error. So set 'pushStateEnabled = false'
	if (navigator.userAgent.indexOf( "Firefox" ) >= 0 && window.top !== window) {
		Mobilebone.pushStateEnabled = false;
	}

	/**
	 * if browser do not support history/classList, stop here
	**/
	if (supportHistory == false) {
		return Mobilebone;
	}

	/**
	 * don't excute window.onpopstate when page load
	**/
	history.popstate = false;

	/**
	 * Function for transition
	 * In most cases, you are unnecessary to use this function , unlike Mobilebone.createPage

	 * @params  pageInto: dom-object. Element which will transform into. - Necessary
	            pageOut:  dom-object. Element which will transform out.   - Optional
			    back:     boolean.    Direction of transition.          - Optional
			    options:  object.     Cover or add parameters.           - Optional
	 * @returns undefined
	 * @example Mobilebone.transition(element);
	            Mobilebone.transition(element1, element2);
		        Mobilebone.transition(element1, element2, true);
		        Mobilebone.transition(element1, element2, { id: "only" });
		        Mobilebone.transition(element1, element2, true, { id: "only" });
	**/
	Mobilebone.transition = function(pageInto, pageOut, back, options) {
		if (arguments.length == 0 || pageInto == pageOut) return;
		if (arguments.length == 3 && isNaN(back * 1) == true) {
			options = back;
			back = options.back;
		};

		//if those parameters is missing
		pageOut = pageOut || null, back = back || false, options = options || {};

		// defaults parameters
		var defaults = {
			// the value of callback is a key name, and the host is root here.
			// eg. if the name of animationstart is 'doLoading', so the script will execute 'root.doLoading()'
			// By default, the value of root is 'window'
			root: this.rootTransition,
			// the form of transition, the value (eg. 'slide') will be a className to add or remove.
			// of course, u can set to other valeu, for example, 'fade' or 'flip'. However, u shou add corresponding CSS3 code.
			form: this.form || this.classAnimation,
			// 'animationstart/animationend/...' are callbacks params
			// Note: those all global callbacks!
			onpagefirstinto: this.onpagefirstinto,
			animationstart: this.animationstart,
			animationend: this.animationend,
			preventdefault: this.preventdefault,
			fallback: this.fallback,
			callback: this.callback
		}, params = function(element) {
			if (!element || !element.getAttribute) return {};

			var _params = {}, _dataparams = _queryToObject(element.getAttribute("data-params") || "");

			// rules as follow:
			// data-* > data-params > options > defaults
			["title", "root", "form"].forEach(function(key) {
				_params[key] = element.getAttribute("data-" + key) || _dataparams[key] || options[key] || defaults[key];
			});

			if (typeof _params.root == "string") {
				_params.root = Mobilebone.getFunction(_params.root);
			}

			["onpagefirstinto", "callback", "fallback", "animationstart", "animationend", "preventdefault"].forEach(function(key) {
				if (Mobilebone.mergeCallback == true && typeof defaults[key] == "function") {
					// merge global callback
					var local_function_key = element.getAttribute("data-" + key) || _dataparams[key];
					if (typeof _params.root[local_function_key] == "function") {
						_params[key] = function() {
							defaults[key].apply(this, arguments);
							_params.root[local_function_key].apply(this, arguments);
						}
					} else if (typeof options[key] == "function") {
						_params[key] = function() {
							defaults[key].apply(this, arguments);
							options[key].apply(this, arguments);
						}
					} else {
						_params[key] = defaults[key];
					}
				} else {
					// replace global callback
					_params[key] = element.getAttribute("data-" + key) || _dataparams[key] || options[key] || defaults[key];
				}
			});

			return _params;
		};

		// get params of each
		var paramsOut = params(pageOut), paramsIn = params(pageInto);

		if (pageOut != null && pageOut.classList) {
			// weather prevent transition
			var preventOut = paramsOut.preventdefault, isPreventOut = false;
			if (typeof preventOut == "string") {
				preventOut = paramsOut.root[preventOut];
			}
		}
		if (pageInto != null && pageInto.classList) {
			// weather prevent transition
			var preventInto = paramsIn.preventdefault, isPreventInto = false;
			if (typeof preventInto == "string") {
				preventInto = paramsIn.root[preventInto];
			}
		}
		if (typeof preventOut == "function") {
			isPreventOut = preventOut.call(paramsOut.root, pageInto, pageOut, options);
		}

		// if functions of 'preventdefault' are same for pageIn and pageout, just excute once.
		if (isPreventOut == true && preventOut === preventInto) {
			return false;
		}

		if (typeof preventInto == "function") {
			isPreventInto = preventInto.call(paramsIn.root, pageInto, pageOut, options);
		}
		// if pageinto stopped, stop all
		if (isPreventInto == true) {
			// only run here and nothing more
			return false;
		}

		// set animation callback as a method
		var funAnimationCall = function(page, data) {
			if (page.flagAniBind == true) return;
			// do callback when animation start/end
			["animationstart", "animationend"].forEach(function(animationkey, index) {
				var animition = paramsIn[animationkey], webkitkey = "webkit" + animationkey.replace(/^a|s|e/g, function(matchs) {
					return matchs.toUpperCase();
				});
				var animateEventName = isWebkit? webkitkey: animationkey;
				// if it's the out element, hide it when 'animationend'
				if (index) {
					page.addEventListener(animateEventName, function() {
						if (this.classList.contains("in") == false) {
							this.style.display = "none";
							// add on v2.5.5
							// move here on v2.5.8
							// main for remove page is just current page
							// remove on v2.7.0
							// if (this.removeSelf == true) {
							// 	this.parentElement.removeChild(this);
							// 	this.removeSelf = null;
							// }
						}
						this.classList.remove(params(this).form);
					});
				}
				// bind animation events
				if (typeof animition == "string" && paramsIn.root[animition]) {
					page.addEventListener(animateEventName, function() {
						data.root[animition].call(data.root, this, this.classList.contains("in")? "into": "out", options);
					});
				} else if (typeof animition == "function") {
					page.addEventListener(animateEventName, function() {
						animition.call(data.root, this, this.classList.contains("in")? "into": "out", options);
					});
				}
				// set a flag
				page.flagAniBind = true;
			});
		};

		if (pageOut != null && pageOut.classList) {
			// do transition if there are no 'prevent'
			if (isPreventOut != true) {
				pageOut.classList.add(paramsOut.form);
				// reflow
				pageOut.offsetWidth;
				// go, go, go
				pageOut.style.display = "block";
				pageOut.classList.add("out");
				pageOut.classList.remove("in");
				// if reverse direction
				pageOut.classList[back? "add": "remove"]("reverse");

				// add on v2.5.5
				pageOut.removeSelf = pageOut.removeSelf || null;

				// set animation callback for 'pageInto'
				// for issues #153
				funAnimationCall(pageOut, paramsOut);

				// do fallback every time
				var fallback = paramsOut.fallback;
				if (typeof fallback == "string") {
					fallback = paramsOut.root[fallback];
				}
				if (typeof fallback == "function") {
					fallback.call(paramsOut.root, pageInto, pageOut, options);
				}
			}
		}

		if (pageInto != null && pageInto.classList) {
			// for title change
			var title = paramsIn.title,
			    header = document.querySelector("h1"),
			    firstPage = document.querySelector("." + this.classPage);

			// do title change
			if (title && options.title !== false) {
				document.title = title;
				if (header) {
					header.innerHTML = title;
					header.title = title;
				}
			} else if (firstPage == pageInto && !pageOut && document.title) {
				// set data-title for first visibie page
				pageInto.setAttribute("data-title", document.title);
			}

			// delete page with same id when options.remove !== false
			var pageid = options.id || pageInto.id;
			var hashid = options.id || pageInto.id;

			// v2.7.0 change rule -> don't auto delete pages
			//                    -> delete by using Mobilebone.remove();
			// v2.7.2 add using data-reload="xxxxId" to auto remove pages
			// Reason Two:
			// 1. Most websites are so simple that it's not enough to talk about performance;
			// 2. We can't judge relation by two page's url
				// eg, below 'if' sentence only cover sence 0:
				/*
				if (options.id) {
					// eg. get 'ajax.php' from 'ajax.php?xx=1'
				 	pageid = pageid.split("?")[0];
				}
				*/

				// 0. url format: //xxx.com/list.php
				                  //xxx.com/detail.php?id=1
				                  //xxx.com/detail.php?id=2

				// but not sence 1 and 2:

				// 1. url format: //xxx.com/list/111/
				                  //xxx.com/list/222/
				                  //xxx.com/list/333/
				                  // 明明需要删除的，没删除

	            // 2. url format: //xxx.com/index.php   列表页
	                              //xxx.com/index.php?list=1   列表详情页1
	                              //xxx.com/index.php?list=2   列表详情页2
	                              // 以上列表页就嗝屁了

			// var relid = store["_" + pageid];
			// // only delete page auto when data-reload is not 'false' or null
			// // hashid may store the same page, we should delete also
			// // when data-reload not 'false' or null
			// // v2.4.4+
			// if (relid && store[relid] && options.reload == true) {
			// 	delete store[relid];
			// 	delete store["_" + pageid];
			// }

			// // below commented on v2.6.2
			// if (options.reload == true) {
			// 	// v2.5.8 for issues #147
			// 	pageInto.removeSelf = true;
			// }

			// if (store[pageid] && store[pageid] != pageInto) {


			// 	if (store[pageid] != pageOut) {
			// 		store[pageid].parentElement && store[pageid].parentElement.removeChild(store[pageid]);
			// 	} else {
			// 		// if the page element same as store
			// 		// remove when animationend
			// 		pageOut.removeSelf = true;
			// 	}
			// 	delete store[pageid];
			// }

			// do transition
			if (pageOut) {
				pageInto.classList.add(paramsIn.form);
			}
			// iOS bug
			// reflow for fixing issues #80, #86
			pageInto.offsetWidth;
			// go~ as normal
			pageInto.style.display = "block";
			pageInto.classList.remove("out");
			pageInto.classList.add("in");
			// if reverse direction
			pageInto.classList[back? "add": "remove"]("reverse");

			// do callback when come in first time
			var onpagefirstinto = paramsIn.onpagefirstinto;
			// first judge change to pageInto store
			// v2.5.5 add for fix issues #138
			if (!pageInto.firstintoBind) {
				if (typeof onpagefirstinto == "string" && paramsIn.root[onpagefirstinto]) {
					paramsIn.root[onpagefirstinto].call(paramsIn.root, pageInto, pageOut, options);
				} else if (typeof onpagefirstinto == "function") {
					onpagefirstinto.call(paramsIn.root, pageInto, pageOut, options);
				}
				// capture form submit
				slice.call(pageInto.querySelectorAll("form")).forEach(function(form) {
					Mobilebone.submit(form);
				});

				pageInto.firstintoBind = true;
			}

			// set animation callback for 'pageInto'
			funAnimationCall(pageInto, paramsIn);

			// history
			// hashid should a full url address
			// different with pageid
			// add on 2.4.2
			var urlPush = hashid, urlPushReplace = "";

			if (urlPush && /#/.test(urlPush) == false) {
				urlPush = "#" + urlPush;
			}
			urlPushReplace = urlPush.replace(/#/, "#&");

			if (supportHistory && this.pushStateEnabled && options.history !== false && urlPush
				// hash should be different
				// can fix issues #79, #87 maybe
				&& urlPushReplace != location.hash
			) {
				// don't trigger 'popstate' events
				history.popstate = false;
				// if only pageIn, use 'replaceState'
				history[pageOut ? "pushState" : "replaceState"](null, document.title, urlPush.replace(/#/, "#&"));
			}

			// store page-id, just once
			if (!store[pageid]) {
				store[pageid] = pageInto;
				// when we back/prev, we need to get true
				// comment on v2.7.0
				// if (hashid !== pageid) {
				// 	store[hashid] = pageInto;
				// 	store["_" + pageid] = hashid;
				// }
			}

			// do callback every time
			var callback = paramsIn.callback;

			if (typeof callback == "string") {
				callback = paramsIn.root[callback];
			}
			if (typeof callback == "function") {
				callback.call(paramsIn.root, pageInto, pageOut, options);
			}

			// Safari do 'popstate' after 'pushState/replaceState'
			// So, we neet setTimeout to avoid excuting 'Mobilebone.transition()' twice
			setTimeout(function() {
				// reset to popable state
				history.popstate = true;
			}, 17);

			// add on v2.7.5 improve back user experence
			// add on v2.7.6
			// only not back transiton store last ship
			if (back == false && history.popstate == false) {
				store.lastShip = [pageInto, pageOut];
			} else {
				store.lastShip = null;
			}
		}
	};

	/**
	 * Remove page DOM add on v2.7.0
	 * @param  domOrId: dom-object|string
	 *          page DOM or <a> DOM
	 *          page id or <a> href value
	 * @return {[type]}         [description]
	 */
	Mobilebone.remove = function (domOrId) {
		if (!domOrId) {
			return this;
		}

		var elePage = domOrId;
		var pageid = domOrId;
		if (typeof pageid == "string") {
			elePage = store[pageid];			
		} else if (elePage.tagName && elePage.tagName.toLowerCase() == "a") {
			pageid = this.getCleanUrl(elePage);
			elePage = store[pageid];
		}

		if (elePage && elePage.parentElement) {
			// remove store
			for (var key in store) {
				if (store[key] == elePage) {
					delete store[key];
				}
			}
			elePage.parentElement.removeChild(elePage);
		}
	};


	/**
	 * For getting whole ajax url
	 * In most cases, you are unnecessary to use this function

	 * @params  trigger: dom-object. element with tag-"a".  - Optional(at least one)
	            url:     string. ajax url.                  - Optional(at least one)
			    params:  string|object. ajax params.        - Optional
	 * @returns string
	 * @example Mobilebone.getCleanUrl(elementOfA);
	            Mobilebone.getCleanUrl(elementOfForm);
	            Mobilebone.getCleanUrl(elementOfA, '', "a=1&b=2");
		        Mobilebone.getCleanUrl(null, "xxx.html");
		        Mobilebone.getCleanUrl(null, "xxx.html?a=1&b=2");
		        Mobilebone.getCleanUrl(null, "xxx.html", "a=1&b=2");
	**/
	Mobilebone.getCleanUrl = function(trigger, url, params) {
		var href = "", formdata = "", cleanUrl = "";
		if (trigger) {
			if (trigger.nodeType == 1) {
				// form element
				if (trigger.action) {
					href = trigger.getAttribute("action");
					// add on v2.4.1
					if (trigger.method && trigger.method.toUpperCase() == "POST") {
						return href;
					} else if (window.$ && $.fn && $.fn.serialize) {
						// use jquery serialize()
						formdata = $(trigger).serialize();
					} else {
						formdata = {};
						// simple serialize from Mobilebone
						slice.call(trigger.querySelectorAll("input,select,textarea")).forEach(function(control) {
							if (control.name && !control.disabled) {
								var val = control.value.trim(), name = control.name;
								if (/^radio|checkbox/i.test(control.type)) {
									if (control.checked) {
										if (formdata[name]) {
											formdata[name].push(val);
										} else {
											formdata[name] = [val];
										}
									}
								} else {
									formdata[name] = [val];
								}
							}
						});
					}
				} else {
					// a element
					href = trigger.getAttribute("href");
					if (/^javascript/.test(href)) {
						href = '';
					}
					formdata = trigger.getAttribute("data-formdata") || trigger.getAttribute("data-params") || "";
					// v2.6.1 for #107
					// remember container when refresh
					var strContainer = "container", attrContainer = trigger.getAttribute("data-" + strContainer);
					if (formdata.indexOf(strContainer) == -1 && attrContainer) {
						var queryContainer = strContainer + "=" + attrContainer;
						formdata = formdata ? formdata + "&" + queryContainer : queryContainer;
					}
				}
			} else if (trigger.url) {
				href = trigger.url;
				formdata = trigger.data;
			}
		}

		if (!(href = href || url)) {
			return "";
		}

		// get formdata
		formdata = formdata || params || "";

		if (typeof formdata == "object") {
			var arrData = [];
			for (key in formdata) {
				if (!formdata[key].forEach) {
					formdata[key] = [formdata[key]];
				}
				formdata[key].forEach(function(keyValue) {
					arrData.push(key + "=" + encodeURIComponent(keyValue));
				});

			}
			if (arrData.length > 0) {
				formdata = arrData.join("&");
			} else {
				formdata = "";
			}
		}

		// get url of root
		cleanUrl = href.split("#")[0].replace(/&+$/, "").replace(/^\.\/+/, "");

		if (cleanUrl.slice(-1) == "?") {
			cleanUrl = cleanUrl.split("?")[0];
		}
		// url = root_url + joiner + formdata
		if (formdata != "") {
			if (/\?/.test(cleanUrl)) {
				formdata = formdata.replace(/^&|\?/, "");
				cleanUrl = cleanUrl + "&" + formdata;
			} else if (formdata != "") {
				formdata = formdata.replace("?", "");
				cleanUrl = cleanUrl + "?" + formdata;
			}
		}
		return cleanUrl;
	};

	/**
	 * Create page according to given Dom-element or HTML string. And, notice!!!!! will do transition auto.

	 * @params  domHtml:        dom-object|string. Create this to dom element as a role of into-page.               - Necessary
	            eleOrObj: dom-object|object. '.page element', or 'a element', or 'options' for get out-page   - Optional
				options:            object.            basically, options = ajax options, of course, u can custom it!   - Optional
	 * @returns undefined
	 * @example Mobilebone.createPage(pageDom);
	            Mobilebone.createPage(generalDom);
		        Mobilebone.createPage('<div class="page out">xxx</div>');
		        Mobilebone.createPage('<p>xxx</p>');
		        Mobilebone.createPage(pageDom, triggerLink);
		        Mobilebone.createPage(pageDom, { response: '<div...>' });
		        Mobilebone.createPage(pageDom, triggerLink, { response: '<div...>' });
	 *
	**/
	Mobilebone.createPage = function(domHtml, eleOrObj, options) {
		var response = null, container = null, classPage = this.classPage, isreload = null;
		// 'eleOrObj' can '.page element', or 'a element', or 'options'
		// basically, options = ajax options, of course, u can custom it!
		if (!domHtml) return;
		if (typeof options == "undefined" && typeof eleOrObj == "object") {
			options = eleOrObj;
		}
		options = options || {};

		// 'options' that 'Mobilebone.transition()' needs
		var optionsTransition = {};

		// get page-title from eleOrObj or options
		var titlePage, idContainer, classPageInside;

		if (eleOrObj) {
			if (eleOrObj.nodeType == 1) {
				// legal elements
				if (eleOrObj.href || eleOrObj.action) {
					titlePage = eleOrObj.getAttribute("data-title") || options.title;
				}
				response = options.response;
				idContainer = eleOrObj.getAttribute("data-container");
				container = document.getElementById(idContainer);
				classPageInside = eleOrObj.getAttribute("data-classpage");
				// pass element as target params, add on v2.3.0
				optionsTransition.target = eleOrObj;
				// v2.4.4 is_root → isreload
				isreload = eleOrObj.getAttribute("data-reload");
				if (eleOrObj.tagName.toLowerCase() == "form" || (isreload !== null && isreload != "false")) {
					optionsTransition.reload = true;
				}
				// v2.5.2
				// is back? for issues #128
				optionsTransition.back = eleOrObj.getAttribute("data-rel") == "back";

				// v2.6.0 history
				if (eleOrObj.getAttribute("data-history") == "false") {
					optionsTransition.history = false;
				}
			} else {
				response = eleOrObj.response || options.response;
				titlePage = eleOrObj.title || options.title;
				container = eleOrObj.container || options.container;
				classPageInside = eleOrObj.classPage || options.classPage;
				optionsTransition.target = eleOrObj.target;
				// v2.5.2
				// is back? for issues #128
				optionsTransition.back = eleOrObj.back || options.back;
			}
			if (container && classPageInside) {
				classPage = classPageInside;
			}
		}

		// get current page(will be out) according to 'page_or_child'
		var eleCurrentPage = (classPage == classPageInside? container : document).querySelector(".in." + classPage);

		// get create page (will be into) according to 'domHtml'
		var eleCreatePage = null;

		var create = document.createElement("div");
		if (typeof domHtml == "string") {
			create.innerHTML = domHtml;
		} else {
			create.appendChild(domHtml);
		}

		// excute inline JavaScript
		if (Mobilebone.evalScript == true && domHtml.firstintoBind != true) {
			slice.call(create.getElementsByTagName("script")).forEach(function(originScript) {
				var scriptContent = originScript.innerHTML.trim();
				var type = originScript.type || originScript.getAttribute("type");

				var head = document.getElementsByTagName("head")[0] || document.documentElement;
				var script = document.createElement("script");
				if (type) {
					script.type = type;
				}

				if (scriptContent) {
					script.appendChild(document.createTextNode(scriptContent));
				} else if (originScript.src && !document.querySelector('script[src="'+ originScript.src +'"]')) {
					// issues #199
                	script.src = originScript.src;
            	}

				setTimeout(function() {
					head.insertBefore(script, head.firstChild);
					head.removeChild(script);
					script = null;
				}, 17);
				originScript = null;
			});
		}

		var eleCreateTitle = create.getElementsByTagName("title")[0];

		// get the page element
		if (!(eleCreatePage = create.querySelector("." + classPage))) {
			// if there no .page, create as eleCreatePage
			create.className = classPage + " out";
			eleCreatePage = create;
		}
		// set and store title
		if (typeof titlePage == "string") {
			eleCreatePage.setAttribute("data-title", titlePage);
		} else if (eleCreateTitle && eleCreateTitle.innerText) { // the judge behind '&&' for issues #144
			eleCreatePage.setAttribute("data-title", eleCreateTitle.innerText);
		}

		// do transition
		optionsTransition.response = response || domHtml;
		optionsTransition.id = options.id || this.getCleanUrl(eleOrObj) || eleCreatePage.id || options.url || ("unique" + Date.now());

		// 'if' statement below added on v2.0.0
		if (typeof options == "object") {
			if (typeof options.history != "undefined") {
				optionsTransition.history = options.history;
			}
			if (typeof options.remove != "undefined") {
				optionsTransition.remove = options.remove;
			}
			if (typeof options.target != "undefined") {
				optionsTransition.target = options.target;
			}
			if (typeof options.title != "undefined") {
				optionsTransition.title = options.title;
			}
			// add on v2.7.4
			if (typeof options.query != "undefined") {
				optionsTransition.query = options.query;
			}
		}
		if (classPage == classPageInside) {
			optionsTransition.history = false;
			optionsTransition.classPage = classPage;
		}

		// append to a accurate position
		container = container || document.body;

		// remove page if have same id when optionsTransition.remove == true
		if (optionsTransition.remove == true && optionsTransition.id) {
			this.remove(optionsTransition.id);
			// v2.8.0 dom remove and ajax remove share same id
			if (options.id) {
				slice.call(document.querySelectorAll('a[data-reload="'+ options.id +'"]')).forEach(function (ele) {
					Mobilebone.remove(ele);
				});
			}
		}
		// 1. if new page, that insert create page as a last-child
		// 2. if replace a page, that insert before replaced page
		var pageid = optionsTransition.id.split("?")[0];
		if (pageid && store[pageid] && container.contains(store[pageid])) {
			container.insertBefore(eleCreatePage, store[pageid]);
		} else {
			container.appendChild(eleCreatePage);
		}

		// release memory
		create = null;

		// do transition
		this.transition(eleCreatePage, eleCurrentPage, optionsTransition);

		var objRelationShip = store.backShip || [];
		objRelationShip.push({
			pageIn: eleCreatePage,
			pageOut: eleCurrentPage,
			isBack: !!optionsTransition.back
		});

		store.backShip = objRelationShip;
	};

	/**
	 * For ajax callback.
	 * For example, data-success="a.b.c". We can't use 'a.b.c' as a function, because it's a string. We should do some work to get it!

	 * @params  keys:        string. - Necessary
	 * @returns function
	            undefined keys is not string
				window    keys undefined
	 * @example Mobilebone.getFunction("a.b.c");
	 *
	**/
	Mobilebone.getFunction = function(keys) {
		if (typeof keys != "string") return;
		// eg. 'globalObject.functionName'
		var fun = Mobilebone.rootTransition, arrKey = keys.split(".");
		for (var index=0; index<arrKey.length; index+=1) {
			if (!(fun = fun[arrKey[index]])) {
				break;
			}
		}
		return fun;
	};

	/**
	 * For ajax request to get HTML or JSON.

	 * @params  eleOrObj        - Necessary
	            1. dom-object:<a>|<form>.
				2. object.
	 * @returns undefined
	 * @example Mobilebone.ajax(document.querySelector("a"));
	            Mobilebone.ajax({
				  url: 'xxx.html',
				  success: function() {}
		    	});
	 *
	**/
	Mobilebone.ajax = function(eleOrObj) {
		if (!eleOrObj) {
			return;
		}

		var trigger = eleOrObj;

		// default params
		var defaults = {
			url: "",
			type: "",
			dataType: "",
			data: {},
			timeout: 10000,
			success: function() {},
			error: function() {},
			complete: function() {}
		};

		var params = {}, eleMask = null, formData = null;

		// classname of mask
		var classMask = this.classMask;

		// if 'eleOrObj' is a element, we should turn it to options-object
		var paramsFromTrigger = {}, attrMask;
		if (eleOrObj.nodeType == 1) {
			paramsFromTrigger = _queryToObject(trigger.getAttribute("data-params") || "");
			// get params
			for (key in defaults) {
				// data-* > data-params > defaults
				params[key] = trigger.getAttribute("data-" + key) || paramsFromTrigger[key] || defaults[key];
				if (typeof defaults[key] == "function" && typeof params[key] == "string") {
					// eg. globalObject.functionName
					params[key] = this.getFunction(params[key]);
					if (typeof params[key] != "function") {
						params[key] = defaults[key];
					}
				}
			}

			// address of ajax url
			var cleanUrl = this.getCleanUrl(trigger, params.url);
			params.url = cleanUrl;

			var queryFromUrl = _queryToObject(cleanUrl.split("?")[1]);

			// v2.7.4 fix params may ingore problem
			for (var key in queryFromUrl) {
				if (typeof paramsFromTrigger[key] == "undefined") {
					paramsFromTrigger[key] = queryFromUrl[key];
				}
			}
			// v2.7.4
			params.query = paramsFromTrigger;
			// store target
			params.target = trigger;
			// v2.5.2
			// is back? for issues #128
			params.back = trigger.getAttribute("data-rel") == "back";

			var tagName = trigger.tagName.toLowerCase();
			if (tagName == "form") {
				params.type = trigger.method;

				formData = new FormData(trigger);
			} else if (tagName == "a") {
				// v2.5.8 for issues #157
				var idContainer = trigger.getAttribute("data-container"),
					classPageInside = trigger.getAttribute("data-classpage"),
					container = idContainer && document.getElementById(idContainer);
				if (container && classPageInside && classPageInside != Mobilebone.classPage) {
					// inner ajax no history change
					params.history = false;
					// title do not change
					params.title = false;
				}

				// v2.8.0 move to here
				var attrReload = trigger.getAttribute("data-reload");
				if (typeof attrReload == "string" && attrReload != "false") {
					if (attrReload != "" && attrReload != "true") {
						// v2.7.2 a new method to remove pafe
						// think 'attrReload' as special ID
						// remove all page using this ID
						slice.call(document.querySelectorAll('a[data-reload="'+ attrReload +'"]')).forEach(function (ele) {
							Mobilebone.remove(ele);
						});
						Mobilebone.remove(store[attrReload]);
					} else {
						// remove page
						Mobilebone.remove(store[cleanUrl]);
					}
				}
			}

			// get mask element
			attrMask = eleOrObj.getAttribute("data-mask");
			if (attrMask == "true" || attrMask == "") {
				eleMask = eleOrObj.querySelector("." + classMask);
			}
		}
		// if 'eleOrObj' is a object
		else if (eleOrObj.url) {
			params = eleOrObj;
			// get params
			for (key2 in defaults) {
				params[key2] = eleOrObj[key2] || defaults[key2];
			}
			// get url
			params.url = this.getCleanUrl(null, params.url, params.data);
			// here params.title will become page title;
			params.title = eleOrObj.title;
			// v2.5.2
			// is back? for issues #128
			// when history.back()
			params.back = eleOrObj.back;
			// v2.6.1
			params.container = eleOrObj.container;
			// v2.7.4
			params.query = _queryToObject(eleOrObj.url.split('?')[1]);
		} else {
			return;
		}

		// do ajax
		// get mask and loading element
		var body = container || document.body;
		if (typeof attrMask != "string") {
			eleMask = [].slice.call(body.children).filter(function (element) {
				return element.classList.contains(classMask);
			})[0];
		}

		if (eleMask == null) {
			eleMask = document.createElement("div");
			eleMask.className = classMask;
			eleMask.innerHTML = '<s class="loading"></s>';
			if (typeof attrMask == "string") {
				eleOrObj.appendChild(eleMask);
			} else {
				body.appendChild(eleMask);
			}
		}
		// show loading
		eleMask.style.display = "inline";
		if (this.showLoading) {
			this.showLoading();
		}

		// ajax request
		var xhr = new XMLHttpRequest();
		xhr.open(params.type || "GET", params.url + (/\?/.test(params.url)? "&" : "?") + "r=" + Date.now());
		xhr.timeout = params.timeout;

		xhr.onload = function() {
			// so far, many browser hasn't supported responseType = 'json', so, use JSON.parse instead
			var response = null;

			if (params.dataType == "json" || params.dataType == "JSON") {
				try {
					response = JSON.parse(xhr.response);
					params.response = response;
					Mobilebone.createPage(Mobilebone.jsonHandle(response, params), eleOrObj, params);
				} catch (e) {
					params.message = "JSON parse error：" + e.message;
					params.error.call(params, xhr, xhr.status);
				}
			} else if (params.dataType == "unknown") {
				// ajax send by url
				// no history hush
				params.history = false;
				// I don't remember why add 'params.remove = false' here,
				// but it seems that this will cause issues #147
				// no element remove
				// del → v2.5.8 // params.remove = false;
				try {
					// as json
					response = JSON.parse(xhr.response);
					params.response = response;
					Mobilebone.createPage(Mobilebone.jsonHandle(response, params), eleOrObj, params);
				} catch (e) {
					// as html
					response = xhr.response;
					Mobilebone.createPage(response, eleOrObj, params);
				}
			} else {
				response = xhr.response;
				// 'response' is string
				Mobilebone.createPage(response, eleOrObj, params);
			}
			params.success.call(params, response, xhr.status);
		};

		xhr.onerror = function(e) {
			params.message = "Illegal request address or an unexpected network error!";
			params.error.call(params, xhr, xhr.status);
		};

		xhr.onloadend = function () {
			// hide loading
			eleMask.style.display = "none";
			if (this.hideLoading) {
				this.hideLoading();
			}

			params.complete.call(params, xhr, xhr.status);
		};

		xhr.ontimeout = function() {
			params.message = "The request timeout!";
			params.error.call(params, xhr, xhr.status);
			// hide loading
			eleMask.style.display = "none";
			if (this.hideLoading) {
				this.hideLoading();
			}
		};

		// set request header for server
		xhr.setRequestHeader("Type", "ajax");
		xhr.setRequestHeader("From", "mobilebone");

		xhr.send(formData);
	};

	/**
	 * capture form submit events to a ajax request.

	 * @params  form:        formElement. - Necessary
	 * @example Mobilebone.form(document.querySelector("form"));
	 *
	**/
	Mobilebone.submit = function(form) {
		if (!form || typeof form.action != "string") {
			return;
		}
		var ajax = form.getAttribute("data-ajax");
		if (ajax == "false" || (Mobilebone.captureForm == false && ajax != "true")) return;

		form.addEventListener("submit", function(event) {
			// prevent detect
			var attrPrevent = this.getAttribute("data-preventdefault");
			// get 'preventDefault' function
			var funPrevent = Mobilebone.getFunction(attrPrevent);
			if (typeof funPrevent == "function" && funPrevent(this) == true) {
				// if the return value of prevent function is true, prevent everything~
				event.preventDefault();
				return false;
			}

			Mobilebone.ajax(this);
			event.preventDefault();
		});
	};


	/**
	 * Sometime we don't know direction of transition. Such as browser history change, or data-rel="auto".
	   In this case, we ensure the direction(back or prev) by the sorts of two pages(into or out)

	 * @params  pageIn  dom-object      - Necessary
	            pageOut  dom-object      - Optional

	 * @returns boolean
	 *
	**/
	Mobilebone.isBack = function(pageIn, pageOut) {
		// back or forwards, according to the order of two pages
		if (history.tempBack == true) {
			// backwords
			history.tempBack = null;
			return true;
		}

		if (history.tempGo == true) {
			// forwards
			history.tempGo = null;
			return false;
		}
		// 2.7.5 return true -> false
		if (typeof pageIn == "undefined") {
			return false;
		}
		if (!pageOut) {
			return false;
		}

		// 2.7.5 store pageIn pageOut backward or forward
		var objRelationShip = store.backShip || [];
		var isBack = null;
		objRelationShip.forEach(function (ship) {
			if (ship.pageIn == pageIn && ship.pageOut == pageOut) {
				isBack = ship.isBack;
			} else if (ship.pageIn == pageOut && ship.pageOut == pageIn) {
				isBack = !ship.isBack;
			}
		});

		if (isBack === null) {
			isBack = !!(pageIn.compareDocumentPosition(pageOut) & Node.DOCUMENT_POSITION_FOLLOWING);
		}

		return isBack;
	};

	/**
	 * If dataType of ajax is 'json', we can't convert json-data to page-element.
	   So, we export a function names 'jsonHandle' to handle json-data.
	 * Attention, it's a global interface. If your project has many json call, you should use JSON itself to make a distinction.
	   For example, every JSON include the only json-id:
	   {
		  "id": "homePage" ,
		  "data": []
	   }
	   different with
	   {
		  "id": "listPage" ,
		  "data": []
	   }
	 *
	 * @params  json    - Necessary
	 * @returns dom-object|string
	 *
	**/
	Mobilebone.jsonHandle = function(json, params) {
		return '<p style="text-align:center;">Dear master, if you see me, show that JSON parsing function is undefined!</p>';
	},

	/**
	 * Initialization. Load page according to location.hash. And bind link-catch events.
	**/
	Mobilebone.init = function() {
		if (hasInited == true) return 'Don\'t repeat initialization!';

		var hash = location.hash.replace("#&", "#"), eleIn = null, container = null;
		// 查询和根处理
		var key = hash.split("?")[0];
		var query = hash.split("?")[1];

		var options = {
	    	query: {}
	    };
	    if (query) {
	    	options.query = _queryToObject(query);
	    }

		if (key == "" || key == "#") {
			this.transition(document.querySelector("." + this.classPage));
		} else if (isSimple.test(key) == true && (eleIn = document.querySelector(key)) && eleIn.classList.contains(this.classPage)) {
		    // add on v2.7.4 pure inner hash also support query params
		    // eg. #somePageId?id=1&type=2
		    options.id = hash.replace(/^#/, "");

			this.transition(eleIn, null, options);
		} else {
			// add on v2.6.1
			if (hash.split("container=").length == 2) {
				container = document.getElementById(hash.split("container=")[1].split("&")[0]);
			}
			// as a ajax
			this.ajax({
				url: hash.replace("#", ""),
				dataType: "unknown",
				container: container,
				error: function() {
					// add on v2.7.4
					options.id = hash.replace(/^#/, "");

					eleIn = document.querySelector("." + Mobilebone.classPage);
					Mobilebone.transition(eleIn, null, options);
				}
			});
		}

		// Initialization link-catch events.
		document.addEventListener("click", this.handleTapEvent);

		// Important:
		// In ios7+, swipe the edge of page will navigate Safari
		// that will trigger 'popstate' events and the page will transition twice
		var isSafari7 = !!navigator.userAgent.match(/safari/i) && !navigator.userAgent.match(/chrome/i) && typeof document.hidden !== "undefined" && !window.chrome;
		if ('ontouchstart' in window == true && isSafari7) {
			document.addEventListener("touchmove", function() {
				history.popstateswipe = true;
			});
			document.addEventListener("touchend", function() {
				history.popstateswipe = false;
			});
		}

		// change flag-var for avoiding repeat init
		hasInited = true;
	};

	/**
	 * If 'a' element has href, slide auto when clicked
	**/
	Mobilebone.handleTapEvent = function(event) {
		var target = null;
		// you can pass target as params directly
		if (event && event.nodeType == 1) {
			target = event;
			target.preventDefault = function() {};
		}
		// get target and href
		target = (target || event.target) && (target || event.target).closest('a');

		if (!target) {
			return;
		}

		// current href
		var href = target.href;

		// the page that current actived
		var selfPage = document.querySelector(".in." + Mobilebone.classPage);

		if (selfPage == null) {
			return;
		}

		// optional params for Mobilebone.transition
		var options = {
			target: target
		};

		// prevent detect
		var attrPrevent = target.getAttribute("data-preventdefault") || _queryToObject(target.getAttribute("data-params") || "").preventdefault;
		// get 'preventDefault' function
		var funPrevent = Mobilebone.getFunction(attrPrevent);
		if (typeof funPrevent == "function" && funPrevent(target) == true) {
			// if the return value of prevent function is true, prevent everything~
			event.preventDefault();
			return false;
		}

		// if mask element exist and displaying, prevent double trigger
		var eleMask = target.getElementsByClassName(Mobilebone.classMask)[0];
		if (eleMask && eleMask.style.display != "none") {
			event.preventDefault();
			return false;
		}

		var idContainer = target.getAttribute("data-container");
		var classPageInside = target.getAttribute("data-classpage");

		var container = idContainer && document.getElementById(idContainer);
		if (container && classPageInside && classPageInside != Mobilebone.classPage) {
			selfPage = container.querySelector(".in." + classPageInside) || container.querySelector(classPageInside);
			// if (selfPage == null) return false;
			options.history = false;
			options.title = false;
			options.classPage = classPageInside;
		}

		// history
		if (target.getAttribute("data-history") == "false") {
			options.history = false;
		}

		// if captureLink
		var capture = (Mobilebone.captureLink == true);
		// get rel
		var rel = target.getAttribute("data-rel");
		if (!rel) {
			rel = 'auto';
		}
		// if back
		var back = false;
		if (rel == "back") {
			back = true;
		}
		// if go
		var go;
		if (rel == "go") {
			go = true;
		}

		// if external link
		var external = (rel == "external");

		// if the 'href' is not legal, return
		// include:
		// 1. undefined
		// 2. javascript: (except data-rel="back")
		// 3. cros, or not capture (except data-ajax="true")
		if (!href) {
			return;
		}

		// 直接获取属性设置的值
		var attrHref = target.getAttribute("href");

		href = href.replace("#&", "#");

		if (attrHref.replace(/#/g, "") === "") {
			event.preventDefault();
			return;
		}
		if (/^javascript/.test(href)) {
			if (back == false && !go) {
				return;
			}
		} else {
			external = external || (href.replace("://", "").split("/")[0] !== location.href.replace("://", "").split("/")[0]);
			if ((external == true || capture == false) && target.getAttribute("data-ajax") != "true") {
				return;
			}
		}

		// judge that if it's a ajax request
		if (/^#/.test(attrHref) == true) {
			event.preventDefault();
			// hash slide
			var hashTargetPage = href.split("#")[1];
			var idTargetPage = hashTargetPage.split("?")[0];
			var queryTargetPage = hashTargetPage.split("?")[1];
			var paramTargetPage = target.getAttribute('data-formdata') || target.getAttribute('data-params');
			if (paramTargetPage) {
				queryTargetPage = queryTargetPage + '&' + paramTargetPage;
			}
			
			// add on v2.7.4
			options.query = _queryToObject(queryTargetPage);
			options.id = hashTargetPage;

			var eleTargetPage = idTargetPage && document.getElementById(idTargetPage);
			if (back == false && rel == "auto") {
				back = Mobilebone.isBack(eleTargetPage, selfPage);
			}

			if (eleTargetPage) {
				var lastShip = store.lastShip;

				if (lastShip && eleTargetPage == lastShip[1] && selfPage == lastShip[0] && !go) {
					// back
					history.tempBack = true;
					history.back();
				} else {
					Mobilebone.transition(eleTargetPage, selfPage, back, options);
				}
			}
		} else if (/^javascript/.test(href)) {
			// back
			if (back) {
				history.tempBack = true;
				history.back();
			} else if (go) {
				history.tempGo = true;
				history.go(1);
			}			
		} else if (target.getAttribute("data-ajax") != "false") {
			event.preventDefault();
			// get a clean ajax url as page id
			var cleanUrl = Mobilebone.getCleanUrl(target);
			// add on v2.7.4
			options.query = _queryToObject(cleanUrl.split("?")[1]);
			// if has loaded and the value of 'data-reload' is not 'true'
			var attrReload = target.getAttribute("data-reload");

			// 之前已经请求过，内存中已经有，则直接使用
			if ((attrReload == null || attrReload == "false") && store[cleanUrl]) {
				if (back == false && rel == "auto") {
					back = Mobilebone.isBack(store[cleanUrl], selfPage);
				}
				options.id = cleanUrl;

				var body = container || document.body;

				if (body.contains(store[cleanUrl]) == false) {
					body.appendChild(store[cleanUrl]);
				}
				Mobilebone.transition(store[cleanUrl], selfPage, back, options);
			} else {	
				// as ajax
				Mobilebone.ajax(target);
			}
			
		}
	};

	/**
	 * closest polyfill
	 */
	if (!Element.prototype.matches) {
		Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
	}

	if (!Element.prototype.closest) {
		Element.prototype.closest = function (s) {
			var el = this;

			do {
				if (el.matches(s)) return el;
				el = el.parentElement || el.parentNode;
			} while (el !== null && el.nodeType === 1);

			return null;
		};
	}


	/**
	 * private method: convert query string to key-value object
	**/
	var _queryToObject = function(string) {
		var obj = {};
		if (typeof string == "string") {
			string.split("&").forEach(function(part) {
				var arrPart = part.split("=");
				if (arrPart.length > 1) {
					obj[arrPart[0]] = part.replace(arrPart[0] + "=", "");
				}
			});
		}
		return obj;
	};

	/**
	 * auto init
	**/
	window.addEventListener("DOMContentLoaded", function() {
		if (hasInited == false && Mobilebone.autoInit == true) {
			Mobilebone.init();
		}
	});

	/**
	 * page change when history change
	**/
	window.addEventListener("popstate", function() {
		if (history.popstateswipe == true) {
			location.reload();
			history.popstateswipe = false;
			return;
		}
		if (history.popstate == false) {
			history.popstate = true;
			return;
		}

		var hash = location.hash.replace("#&", "#").replace(/^#/, "");
		// add on v2.7.4
		var key = hash.split('?')[0];
		var pageIn = null;
		// add on v2.7.5
		var pageOut = document.querySelector(".in." + Mobilebone.classPage);
		// add on v2.6.1
		var container = null;

		if (hash == "") {
			// if no hash, get first page as 'pageIn'
			pageIn = document.querySelector("." + Mobilebone.classPage);
			if (pageIn.id) return;
		} else {
			// add on v2.7.4
			pageIn = store[key] || store[hash];

			// add on v2.6.1
			if (hash.split("container=").length == 2) {
				container = document.getElementById(hash.split("container=")[1].split("&")[0]);
			}

			// url address and with cache
			if (pageIn && isSimple.test(key) == false) {
				// just transition
				Mobilebone.transition(pageIn, pageOut, Mobilebone.isBack(pageIn, pageOut), {
					id: hash,  // fix issue #83
					history: false,
					container: container,
					target: document.activeElement,  // add on v2.7.7
					query: _queryToObject(hash.split('?')[1])
				});
				return;
			}
		}

		if (!pageIn) {
			if (isSimple.test(hash) == false) {
				// as a url
				Mobilebone.ajax({
					url: hash,
					dataType: "unknown",
					// no cache url, usually reload ajax
					back: Mobilebone.isBack(),
					container: container
				});
				return;
			}
			pageIn = document.querySelector("#" + key) || document.querySelector("#" + hash);
		}

		if ((pageIn && pageIn == pageOut) || Mobilebone.pushStateEnabled == false) return;

		// hash ↔ id
		if (pageIn) {
			Mobilebone.transition(pageIn, pageOut, Mobilebone.isBack(pageIn, pageOut), {
				id: hash,  // fix issue #83
				history: false,
				target: document.activeElement,  // add on v2.7.7
				query: _queryToObject(hash.split('?')[1])
			});
		}
	});

	document.MBLOADED = true;

	return Mobilebone;
});
<MSG> change version

change version to '1.0.0'

<DFF> @@ -45,7 +45,7 @@
 	 *
 	 * @type string
 	**/
-	Mobilebone.VERSION = '0.0.0';
+	Mobilebone.VERSION = '1.0.0';
 	
 	/**
 	 * Whether bind events when dom ready

{'commit': 'diff --git a/Gruntfile.js b/Gruntfile.js\ndeleted file mode 100644\nindex 50eccce..0000000\n--- a/Gruntfile.js\n+++ /dev/null\n@@ -1,69 +0,0 @@\n-/*********************\n-说明：文件结构基于 HTML5Boilerplate:\n-\n-+ index.html\n-\n-- css\n-  + main.css\n-\n-- js\n-  - vendor\n-  + main.js\n-\n-+ img/\n-\n-***************************/\n-\n-/*global module:false*/\n-module.exports = function (grunt) {\n-    \'use strict\';\n-    grunt.initConfig({\n-        csslint: {\n-            /* 检查 CSS 语法 */\n-            src: [\'css/**/*.css\']\n-        },\n-        jshint: {\n-            /* 检查 js 语法 */\n-            all: [\'Gruntfile.js\', \'js/main.js\', \'js/lib/*.js\']\n-        },\n-        cssmin: {\n-            /*压缩 CSS 文件为 .min.css */\n-            options: {\n-                keepSpecialComments: 0 /* 移除 CSS 文件中的所有注释 */\n-            },\n-            minify: {\n-                expand: true,\n-                cwd: \'css/\',\n-                src: [\'all.css\'],\n-                dest: \'css/\',\n-                ext: \'.min.css\'\n-            }\n-        },\n-        uglify: {\n-            /* 最小化、混淆、合并 JavaScript 文件 */\n-            target: {\n-                files: {\n-                    \'js/all.min.js\': [\'js/all.js\']\n-                }\n-            },\n-            minjs: { //最小化、混淆所有 js/ 目录下的 JavaScript 文件\n-                files: [{\n-                    expand: true,\n-                    cwd: \'js/\',\n-                    src: [\'**/*.js\', \'!**/*.min.js\'],\n-                    dest: \'js/\',\n-                    ext: \'.min.js\'\n-                }]\n-            }\n-        }\n-    });\n-    grunt.loadNpmTasks(\'grunt-contrib-csslint\');\n-    grunt.loadNpmTasks(\'grunt-contrib-jshint\');\n-    grunt.loadNpmTasks(\'grunt-contrib-cssmin\');\n-    grunt.loadNpmTasks(\'grunt-contrib-uglify\');\n-    // 定义默认任务\n-    grunt.registerTask(\'default\', [\'csslint\', \'jshint\', \'cssmin\', \'uglify\']);\n-    grunt.registerTask(\'css\', [\'cssmin\']);\n-    grunt.registerTask(\'dev\', [\'csslint\', \'jshint\']);\n-    grunt.registerTask(\'dest\', [\'cssmin\', \'uglify:minjs\']);\n-};\n\\ No newline at end of file\ndiff --git a/package.json b/package.json\nindex cc5ed2f..07631eb 100644\n--- a/package.json\n+++ b/package.json\n@@ -1,6 +1,6 @@\n {\n   "name": "mobilebone",\n-  "version": "0.0.0",\n+  "version": "1.0.0",\n   "description": "Bone for mobile web APP with a sigle page mode.",\n   "main": "src/mobilebone.js",\n   "directories": {\ndiff --git a/src/mobilebone.js b/src/mobilebone.js\nindex 6c2587c..258038e 100644\n--- a/src/mobilebone.js\n+++ b/src/mobilebone.js\n@@ -45,7 +45,7 @@\n \t *\n \t * @type string\n \t**/\n-\tMobilebone.VERSION = \'0.0.0\';\n+\tMobilebone.VERSION = \'1.0.0\';\n \t\n \t/**\n \t * Whether bind events when dom ready\n', 'commit_subject': 'change version', 'repo_name': 'zhangxinxu/mobilebone', 'committer': {'name': 'zhangxinxu', 'email': '1091875180c30bff62e79f9fa105f735792f08ba@zhangxinxu.com', 'time_sec': '1414580520', 'tz_offset': '480', 'date': {'seconds': '1414580520'}}, 'commit_tree': '84b600c7e5f135cd0cf5743d77cf656ddcee80a0', 'commit_message': "change version\n\nchange version to '1.0.0'\n", 'language_name': 'PHP', 'license': 'mit', 'hunks': "@@ -45,7 +45,7 @@\n \t *\n \t * @type string\n \t**/\n-\tMobilebone.VERSION = '0.0.0';\n+\tMobilebone.VERSION = '1.0.0';\n \t\n \t/**\n \t * Whether bind events when dom ready\n", 'addition_count': 1, 'deletion_count': 1, 'src_file': 'a/src/mobilebone.js', 'tgt_file': 'b/src/mobilebone.js', 'file_extension': '.js', 'before_file': '/**! \n * @author @copyright zhangxinxu(.com) \n * @since 2014-09-26\n * @description mobilebone.js(v2.8.1) - bone of switch for mobile web app - https://github.com/zhangxinxu/mobilebone\n * @license MIT\n**/\n\n(function(root, factory) {\n\tif (document.MBLOADED) {\n\t\troot.console && console.warn("Don\\\'t repeat load Mobilebone!");\n\t\treturn;\n\t}\n\t// Set up Mobilebone appropriately for the environment.\n\tif (typeof define === "function" && (define.amd || define.cmd)) {\n\t\tdefine("mobilebone", function(exports) {\n\t\t\treturn factory(root, exports);\n\t\t});\n\t} else if ( typeof module === "object" && typeof module.exports === "object" ) {\n\t\tmodule.exports = factory(root, {});\n\t} else {\n\t\t// Finally, as a browser global.\n\t\troot.Mobilebone = factory(root, {});\n\t}\n})((typeof global !== "undefined") ? global\n: ((typeof window !== "undefined") ? window\n\t: ((typeof self !== "undefined") ? self : this)), function(root, Mobilebone) {\n\t// Avoid repeated callbacks\n\tvar store = {};\n\n\t// Create local references to array methods we\'ll want to use later.\n\tvar array = [];\n\tvar slice = array.slice;\n\n\t// Is it a id selector\n\tvar isSimple = /^#?\\w+(?:[\\-_]\\w+)*$/i;\n\n\t// Is it webkit\n\tvar isWebkit = "WebkitAppearance" in document.documentElement.style || typeof document.webkitHidden != "undefined";\n\n\t// Is it suppory history API\n\tvar supportHistory = "pushState" in history && "replaceState" in history;\n\n\tMobilebone.support = supportHistory;\n\n\t *\n\t * @type string\n\t**/\n\tMobilebone.VERSION = \'0.0.0\';\n\t\n\t/**\n\t * Whether bind events when dom ready\n\tMobilebone.VERSION = "2.8.1";\n\n\t/**\n\t * Whether auto init Mobilebone\n\t * If this value is false, you shou excute Mobilebone.init() in the right place\n\t */\n\tMobilebone.autoInit = true;\n\n\t/**\n\t * Whether catch attribute of href from element with tag \'a\'\n\t * If the value set to false, jump links in a refresh form(not slide)\n\t * In most cases, you do not need to care about this parameter.\n\t   Except some special pages that should refresh all links, as test/index.html show.\n\t   However, if your only want several links refesh, you can use data-ajax="false" or data-rel="external"\n\t *\n\t * @type boolean\n\t**/\n\tMobilebone.captureLink = true;\n\n\t/**\n\t * Whether catch events of \'submit\' from <form> element\n\t * If the value set to false, <form> is a normal form except data-ajax="true"\n\t * If the value set to true, <form> will submit as a ajax request,\n\t   and the return value will be used to create a new page and transition into meanwhile.\n\t   However, if data-ajax="false", <form> won\'t submit as a ajax.\n\t *\n\t * @type boolean\n\t**/\n\tMobilebone.captureForm = true;\n\n\t/**\n\t * The root of transition-callback\n\t * Default value is \'root\', you can consider as window-object.\n\t   However, there are may many callbacks, it\'s impossible that all functions are global function.\n\t   We may custom a global object to store our callbacks, such as:\n\t   Callback = {\n\t\t fun1: function() {},\n\t\t fun2: function() {},\n\t\t fun3: function() {},\n\t   }\n\t   In this case, the value of \'obilebone.rootTransition\' should set Callback;\n\t *\n\t * @type object\n\t**/\n\tMobilebone.rootTransition = root;\n\n\t/**\n\t * Whether merge(vs cover) global callback and local callback\n\t *\n\t * @type boolean\n\t**/\n\tMobilebone.mergeCallback = true;\n\n\t/**\n\t *  className of animation\n\t *\n\t * @type string\n\t**/\n\tMobilebone.classAnimation = "slide";\n\t/**\n\t *  for mark page element\n\t *\n\t * @type string\n\t**/\n\tMobilebone.classPage = "page";\n\t/**\n\t * className for mark mask element\n\t *\n\t * @type string\n\t**/\n\tMobilebone.classMask = "mask";\n\t/**\n\t * Whether url changes when history changes\n\t * If this value is false, the url will be no change.\n\t *\n\t * @type boolean\n\t**/\n\tMobilebone.pushStateEnabled = true;\n\t/**\n\t * Whether excute JavaScript when ajax HTML loaded\n\t * If this value is true, the script will excute.\n\t *\n\t * @type boolean\n\t**/\n\tMobilebone.evalScript = false;\n\t\n\t// When running inside a FF iframe, calling replaceState causes an error. So set \'pushStateEnabled = false\'\n\tif (navigator.userAgent.indexOf( "Firefox" ) >= 0 && window.top !== window) {\n\t\tMobilebone.pushStateEnabled = false;\n\t}\n\n\t/**\n\t * if browser do not support history/classList, stop here\n\t**/\n\tif (supportHistory == false) {\n\t\treturn Mobilebone;\n\t}\n\n\t/**\n\t * don\'t excute window.onpopstate when page load\n\t**/\n\thistory.popstate = false;\n\n\t/**\n\t * Function for transition\n\t * In most cases, you are unnecessary to use this function , unlike Mobilebone.createPage\n\n\t * @params  pageInto: dom-object. Element which will transform into. - Necessary\n\t            pageOut:  dom-object. Element which will transform out.   - Optional\n\t\t\t    back:     boolean.    Direction of transition.          - Optional\n\t\t\t    options:  object.     Cover or add parameters.           - Optional\n\t * @returns undefined\n\t * @example Mobilebone.transition(element);\n\t            Mobilebone.transition(element1, element2);\n\t\t        Mobilebone.transition(element1, element2, true);\n\t\t        Mobilebone.transition(element1, element2, { id: "only" });\n\t\t        Mobilebone.transition(element1, element2, true, { id: "only" });\n\t**/\n\tMobilebone.transition = function(pageInto, pageOut, back, options) {\n\t\tif (arguments.length == 0 || pageInto == pageOut) return;\n\t\tif (arguments.length == 3 && isNaN(back * 1) == true) {\n\t\t\toptions = back;\n\t\t\tback = options.back;\n\t\t};\n\n\t\t//if those parameters is missing\n\t\tpageOut = pageOut || null, back = back || false, options = options || {};\n\n\t\t// defaults parameters\n\t\tvar defaults = {\n\t\t\t// the value of callback is a key name, and the host is root here.\n\t\t\t// eg. if the name of animationstart is \'doLoading\', so the script will execute \'root.doLoading()\'\n\t\t\t// By default, the value of root is \'window\'\n\t\t\troot: this.rootTransition,\n\t\t\t// the form of transition, the value (eg. \'slide\') will be a className to add or remove.\n\t\t\t// of course, u can set to other valeu, for example, \'fade\' or \'flip\'. However, u shou add corresponding CSS3 code.\n\t\t\tform: this.form || this.classAnimation,\n\t\t\t// \'animationstart/animationend/...\' are callbacks params\n\t\t\t// Note: those all global callbacks!\n\t\t\tonpagefirstinto: this.onpagefirstinto,\n\t\t\tanimationstart: this.animationstart,\n\t\t\tanimationend: this.animationend,\n\t\t\tpreventdefault: this.preventdefault,\n\t\t\tfallback: this.fallback,\n\t\t\tcallback: this.callback\n\t\t}, params = function(element) {\n\t\t\tif (!element || !element.getAttribute) return {};\n\n\t\t\tvar _params = {}, _dataparams = _queryToObject(element.getAttribute("data-params") || "");\n\n\t\t\t// rules as follow:\n\t\t\t// data-* > data-params > options > defaults\n\t\t\t["title", "root", "form"].forEach(function(key) {\n\t\t\t\t_params[key] = element.getAttribute("data-" + key) || _dataparams[key] || options[key] || defaults[key];\n\t\t\t});\n\n\t\t\tif (typeof _params.root == "string") {\n\t\t\t\t_params.root = Mobilebone.getFunction(_params.root);\n\t\t\t}\n\n\t\t\t["onpagefirstinto", "callback", "fallback", "animationstart", "animationend", "preventdefault"].forEach(function(key) {\n\t\t\t\tif (Mobilebone.mergeCallback == true && typeof defaults[key] == "function") {\n\t\t\t\t\t// merge global callback\n\t\t\t\t\tvar local_function_key = element.getAttribute("data-" + key) || _dataparams[key];\n\t\t\t\t\tif (typeof _params.root[local_function_key] == "function") {\n\t\t\t\t\t\t_params[key] = function() {\n\t\t\t\t\t\t\tdefaults[key].apply(this, arguments);\n\t\t\t\t\t\t\t_params.root[local_function_key].apply(this, arguments);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (typeof options[key] == "function") {\n\t\t\t\t\t\t_params[key] = function() {\n\t\t\t\t\t\t\tdefaults[key].apply(this, arguments);\n\t\t\t\t\t\t\toptions[key].apply(this, arguments);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_params[key] = defaults[key];\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// replace global callback\n\t\t\t\t\t_params[key] = element.getAttribute("data-" + key) || _dataparams[key] || options[key] || defaults[key];\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn _params;\n\t\t};\n\n\t\t// get params of each\n\t\tvar paramsOut = params(pageOut), paramsIn = params(pageInto);\n\n\t\tif (pageOut != null && pageOut.classList) {\n\t\t\t// weather prevent transition\n\t\t\tvar preventOut = paramsOut.preventdefault, isPreventOut = false;\n\t\t\tif (typeof preventOut == "string") {\n\t\t\t\tpreventOut = paramsOut.root[preventOut];\n\t\t\t}\n\t\t}\n\t\tif (pageInto != null && pageInto.classList) {\n\t\t\t// weather prevent transition\n\t\t\tvar preventInto = paramsIn.preventdefault, isPreventInto = false;\n\t\t\tif (typeof preventInto == "string") {\n\t\t\t\tpreventInto = paramsIn.root[preventInto];\n\t\t\t}\n\t\t}\n\t\tif (typeof preventOut == "function") {\n\t\t\tisPreventOut = preventOut.call(paramsOut.root, pageInto, pageOut, options);\n\t\t}\n\n\t\t// if functions of \'preventdefault\' are same for pageIn and pageout, just excute once.\n\t\tif (isPreventOut == true && preventOut === preventInto) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (typeof preventInto == "function") {\n\t\t\tisPreventInto = preventInto.call(paramsIn.root, pageInto, pageOut, options);\n\t\t}\n\t\t// if pageinto stopped, stop all\n\t\tif (isPreventInto == true) {\n\t\t\t// only run here and nothing more\n\t\t\treturn false;\n\t\t}\n\n\t\t// set animation callback as a method\n\t\tvar funAnimationCall = function(page, data) {\n\t\t\tif (page.flagAniBind == true) return;\n\t\t\t// do callback when animation start/end\n\t\t\t["animationstart", "animationend"].forEach(function(animationkey, index) {\n\t\t\t\tvar animition = paramsIn[animationkey], webkitkey = "webkit" + animationkey.replace(/^a|s|e/g, function(matchs) {\n\t\t\t\t\treturn matchs.toUpperCase();\n\t\t\t\t});\n\t\t\t\tvar animateEventName = isWebkit? webkitkey: animationkey;\n\t\t\t\t// if it\'s the out element, hide it when \'animationend\'\n\t\t\t\tif (index) {\n\t\t\t\t\tpage.addEventListener(animateEventName, function() {\n\t\t\t\t\t\tif (this.classList.contains("in") == false) {\n\t\t\t\t\t\t\tthis.style.display = "none";\n\t\t\t\t\t\t\t// add on v2.5.5\n\t\t\t\t\t\t\t// move here on v2.5.8\n\t\t\t\t\t\t\t// main for remove page is just current page\n\t\t\t\t\t\t\t// remove on v2.7.0\n\t\t\t\t\t\t\t// if (this.removeSelf == true) {\n\t\t\t\t\t\t\t// \tthis.parentElement.removeChild(this);\n\t\t\t\t\t\t\t// \tthis.removeSelf = null;\n\t\t\t\t\t\t\t// }\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.classList.remove(params(this).form);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// bind animation events\n\t\t\t\tif (typeof animition == "string" && paramsIn.root[animition]) {\n\t\t\t\t\tpage.addEventListener(animateEventName, function() {\n\t\t\t\t\t\tdata.root[animition].call(data.root, this, this.classList.contains("in")? "into": "out", options);\n\t\t\t\t\t});\n\t\t\t\t} else if (typeof animition == "function") {\n\t\t\t\t\tpage.addEventListener(animateEventName, function() {\n\t\t\t\t\t\tanimition.call(data.root, this, this.classList.contains("in")? "into": "out", options);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// set a flag\n\t\t\t\tpage.flagAniBind = true;\n\t\t\t});\n\t\t};\n\n\t\tif (pageOut != null && pageOut.classList) {\n\t\t\t// do transition if there are no \'prevent\'\n\t\t\tif (isPreventOut != true) {\n\t\t\t\tpageOut.classList.add(paramsOut.form);\n\t\t\t\t// reflow\n\t\t\t\tpageOut.offsetWidth;\n\t\t\t\t// go, go, go\n\t\t\t\tpageOut.style.display = "block";\n\t\t\t\tpageOut.classList.add("out");\n\t\t\t\tpageOut.classList.remove("in");\n\t\t\t\t// if reverse direction\n\t\t\t\tpageOut.classList[back? "add": "remove"]("reverse");\n\n\t\t\t\t// add on v2.5.5\n\t\t\t\tpageOut.removeSelf = pageOut.removeSelf || null;\n\n\t\t\t\t// set animation callback for \'pageInto\'\n\t\t\t\t// for issues #153\n\t\t\t\tfunAnimationCall(pageOut, paramsOut);\n\n\t\t\t\t// do fallback every time\n\t\t\t\tvar fallback = paramsOut.fallback;\n\t\t\t\tif (typeof fallback == "string") {\n\t\t\t\t\tfallback = paramsOut.root[fallback];\n\t\t\t\t}\n\t\t\t\tif (typeof fallback == "function") {\n\t\t\t\t\tfallback.call(paramsOut.root, pageInto, pageOut, options);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (pageInto != null && pageInto.classList) {\n\t\t\t// for title change\n\t\t\tvar title = paramsIn.title,\n\t\t\t    header = document.querySelector("h1"),\n\t\t\t    firstPage = document.querySelector("." + this.classPage);\n\n\t\t\t// do title change\n\t\t\tif (title && options.title !== false) {\n\t\t\t\tdocument.title = title;\n\t\t\t\tif (header) {\n\t\t\t\t\theader.innerHTML = title;\n\t\t\t\t\theader.title = title;\n\t\t\t\t}\n\t\t\t} else if (firstPage == pageInto && !pageOut && document.title) {\n\t\t\t\t// set data-title for first visibie page\n\t\t\t\tpageInto.setAttribute("data-title", document.title);\n\t\t\t}\n\n\t\t\t// delete page with same id when options.remove !== false\n\t\t\tvar pageid = options.id || pageInto.id;\n\t\t\tvar hashid = options.id || pageInto.id;\n\n\t\t\t// v2.7.0 change rule -> don\'t auto delete pages\n\t\t\t//                    -> delete by using Mobilebone.remove();\n\t\t\t// v2.7.2 add using data-reload="xxxxId" to auto remove pages\n\t\t\t// Reason Two:\n\t\t\t// 1. Most websites are so simple that it\'s not enough to talk about performance;\n\t\t\t// 2. We can\'t judge relation by two page\'s url\n\t\t\t\t// eg, below \'if\' sentence only cover sence 0:\n\t\t\t\t/*\n\t\t\t\tif (options.id) {\n\t\t\t\t\t// eg. get \'ajax.php\' from \'ajax.php?xx=1\'\n\t\t\t\t \tpageid = pageid.split("?")[0];\n\t\t\t\t}\n\t\t\t\t*/\n\n\t\t\t\t// 0. url format: //xxx.com/list.php\n\t\t\t\t                  //xxx.com/detail.php?id=1\n\t\t\t\t                  //xxx.com/detail.php?id=2\n\n\t\t\t\t// but not sence 1 and 2:\n\n\t\t\t\t// 1. url format: //xxx.com/list/111/\n\t\t\t\t                  //xxx.com/list/222/\n\t\t\t\t                  //xxx.com/list/333/\n\t\t\t\t                  // 明明需要删除的，没删除\n\n\t            // 2. url format: //xxx.com/index.php   列表页\n\t                              //xxx.com/index.php?list=1   列表详情页1\n\t                              //xxx.com/index.php?list=2   列表详情页2\n\t                              // 以上列表页就嗝屁了\n\n\t\t\t// var relid = store["_" + pageid];\n\t\t\t// // only delete page auto when data-reload is not \'false\' or null\n\t\t\t// // hashid may store the same page, we should delete also\n\t\t\t// // when data-reload not \'false\' or null\n\t\t\t// // v2.4.4+\n\t\t\t// if (relid && store[relid] && options.reload == true) {\n\t\t\t// \tdelete store[relid];\n\t\t\t// \tdelete store["_" + pageid];\n\t\t\t// }\n\n\t\t\t// // below commented on v2.6.2\n\t\t\t// if (options.reload == true) {\n\t\t\t// \t// v2.5.8 for issues #147\n\t\t\t// \tpageInto.removeSelf = true;\n\t\t\t// }\n\n\t\t\t// if (store[pageid] && store[pageid] != pageInto) {\n\n\n\t\t\t// \tif (store[pageid] != pageOut) {\n\t\t\t// \t\tstore[pageid].parentElement && store[pageid].parentElement.removeChild(store[pageid]);\n\t\t\t// \t} else {\n\t\t\t// \t\t// if the page element same as store\n\t\t\t// \t\t// remove when animationend\n\t\t\t// \t\tpageOut.removeSelf = true;\n\t\t\t// \t}\n\t\t\t// \tdelete store[pageid];\n\t\t\t// }\n\n\t\t\t// do transition\n\t\t\tif (pageOut) {\n\t\t\t\tpageInto.classList.add(paramsIn.form);\n\t\t\t}\n\t\t\t// iOS bug\n\t\t\t// reflow for fixing issues #80, #86\n\t\t\tpageInto.offsetWidth;\n\t\t\t// go~ as normal\n\t\t\tpageInto.style.display = "block";\n\t\t\tpageInto.classList.remove("out");\n\t\t\tpageInto.classList.add("in");\n\t\t\t// if reverse direction\n\t\t\tpageInto.classList[back? "add": "remove"]("reverse");\n\n\t\t\t// do callback when come in first time\n\t\t\tvar onpagefirstinto = paramsIn.onpagefirstinto;\n\t\t\t// first judge change to pageInto store\n\t\t\t// v2.5.5 add for fix issues #138\n\t\t\tif (!pageInto.firstintoBind) {\n\t\t\t\tif (typeof onpagefirstinto == "string" && paramsIn.root[onpagefirstinto]) {\n\t\t\t\t\tparamsIn.root[onpagefirstinto].call(paramsIn.root, pageInto, pageOut, options);\n\t\t\t\t} else if (typeof onpagefirstinto == "function") {\n\t\t\t\t\tonpagefirstinto.call(paramsIn.root, pageInto, pageOut, options);\n\t\t\t\t}\n\t\t\t\t// capture form submit\n\t\t\t\tslice.call(pageInto.querySelectorAll("form")).forEach(function(form) {\n\t\t\t\t\tMobilebone.submit(form);\n\t\t\t\t});\n\n\t\t\t\tpageInto.firstintoBind = true;\n\t\t\t}\n\n\t\t\t// set animation callback for \'pageInto\'\n\t\t\tfunAnimationCall(pageInto, paramsIn);\n\n\t\t\t// history\n\t\t\t// hashid should a full url address\n\t\t\t// different with pageid\n\t\t\t// add on 2.4.2\n\t\t\tvar urlPush = hashid, urlPushReplace = "";\n\n\t\t\tif (urlPush && /#/.test(urlPush) == false) {\n\t\t\t\turlPush = "#" + urlPush;\n\t\t\t}\n\t\t\turlPushReplace = urlPush.replace(/#/, "#&");\n\n\t\t\tif (supportHistory && this.pushStateEnabled && options.history !== false && urlPush\n\t\t\t\t// hash should be different\n\t\t\t\t// can fix issues #79, #87 maybe\n\t\t\t\t&& urlPushReplace != location.hash\n\t\t\t) {\n\t\t\t\t// don\'t trigger \'popstate\' events\n\t\t\t\thistory.popstate = false;\n\t\t\t\t// if only pageIn, use \'replaceState\'\n\t\t\t\thistory[pageOut ? "pushState" : "replaceState"](null, document.title, urlPush.replace(/#/, "#&"));\n\t\t\t}\n\n\t\t\t// store page-id, just once\n\t\t\tif (!store[pageid]) {\n\t\t\t\tstore[pageid] = pageInto;\n\t\t\t\t// when we back/prev, we need to get true\n\t\t\t\t// comment on v2.7.0\n\t\t\t\t// if (hashid !== pageid) {\n\t\t\t\t// \tstore[hashid] = pageInto;\n\t\t\t\t// \tstore["_" + pageid] = hashid;\n\t\t\t\t// }\n\t\t\t}\n\n\t\t\t// do callback every time\n\t\t\tvar callback = paramsIn.callback;\n\n\t\t\tif (typeof callback == "string") {\n\t\t\t\tcallback = paramsIn.root[callback];\n\t\t\t}\n\t\t\tif (typeof callback == "function") {\n\t\t\t\tcallback.call(paramsIn.root, pageInto, pageOut, options);\n\t\t\t}\n\n\t\t\t// Safari do \'popstate\' after \'pushState/replaceState\'\n\t\t\t// So, we neet setTimeout to avoid excuting \'Mobilebone.transition()\' twice\n\t\t\tsetTimeout(function() {\n\t\t\t\t// reset to popable state\n\t\t\t\thistory.popstate = true;\n\t\t\t}, 17);\n\n\t\t\t// add on v2.7.5 improve back user experence\n\t\t\t// add on v2.7.6\n\t\t\t// only not back transiton store last ship\n\t\t\tif (back == false && history.popstate == false) {\n\t\t\t\tstore.lastShip = [pageInto, pageOut];\n\t\t\t} else {\n\t\t\t\tstore.lastShip = null;\n\t\t\t}\n\t\t}\n\t};\n\n\t/**\n\t * Remove page DOM add on v2.7.0\n\t * @param  domOrId: dom-object|string\n\t *          page DOM or <a> DOM\n\t *          page id or <a> href value\n\t * @return {[type]}         [description]\n\t */\n\tMobilebone.remove = function (domOrId) {\n\t\tif (!domOrId) {\n\t\t\treturn this;\n\t\t}\n\n\t\tvar elePage = domOrId;\n\t\tvar pageid = domOrId;\n\t\tif (typeof pageid == "string") {\n\t\t\telePage = store[pageid];\t\t\t\n\t\t} else if (elePage.tagName && elePage.tagName.toLowerCase() == "a") {\n\t\t\tpageid = this.getCleanUrl(elePage);\n\t\t\telePage = store[pageid];\n\t\t}\n\n\t\tif (elePage && elePage.parentElement) {\n\t\t\t// remove store\n\t\t\tfor (var key in store) {\n\t\t\t\tif (store[key] == elePage) {\n\t\t\t\t\tdelete store[key];\n\t\t\t\t}\n\t\t\t}\n\t\t\telePage.parentElement.removeChild(elePage);\n\t\t}\n\t};\n\n\n\t/**\n\t * For getting whole ajax url\n\t * In most cases, you are unnecessary to use this function\n\n\t * @params  trigger: dom-object. element with tag-"a".  - Optional(at least one)\n\t            url:     string. ajax url.                  - Optional(at least one)\n\t\t\t    params:  string|object. ajax params.        - Optional\n\t * @returns string\n\t * @example Mobilebone.getCleanUrl(elementOfA);\n\t            Mobilebone.getCleanUrl(elementOfForm);\n\t            Mobilebone.getCleanUrl(elementOfA, \'\', "a=1&b=2");\n\t\t        Mobilebone.getCleanUrl(null, "xxx.html");\n\t\t        Mobilebone.getCleanUrl(null, "xxx.html?a=1&b=2");\n\t\t        Mobilebone.getCleanUrl(null, "xxx.html", "a=1&b=2");\n\t**/\n\tMobilebone.getCleanUrl = function(trigger, url, params) {\n\t\tvar href = "", formdata = "", cleanUrl = "";\n\t\tif (trigger) {\n\t\t\tif (trigger.nodeType == 1) {\n\t\t\t\t// form element\n\t\t\t\tif (trigger.action) {\n\t\t\t\t\thref = trigger.getAttribute("action");\n\t\t\t\t\t// add on v2.4.1\n\t\t\t\t\tif (trigger.method && trigger.method.toUpperCase() == "POST") {\n\t\t\t\t\t\treturn href;\n\t\t\t\t\t} else if (window.$ && $.fn && $.fn.serialize) {\n\t\t\t\t\t\t// use jquery serialize()\n\t\t\t\t\t\tformdata = $(trigger).serialize();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tformdata = {};\n\t\t\t\t\t\t// simple serialize from Mobilebone\n\t\t\t\t\t\tslice.call(trigger.querySelectorAll("input,select,textarea")).forEach(function(control) {\n\t\t\t\t\t\t\tif (control.name && !control.disabled) {\n\t\t\t\t\t\t\t\tvar val = control.value.trim(), name = control.name;\n\t\t\t\t\t\t\t\tif (/^radio|checkbox/i.test(control.type)) {\n\t\t\t\t\t\t\t\t\tif (control.checked) {\n\t\t\t\t\t\t\t\t\t\tif (formdata[name]) {\n\t\t\t\t\t\t\t\t\t\t\tformdata[name].push(val);\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tformdata[name] = [val];\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tformdata[name] = [val];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// a element\n\t\t\t\t\thref = trigger.getAttribute("href");\n\t\t\t\t\tif (/^javascript/.test(href)) {\n\t\t\t\t\t\thref = \'\';\n\t\t\t\t\t}\n\t\t\t\t\tformdata = trigger.getAttribute("data-formdata") || trigger.getAttribute("data-params") || "";\n\t\t\t\t\t// v2.6.1 for #107\n\t\t\t\t\t// remember container when refresh\n\t\t\t\t\tvar strContainer = "container", attrContainer = trigger.getAttribute("data-" + strContainer);\n\t\t\t\t\tif (formdata.indexOf(strContainer) == -1 && attrContainer) {\n\t\t\t\t\t\tvar queryContainer = strContainer + "=" + attrContainer;\n\t\t\t\t\t\tformdata = formdata ? formdata + "&" + queryContainer : queryContainer;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (trigger.url) {\n\t\t\t\thref = trigger.url;\n\t\t\t\tformdata = trigger.data;\n\t\t\t}\n\t\t}\n\n\t\tif (!(href = href || url)) {\n\t\t\treturn "";\n\t\t}\n\n\t\t// get formdata\n\t\tformdata = formdata || params || "";\n\n\t\tif (typeof formdata == "object") {\n\t\t\tvar arrData = [];\n\t\t\tfor (key in formdata) {\n\t\t\t\tif (!formdata[key].forEach) {\n\t\t\t\t\tformdata[key] = [formdata[key]];\n\t\t\t\t}\n\t\t\t\tformdata[key].forEach(function(keyValue) {\n\t\t\t\t\tarrData.push(key + "=" + encodeURIComponent(keyValue));\n\t\t\t\t});\n\n\t\t\t}\n\t\t\tif (arrData.length > 0) {\n\t\t\t\tformdata = arrData.join("&");\n\t\t\t} else {\n\t\t\t\tformdata = "";\n\t\t\t}\n\t\t}\n\n\t\t// get url of root\n\t\tcleanUrl = href.split("#")[0].replace(/&+$/, "").replace(/^\\.\\/+/, "");\n\n\t\tif (cleanUrl.slice(-1) == "?") {\n\t\t\tcleanUrl = cleanUrl.split("?")[0];\n\t\t}\n\t\t// url = root_url + joiner + formdata\n\t\tif (formdata != "") {\n\t\t\tif (/\\?/.test(cleanUrl)) {\n\t\t\t\tformdata = formdata.replace(/^&|\\?/, "");\n\t\t\t\tcleanUrl = cleanUrl + "&" + formdata;\n\t\t\t} else if (formdata != "") {\n\t\t\t\tformdata = formdata.replace("?", "");\n\t\t\t\tcleanUrl = cleanUrl + "?" + formdata;\n\t\t\t}\n\t\t}\n\t\treturn cleanUrl;\n\t};\n\n\t/**\n\t * Create page according to given Dom-element or HTML string. And, notice!!!!! will do transition auto.\n\n\t * @params  domHtml:        dom-object|string. Create this to dom element as a role of into-page.               - Necessary\n\t            eleOrObj: dom-object|object. \'.page element\', or \'a element\', or \'options\' for get out-page   - Optional\n\t\t\t\toptions:            object.            basically, options = ajax options, of course, u can custom it!   - Optional\n\t * @returns undefined\n\t * @example Mobilebone.createPage(pageDom);\n\t            Mobilebone.createPage(generalDom);\n\t\t        Mobilebone.createPage(\'<div class="page out">xxx</div>\');\n\t\t        Mobilebone.createPage(\'<p>xxx</p>\');\n\t\t        Mobilebone.createPage(pageDom, triggerLink);\n\t\t        Mobilebone.createPage(pageDom, { response: \'<div...>\' });\n\t\t        Mobilebone.createPage(pageDom, triggerLink, { response: \'<div...>\' });\n\t *\n\t**/\n\tMobilebone.createPage = function(domHtml, eleOrObj, options) {\n\t\tvar response = null, container = null, classPage = this.classPage, isreload = null;\n\t\t// \'eleOrObj\' can \'.page element\', or \'a element\', or \'options\'\n\t\t// basically, options = ajax options, of course, u can custom it!\n\t\tif (!domHtml) return;\n\t\tif (typeof options == "undefined" && typeof eleOrObj == "object") {\n\t\t\toptions = eleOrObj;\n\t\t}\n\t\toptions = options || {};\n\n\t\t// \'options\' that \'Mobilebone.transition()\' needs\n\t\tvar optionsTransition = {};\n\n\t\t// get page-title from eleOrObj or options\n\t\tvar titlePage, idContainer, classPageInside;\n\n\t\tif (eleOrObj) {\n\t\t\tif (eleOrObj.nodeType == 1) {\n\t\t\t\t// legal elements\n\t\t\t\tif (eleOrObj.href || eleOrObj.action) {\n\t\t\t\t\ttitlePage = eleOrObj.getAttribute("data-title") || options.title;\n\t\t\t\t}\n\t\t\t\tresponse = options.response;\n\t\t\t\tidContainer = eleOrObj.getAttribute("data-container");\n\t\t\t\tcontainer = document.getElementById(idContainer);\n\t\t\t\tclassPageInside = eleOrObj.getAttribute("data-classpage");\n\t\t\t\t// pass element as target params, add on v2.3.0\n\t\t\t\toptionsTransition.target = eleOrObj;\n\t\t\t\t// v2.4.4 is_root → isreload\n\t\t\t\tisreload = eleOrObj.getAttribute("data-reload");\n\t\t\t\tif (eleOrObj.tagName.toLowerCase() == "form" || (isreload !== null && isreload != "false")) {\n\t\t\t\t\toptionsTransition.reload = true;\n\t\t\t\t}\n\t\t\t\t// v2.5.2\n\t\t\t\t// is back? for issues #128\n\t\t\t\toptionsTransition.back = eleOrObj.getAttribute("data-rel") == "back";\n\n\t\t\t\t// v2.6.0 history\n\t\t\t\tif (eleOrObj.getAttribute("data-history") == "false") {\n\t\t\t\t\toptionsTransition.history = false;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresponse = eleOrObj.response || options.response;\n\t\t\t\ttitlePage = eleOrObj.title || options.title;\n\t\t\t\tcontainer = eleOrObj.container || options.container;\n\t\t\t\tclassPageInside = eleOrObj.classPage || options.classPage;\n\t\t\t\toptionsTransition.target = eleOrObj.target;\n\t\t\t\t// v2.5.2\n\t\t\t\t// is back? for issues #128\n\t\t\t\toptionsTransition.back = eleOrObj.back || options.back;\n\t\t\t}\n\t\t\tif (container && classPageInside) {\n\t\t\t\tclassPage = classPageInside;\n\t\t\t}\n\t\t}\n\n\t\t// get current page(will be out) according to \'page_or_child\'\n\t\tvar eleCurrentPage = (classPage == classPageInside? container : document).querySelector(".in." + classPage);\n\n\t\t// get create page (will be into) according to \'domHtml\'\n\t\tvar eleCreatePage = null;\n\n\t\tvar create = document.createElement("div");\n\t\tif (typeof domHtml == "string") {\n\t\t\tcreate.innerHTML = domHtml;\n\t\t} else {\n\t\t\tcreate.appendChild(domHtml);\n\t\t}\n\n\t\t// excute inline JavaScript\n\t\tif (Mobilebone.evalScript == true && domHtml.firstintoBind != true) {\n\t\t\tslice.call(create.getElementsByTagName("script")).forEach(function(originScript) {\n\t\t\t\tvar scriptContent = originScript.innerHTML.trim();\n\t\t\t\tvar type = originScript.type || originScript.getAttribute("type");\n\n\t\t\t\tvar head = document.getElementsByTagName("head")[0] || document.documentElement;\n\t\t\t\tvar script = document.createElement("script");\n\t\t\t\tif (type) {\n\t\t\t\t\tscript.type = type;\n\t\t\t\t}\n\n\t\t\t\tif (scriptContent) {\n\t\t\t\t\tscript.appendChild(document.createTextNode(scriptContent));\n\t\t\t\t} else if (originScript.src && !document.querySelector(\'script[src="\'+ originScript.src +\'"]\')) {\n\t\t\t\t\t// issues #199\n                \tscript.src = originScript.src;\n            \t}\n\n\t\t\t\tsetTimeout(function() {\n\t\t\t\t\thead.insertBefore(script, head.firstChild);\n\t\t\t\t\thead.removeChild(script);\n\t\t\t\t\tscript = null;\n\t\t\t\t}, 17);\n\t\t\t\toriginScript = null;\n\t\t\t});\n\t\t}\n\n\t\tvar eleCreateTitle = create.getElementsByTagName("title")[0];\n\n\t\t// get the page element\n\t\tif (!(eleCreatePage = create.querySelector("." + classPage))) {\n\t\t\t// if there no .page, create as eleCreatePage\n\t\t\tcreate.className = classPage + " out";\n\t\t\teleCreatePage = create;\n\t\t}\n\t\t// set and store title\n\t\tif (typeof titlePage == "string") {\n\t\t\teleCreatePage.setAttribute("data-title", titlePage);\n\t\t} else if (eleCreateTitle && eleCreateTitle.innerText) { // the judge behind \'&&\' for issues #144\n\t\t\teleCreatePage.setAttribute("data-title", eleCreateTitle.innerText);\n\t\t}\n\n\t\t// do transition\n\t\toptionsTransition.response = response || domHtml;\n\t\toptionsTransition.id = options.id || this.getCleanUrl(eleOrObj) || eleCreatePage.id || options.url || ("unique" + Date.now());\n\n\t\t// \'if\' statement below added on v2.0.0\n\t\tif (typeof options == "object") {\n\t\t\tif (typeof options.history != "undefined") {\n\t\t\t\toptionsTransition.history = options.history;\n\t\t\t}\n\t\t\tif (typeof options.remove != "undefined") {\n\t\t\t\toptionsTransition.remove = options.remove;\n\t\t\t}\n\t\t\tif (typeof options.target != "undefined") {\n\t\t\t\toptionsTransition.target = options.target;\n\t\t\t}\n\t\t\tif (typeof options.title != "undefined") {\n\t\t\t\toptionsTransition.title = options.title;\n\t\t\t}\n\t\t\t// add on v2.7.4\n\t\t\tif (typeof options.query != "undefined") {\n\t\t\t\toptionsTransition.query = options.query;\n\t\t\t}\n\t\t}\n\t\tif (classPage == classPageInside) {\n\t\t\toptionsTransition.history = false;\n\t\t\toptionsTransition.classPage = classPage;\n\t\t}\n\n\t\t// append to a accurate position\n\t\tcontainer = container || document.body;\n\n\t\t// remove page if have same id when optionsTransition.remove == true\n\t\tif (optionsTransition.remove == true && optionsTransition.id) {\n\t\t\tthis.remove(optionsTransition.id);\n\t\t\t// v2.8.0 dom remove and ajax remove share same id\n\t\t\tif (options.id) {\n\t\t\t\tslice.call(document.querySelectorAll(\'a[data-reload="\'+ options.id +\'"]\')).forEach(function (ele) {\n\t\t\t\t\tMobilebone.remove(ele);\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\t// 1. if new page, that insert create page as a last-child\n\t\t// 2. if replace a page, that insert before replaced page\n\t\tvar pageid = optionsTransition.id.split("?")[0];\n\t\tif (pageid && store[pageid] && container.contains(store[pageid])) {\n\t\t\tcontainer.insertBefore(eleCreatePage, store[pageid]);\n\t\t} else {\n\t\t\tcontainer.appendChild(eleCreatePage);\n\t\t}\n\n\t\t// release memory\n\t\tcreate = null;\n\n\t\t// do transition\n\t\tthis.transition(eleCreatePage, eleCurrentPage, optionsTransition);\n\n\t\tvar objRelationShip = store.backShip || [];\n\t\tobjRelationShip.push({\n\t\t\tpageIn: eleCreatePage,\n\t\t\tpageOut: eleCurrentPage,\n\t\t\tisBack: !!optionsTransition.back\n\t\t});\n\n\t\tstore.backShip = objRelationShip;\n\t};\n\n\t/**\n\t * For ajax callback.\n\t * For example, data-success="a.b.c". We can\'t use \'a.b.c\' as a function, because it\'s a string. We should do some work to get it!\n\n\t * @params  keys:        string. - Necessary\n\t * @returns function\n\t            undefined keys is not string\n\t\t\t\twindow    keys undefined\n\t * @example Mobilebone.getFunction("a.b.c");\n\t *\n\t**/\n\tMobilebone.getFunction = function(keys) {\n\t\tif (typeof keys != "string") return;\n\t\t// eg. \'globalObject.functionName\'\n\t\tvar fun = Mobilebone.rootTransition, arrKey = keys.split(".");\n\t\tfor (var index=0; index<arrKey.length; index+=1) {\n\t\t\tif (!(fun = fun[arrKey[index]])) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn fun;\n\t};\n\n\t/**\n\t * For ajax request to get HTML or JSON.\n\n\t * @params  eleOrObj        - Necessary\n\t            1. dom-object:<a>|<form>.\n\t\t\t\t2. object.\n\t * @returns undefined\n\t * @example Mobilebone.ajax(document.querySelector("a"));\n\t            Mobilebone.ajax({\n\t\t\t\t  url: \'xxx.html\',\n\t\t\t\t  success: function() {}\n\t\t    \t});\n\t *\n\t**/\n\tMobilebone.ajax = function(eleOrObj) {\n\t\tif (!eleOrObj) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar trigger = eleOrObj;\n\n\t\t// default params\n\t\tvar defaults = {\n\t\t\turl: "",\n\t\t\ttype: "",\n\t\t\tdataType: "",\n\t\t\tdata: {},\n\t\t\ttimeout: 10000,\n\t\t\tsuccess: function() {},\n\t\t\terror: function() {},\n\t\t\tcomplete: function() {}\n\t\t};\n\n\t\tvar params = {}, eleMask = null, formData = null;\n\n\t\t// classname of mask\n\t\tvar classMask = this.classMask;\n\n\t\t// if \'eleOrObj\' is a element, we should turn it to options-object\n\t\tvar paramsFromTrigger = {}, attrMask;\n\t\tif (eleOrObj.nodeType == 1) {\n\t\t\tparamsFromTrigger = _queryToObject(trigger.getAttribute("data-params") || "");\n\t\t\t// get params\n\t\t\tfor (key in defaults) {\n\t\t\t\t// data-* > data-params > defaults\n\t\t\t\tparams[key] = trigger.getAttribute("data-" + key) || paramsFromTrigger[key] || defaults[key];\n\t\t\t\tif (typeof defaults[key] == "function" && typeof params[key] == "string") {\n\t\t\t\t\t// eg. globalObject.functionName\n\t\t\t\t\tparams[key] = this.getFunction(params[key]);\n\t\t\t\t\tif (typeof params[key] != "function") {\n\t\t\t\t\t\tparams[key] = defaults[key];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// address of ajax url\n\t\t\tvar cleanUrl = this.getCleanUrl(trigger, params.url);\n\t\t\tparams.url = cleanUrl;\n\n\t\t\tvar queryFromUrl = _queryToObject(cleanUrl.split("?")[1]);\n\n\t\t\t// v2.7.4 fix params may ingore problem\n\t\t\tfor (var key in queryFromUrl) {\n\t\t\t\tif (typeof paramsFromTrigger[key] == "undefined") {\n\t\t\t\t\tparamsFromTrigger[key] = queryFromUrl[key];\n\t\t\t\t}\n\t\t\t}\n\t\t\t// v2.7.4\n\t\t\tparams.query = paramsFromTrigger;\n\t\t\t// store target\n\t\t\tparams.target = trigger;\n\t\t\t// v2.5.2\n\t\t\t// is back? for issues #128\n\t\t\tparams.back = trigger.getAttribute("data-rel") == "back";\n\n\t\t\tvar tagName = trigger.tagName.toLowerCase();\n\t\t\tif (tagName == "form") {\n\t\t\t\tparams.type = trigger.method;\n\n\t\t\t\tformData = new FormData(trigger);\n\t\t\t} else if (tagName == "a") {\n\t\t\t\t// v2.5.8 for issues #157\n\t\t\t\tvar idContainer = trigger.getAttribute("data-container"),\n\t\t\t\t\tclassPageInside = trigger.getAttribute("data-classpage"),\n\t\t\t\t\tcontainer = idContainer && document.getElementById(idContainer);\n\t\t\t\tif (container && classPageInside && classPageInside != Mobilebone.classPage) {\n\t\t\t\t\t// inner ajax no history change\n\t\t\t\t\tparams.history = false;\n\t\t\t\t\t// title do not change\n\t\t\t\t\tparams.title = false;\n\t\t\t\t}\n\n\t\t\t\t// v2.8.0 move to here\n\t\t\t\tvar attrReload = trigger.getAttribute("data-reload");\n\t\t\t\tif (typeof attrReload == "string" && attrReload != "false") {\n\t\t\t\t\tif (attrReload != "" && attrReload != "true") {\n\t\t\t\t\t\t// v2.7.2 a new method to remove pafe\n\t\t\t\t\t\t// think \'attrReload\' as special ID\n\t\t\t\t\t\t// remove all page using this ID\n\t\t\t\t\t\tslice.call(document.querySelectorAll(\'a[data-reload="\'+ attrReload +\'"]\')).forEach(function (ele) {\n\t\t\t\t\t\t\tMobilebone.remove(ele);\n\t\t\t\t\t\t});\n\t\t\t\t\t\tMobilebone.remove(store[attrReload]);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// remove page\n\t\t\t\t\t\tMobilebone.remove(store[cleanUrl]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// get mask element\n\t\t\tattrMask = eleOrObj.getAttribute("data-mask");\n\t\t\tif (attrMask == "true" || attrMask == "") {\n\t\t\t\teleMask = eleOrObj.querySelector("." + classMask);\n\t\t\t}\n\t\t}\n\t\t// if \'eleOrObj\' is a object\n\t\telse if (eleOrObj.url) {\n\t\t\tparams = eleOrObj;\n\t\t\t// get params\n\t\t\tfor (key2 in defaults) {\n\t\t\t\tparams[key2] = eleOrObj[key2] || defaults[key2];\n\t\t\t}\n\t\t\t// get url\n\t\t\tparams.url = this.getCleanUrl(null, params.url, params.data);\n\t\t\t// here params.title will become page title;\n\t\t\tparams.title = eleOrObj.title;\n\t\t\t// v2.5.2\n\t\t\t// is back? for issues #128\n\t\t\t// when history.back()\n\t\t\tparams.back = eleOrObj.back;\n\t\t\t// v2.6.1\n\t\t\tparams.container = eleOrObj.container;\n\t\t\t// v2.7.4\n\t\t\tparams.query = _queryToObject(eleOrObj.url.split(\'?\')[1]);\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\n\t\t// do ajax\n\t\t// get mask and loading element\n\t\tvar body = container || document.body;\n\t\tif (typeof attrMask != "string") {\n\t\t\teleMask = [].slice.call(body.children).filter(function (element) {\n\t\t\t\treturn element.classList.contains(classMask);\n\t\t\t})[0];\n\t\t}\n\n\t\tif (eleMask == null) {\n\t\t\teleMask = document.createElement("div");\n\t\t\teleMask.className = classMask;\n\t\t\teleMask.innerHTML = \'<s class="loading"></s>\';\n\t\t\tif (typeof attrMask == "string") {\n\t\t\t\teleOrObj.appendChild(eleMask);\n\t\t\t} else {\n\t\t\t\tbody.appendChild(eleMask);\n\t\t\t}\n\t\t}\n\t\t// show loading\n\t\teleMask.style.display = "inline";\n\t\tif (this.showLoading) {\n\t\t\tthis.showLoading();\n\t\t}\n\n\t\t// ajax request\n\t\tvar xhr = new XMLHttpRequest();\n\t\txhr.open(params.type || "GET", params.url + (/\\?/.test(params.url)? "&" : "?") + "r=" + Date.now());\n\t\txhr.timeout = params.timeout;\n\n\t\txhr.onload = function() {\n\t\t\t// so far, many browser hasn\'t supported responseType = \'json\', so, use JSON.parse instead\n\t\t\tvar response = null;\n\n\t\t\tif (params.dataType == "json" || params.dataType == "JSON") {\n\t\t\t\ttry {\n\t\t\t\t\tresponse = JSON.parse(xhr.response);\n\t\t\t\t\tparams.response = response;\n\t\t\t\t\tMobilebone.createPage(Mobilebone.jsonHandle(response, params), eleOrObj, params);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tparams.message = "JSON parse error：" + e.message;\n\t\t\t\t\tparams.error.call(params, xhr, xhr.status);\n\t\t\t\t}\n\t\t\t} else if (params.dataType == "unknown") {\n\t\t\t\t// ajax send by url\n\t\t\t\t// no history hush\n\t\t\t\tparams.history = false;\n\t\t\t\t// I don\'t remember why add \'params.remove = false\' here,\n\t\t\t\t// but it seems that this will cause issues #147\n\t\t\t\t// no element remove\n\t\t\t\t// del → v2.5.8 // params.remove = false;\n\t\t\t\ttry {\n\t\t\t\t\t// as json\n\t\t\t\t\tresponse = JSON.parse(xhr.response);\n\t\t\t\t\tparams.response = response;\n\t\t\t\t\tMobilebone.createPage(Mobilebone.jsonHandle(response, params), eleOrObj, params);\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// as html\n\t\t\t\t\tresponse = xhr.response;\n\t\t\t\t\tMobilebone.createPage(response, eleOrObj, params);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresponse = xhr.response;\n\t\t\t\t// \'response\' is string\n\t\t\t\tMobilebone.createPage(response, eleOrObj, params);\n\t\t\t}\n\t\t\tparams.success.call(params, response, xhr.status);\n\t\t};\n\n\t\txhr.onerror = function(e) {\n\t\t\tparams.message = "Illegal request address or an unexpected network error!";\n\t\t\tparams.error.call(params, xhr, xhr.status);\n\t\t};\n\n\t\txhr.onloadend = function () {\n\t\t\t// hide loading\n\t\t\teleMask.style.display = "none";\n\t\t\tif (this.hideLoading) {\n\t\t\t\tthis.hideLoading();\n\t\t\t}\n\n\t\t\tparams.complete.call(params, xhr, xhr.status);\n\t\t};\n\n\t\txhr.ontimeout = function() {\n\t\t\tparams.message = "The request timeout!";\n\t\t\tparams.error.call(params, xhr, xhr.status);\n\t\t\t// hide loading\n\t\t\teleMask.style.display = "none";\n\t\t\tif (this.hideLoading) {\n\t\t\t\tthis.hideLoading();\n\t\t\t}\n\t\t};\n\n\t\t// set request header for server\n\t\txhr.setRequestHeader("Type", "ajax");\n\t\txhr.setRequestHeader("From", "mobilebone");\n\n\t\txhr.send(formData);\n\t};\n\n\t/**\n\t * capture form submit events to a ajax request.\n\n\t * @params  form:        formElement. - Necessary\n\t * @example Mobilebone.form(document.querySelector("form"));\n\t *\n\t**/\n\tMobilebone.submit = function(form) {\n\t\tif (!form || typeof form.action != "string") {\n\t\t\treturn;\n\t\t}\n\t\tvar ajax = form.getAttribute("data-ajax");\n\t\tif (ajax == "false" || (Mobilebone.captureForm == false && ajax != "true")) return;\n\n\t\tform.addEventListener("submit", function(event) {\n\t\t\t// prevent detect\n\t\t\tvar attrPrevent = this.getAttribute("data-preventdefault");\n\t\t\t// get \'preventDefault\' function\n\t\t\tvar funPrevent = Mobilebone.getFunction(attrPrevent);\n\t\t\tif (typeof funPrevent == "function" && funPrevent(this) == true) {\n\t\t\t\t// if the return value of prevent function is true, prevent everything~\n\t\t\t\tevent.preventDefault();\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tMobilebone.ajax(this);\n\t\t\tevent.preventDefault();\n\t\t});\n\t};\n\n\n\t/**\n\t * Sometime we don\'t know direction of transition. Such as browser history change, or data-rel="auto".\n\t   In this case, we ensure the direction(back or prev) by the sorts of two pages(into or out)\n\n\t * @params  pageIn  dom-object      - Necessary\n\t            pageOut  dom-object      - Optional\n\n\t * @returns boolean\n\t *\n\t**/\n\tMobilebone.isBack = function(pageIn, pageOut) {\n\t\t// back or forwards, according to the order of two pages\n\t\tif (history.tempBack == true) {\n\t\t\t// backwords\n\t\t\thistory.tempBack = null;\n\t\t\treturn true;\n\t\t}\n\n\t\tif (history.tempGo == true) {\n\t\t\t// forwards\n\t\t\thistory.tempGo = null;\n\t\t\treturn false;\n\t\t}\n\t\t// 2.7.5 return true -> false\n\t\tif (typeof pageIn == "undefined") {\n\t\t\treturn false;\n\t\t}\n\t\tif (!pageOut) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// 2.7.5 store pageIn pageOut backward or forward\n\t\tvar objRelationShip = store.backShip || [];\n\t\tvar isBack = null;\n\t\tobjRelationShip.forEach(function (ship) {\n\t\t\tif (ship.pageIn == pageIn && ship.pageOut == pageOut) {\n\t\t\t\tisBack = ship.isBack;\n\t\t\t} else if (ship.pageIn == pageOut && ship.pageOut == pageIn) {\n\t\t\t\tisBack = !ship.isBack;\n\t\t\t}\n\t\t});\n\n\t\tif (isBack === null) {\n\t\t\tisBack = !!(pageIn.compareDocumentPosition(pageOut) & Node.DOCUMENT_POSITION_FOLLOWING);\n\t\t}\n\n\t\treturn isBack;\n\t};\n\n\t/**\n\t * If dataType of ajax is \'json\', we can\'t convert json-data to page-element.\n\t   So, we export a function names \'jsonHandle\' to handle json-data.\n\t * Attention, it\'s a global interface. If your project has many json call, you should use JSON itself to make a distinction.\n\t   For example, every JSON include the only json-id:\n\t   {\n\t\t  "id": "homePage" ,\n\t\t  "data": []\n\t   }\n\t   different with\n\t   {\n\t\t  "id": "listPage" ,\n\t\t  "data": []\n\t   }\n\t *\n\t * @params  json    - Necessary\n\t * @returns dom-object|string\n\t *\n\t**/\n\tMobilebone.jsonHandle = function(json, params) {\n\t\treturn \'<p style="text-align:center;">Dear master, if you see me, show that JSON parsing function is undefined!</p>\';\n\t},\n\n\t/**\n\t * Initialization. Load page according to location.hash. And bind link-catch events.\n\t**/\n\tMobilebone.init = function() {\n\t\tif (hasInited == true) return \'Don\\\'t repeat initialization!\';\n\n\t\tvar hash = location.hash.replace("#&", "#"), eleIn = null, container = null;\n\t\t// 查询和根处理\n\t\tvar key = hash.split("?")[0];\n\t\tvar query = hash.split("?")[1];\n\n\t\tvar options = {\n\t    \tquery: {}\n\t    };\n\t    if (query) {\n\t    \toptions.query = _queryToObject(query);\n\t    }\n\n\t\tif (key == "" || key == "#") {\n\t\t\tthis.transition(document.querySelector("." + this.classPage));\n\t\t} else if (isSimple.test(key) == true && (eleIn = document.querySelector(key)) && eleIn.classList.contains(this.classPage)) {\n\t\t    // add on v2.7.4 pure inner hash also support query params\n\t\t    // eg. #somePageId?id=1&type=2\n\t\t    options.id = hash.replace(/^#/, "");\n\n\t\t\tthis.transition(eleIn, null, options);\n\t\t} else {\n\t\t\t// add on v2.6.1\n\t\t\tif (hash.split("container=").length == 2) {\n\t\t\t\tcontainer = document.getElementById(hash.split("container=")[1].split("&")[0]);\n\t\t\t}\n\t\t\t// as a ajax\n\t\t\tthis.ajax({\n\t\t\t\turl: hash.replace("#", ""),\n\t\t\t\tdataType: "unknown",\n\t\t\t\tcontainer: container,\n\t\t\t\terror: function() {\n\t\t\t\t\t// add on v2.7.4\n\t\t\t\t\toptions.id = hash.replace(/^#/, "");\n\n\t\t\t\t\teleIn = document.querySelector("." + Mobilebone.classPage);\n\t\t\t\t\tMobilebone.transition(eleIn, null, options);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t// Initialization link-catch events.\n\t\tdocument.addEventListener("click", this.handleTapEvent);\n\n\t\t// Important:\n\t\t// In ios7+, swipe the edge of page will navigate Safari\n\t\t// that will trigger \'popstate\' events and the page will transition twice\n\t\tvar isSafari7 = !!navigator.userAgent.match(/safari/i) && !navigator.userAgent.match(/chrome/i) && typeof document.hidden !== "undefined" && !window.chrome;\n\t\tif (\'ontouchstart\' in window == true && isSafari7) {\n\t\t\tdocument.addEventListener("touchmove", function() {\n\t\t\t\thistory.popstateswipe = true;\n\t\t\t});\n\t\t\tdocument.addEventListener("touchend", function() {\n\t\t\t\thistory.popstateswipe = false;\n\t\t\t});\n\t\t}\n\n\t\t// change flag-var for avoiding repeat init\n\t\thasInited = true;\n\t};\n\n\t/**\n\t * If \'a\' element has href, slide auto when clicked\n\t**/\n\tMobilebone.handleTapEvent = function(event) {\n\t\tvar target = null;\n\t\t// you can pass target as params directly\n\t\tif (event && event.nodeType == 1) {\n\t\t\ttarget = event;\n\t\t\ttarget.preventDefault = function() {};\n\t\t}\n\t\t// get target and href\n\t\ttarget = (target || event.target) && (target || event.target).closest(\'a\');\n\n\t\tif (!target) {\n\t\t\treturn;\n\t\t}\n\n\t\t// current href\n\t\tvar href = target.href;\n\n\t\t// the page that current actived\n\t\tvar selfPage = document.querySelector(".in." + Mobilebone.classPage);\n\n\t\tif (selfPage == null) {\n\t\t\treturn;\n\t\t}\n\n\t\t// optional params for Mobilebone.transition\n\t\tvar options = {\n\t\t\ttarget: target\n\t\t};\n\n\t\t// prevent detect\n\t\tvar attrPrevent = target.getAttribute("data-preventdefault") || _queryToObject(target.getAttribute("data-params") || "").preventdefault;\n\t\t// get \'preventDefault\' function\n\t\tvar funPrevent = Mobilebone.getFunction(attrPrevent);\n\t\tif (typeof funPrevent == "function" && funPrevent(target) == true) {\n\t\t\t// if the return value of prevent function is true, prevent everything~\n\t\t\tevent.preventDefault();\n\t\t\treturn false;\n\t\t}\n\n\t\t// if mask element exist and displaying, prevent double trigger\n\t\tvar eleMask = target.getElementsByClassName(Mobilebone.classMask)[0];\n\t\tif (eleMask && eleMask.style.display != "none") {\n\t\t\tevent.preventDefault();\n\t\t\treturn false;\n\t\t}\n\n\t\tvar idContainer = target.getAttribute("data-container");\n\t\tvar classPageInside = target.getAttribute("data-classpage");\n\n\t\tvar container = idContainer && document.getElementById(idContainer);\n\t\tif (container && classPageInside && classPageInside != Mobilebone.classPage) {\n\t\t\tselfPage = container.querySelector(".in." + classPageInside) || container.querySelector(classPageInside);\n\t\t\t// if (selfPage == null) return false;\n\t\t\toptions.history = false;\n\t\t\toptions.title = false;\n\t\t\toptions.classPage = classPageInside;\n\t\t}\n\n\t\t// history\n\t\tif (target.getAttribute("data-history") == "false") {\n\t\t\toptions.history = false;\n\t\t}\n\n\t\t// if captureLink\n\t\tvar capture = (Mobilebone.captureLink == true);\n\t\t// get rel\n\t\tvar rel = target.getAttribute("data-rel");\n\t\tif (!rel) {\n\t\t\trel = \'auto\';\n\t\t}\n\t\t// if back\n\t\tvar back = false;\n\t\tif (rel == "back") {\n\t\t\tback = true;\n\t\t}\n\t\t// if go\n\t\tvar go;\n\t\tif (rel == "go") {\n\t\t\tgo = true;\n\t\t}\n\n\t\t// if external link\n\t\tvar external = (rel == "external");\n\n\t\t// if the \'href\' is not legal, return\n\t\t// include:\n\t\t// 1. undefined\n\t\t// 2. javascript: (except data-rel="back")\n\t\t// 3. cros, or not capture (except data-ajax="true")\n\t\tif (!href) {\n\t\t\treturn;\n\t\t}\n\n\t\t// 直接获取属性设置的值\n\t\tvar attrHref = target.getAttribute("href");\n\n\t\thref = href.replace("#&", "#");\n\n\t\tif (attrHref.replace(/#/g, "") === "") {\n\t\t\tevent.preventDefault();\n\t\t\treturn;\n\t\t}\n\t\tif (/^javascript/.test(href)) {\n\t\t\tif (back == false && !go) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\texternal = external || (href.replace("://", "").split("/")[0] !== location.href.replace("://", "").split("/")[0]);\n\t\t\tif ((external == true || capture == false) && target.getAttribute("data-ajax") != "true") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// judge that if it\'s a ajax request\n\t\tif (/^#/.test(attrHref) == true) {\n\t\t\tevent.preventDefault();\n\t\t\t// hash slide\n\t\t\tvar hashTargetPage = href.split("#")[1];\n\t\t\tvar idTargetPage = hashTargetPage.split("?")[0];\n\t\t\tvar queryTargetPage = hashTargetPage.split("?")[1];\n\t\t\tvar paramTargetPage = target.getAttribute(\'data-formdata\') || target.getAttribute(\'data-params\');\n\t\t\tif (paramTargetPage) {\n\t\t\t\tqueryTargetPage = queryTargetPage + \'&\' + paramTargetPage;\n\t\t\t}\n\t\t\t\n\t\t\t// add on v2.7.4\n\t\t\toptions.query = _queryToObject(queryTargetPage);\n\t\t\toptions.id = hashTargetPage;\n\n\t\t\tvar eleTargetPage = idTargetPage && document.getElementById(idTargetPage);\n\t\t\tif (back == false && rel == "auto") {\n\t\t\t\tback = Mobilebone.isBack(eleTargetPage, selfPage);\n\t\t\t}\n\n\t\t\tif (eleTargetPage) {\n\t\t\t\tvar lastShip = store.lastShip;\n\n\t\t\t\tif (lastShip && eleTargetPage == lastShip[1] && selfPage == lastShip[0] && !go) {\n\t\t\t\t\t// back\n\t\t\t\t\thistory.tempBack = true;\n\t\t\t\t\thistory.back();\n\t\t\t\t} else {\n\t\t\t\t\tMobilebone.transition(eleTargetPage, selfPage, back, options);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (/^javascript/.test(href)) {\n\t\t\t// back\n\t\t\tif (back) {\n\t\t\t\thistory.tempBack = true;\n\t\t\t\thistory.back();\n\t\t\t} else if (go) {\n\t\t\t\thistory.tempGo = true;\n\t\t\t\thistory.go(1);\n\t\t\t}\t\t\t\n\t\t} else if (target.getAttribute("data-ajax") != "false") {\n\t\t\tevent.preventDefault();\n\t\t\t// get a clean ajax url as page id\n\t\t\tvar cleanUrl = Mobilebone.getCleanUrl(target);\n\t\t\t// add on v2.7.4\n\t\t\toptions.query = _queryToObject(cleanUrl.split("?")[1]);\n\t\t\t// if has loaded and the value of \'data-reload\' is not \'true\'\n\t\t\tvar attrReload = target.getAttribute("data-reload");\n\n\t\t\t// 之前已经请求过，内存中已经有，则直接使用\n\t\t\tif ((attrReload == null || attrReload == "false") && store[cleanUrl]) {\n\t\t\t\tif (back == false && rel == "auto") {\n\t\t\t\t\tback = Mobilebone.isBack(store[cleanUrl], selfPage);\n\t\t\t\t}\n\t\t\t\toptions.id = cleanUrl;\n\n\t\t\t\tvar body = container || document.body;\n\n\t\t\t\tif (body.contains(store[cleanUrl]) == false) {\n\t\t\t\t\tbody.appendChild(store[cleanUrl]);\n\t\t\t\t}\n\t\t\t\tMobilebone.transition(store[cleanUrl], selfPage, back, options);\n\t\t\t} else {\t\n\t\t\t\t// as ajax\n\t\t\t\tMobilebone.ajax(target);\n\t\t\t}\n\t\t\t\n\t\t}\n\t};\n\n\t/**\n\t * closest polyfill\n\t */\n\tif (!Element.prototype.matches) {\n\t\tElement.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;\n\t}\n\n\tif (!Element.prototype.closest) {\n\t\tElement.prototype.closest = function (s) {\n\t\t\tvar el = this;\n\n\t\t\tdo {\n\t\t\t\tif (el.matches(s)) return el;\n\t\t\t\tel = el.parentElement || el.parentNode;\n\t\t\t} while (el !== null && el.nodeType === 1);\n\n\t\t\treturn null;\n\t\t};\n\t}\n\n\n\t/**\n\t * private method: convert query string to key-value object\n\t**/\n\tvar _queryToObject = function(string) {\n\t\tvar obj = {};\n\t\tif (typeof string == "string") {\n\t\t\tstring.split("&").forEach(function(part) {\n\t\t\t\tvar arrPart = part.split("=");\n\t\t\t\tif (arrPart.length > 1) {\n\t\t\t\t\tobj[arrPart[0]] = part.replace(arrPart[0] + "=", "");\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\treturn obj;\n\t};\n\n\t/**\n\t * auto init\n\t**/\n\twindow.addEventListener("DOMContentLoaded", function() {\n\t\tif (hasInited == false && Mobilebone.autoInit == true) {\n\t\t\tMobilebone.init();\n\t\t}\n\t});\n\n\t/**\n\t * page change when history change\n\t**/\n\twindow.addEventListener("popstate", function() {\n\t\tif (history.popstateswipe == true) {\n\t\t\tlocation.reload();\n\t\t\thistory.popstateswipe = false;\n\t\t\treturn;\n\t\t}\n\t\tif (history.popstate == false) {\n\t\t\thistory.popstate = true;\n\t\t\treturn;\n\t\t}\n\n\t\tvar hash = location.hash.replace("#&", "#").replace(/^#/, "");\n\t\t// add on v2.7.4\n\t\tvar key = hash.split(\'?\')[0];\n\t\tvar pageIn = null;\n\t\t// add on v2.7.5\n\t\tvar pageOut = document.querySelector(".in." + Mobilebone.classPage);\n\t\t// add on v2.6.1\n\t\tvar container = null;\n\n\t\tif (hash == "") {\n\t\t\t// if no hash, get first page as \'pageIn\'\n\t\t\tpageIn = document.querySelector("." + Mobilebone.classPage);\n\t\t\tif (pageIn.id) return;\n\t\t} else {\n\t\t\t// add on v2.7.4\n\t\t\tpageIn = store[key] || store[hash];\n\n\t\t\t// add on v2.6.1\n\t\t\tif (hash.split("container=").length == 2) {\n\t\t\t\tcontainer = document.getElementById(hash.split("container=")[1].split("&")[0]);\n\t\t\t}\n\n\t\t\t// url address and with cache\n\t\t\tif (pageIn && isSimple.test(key) == false) {\n\t\t\t\t// just transition\n\t\t\t\tMobilebone.transition(pageIn, pageOut, Mobilebone.isBack(pageIn, pageOut), {\n\t\t\t\t\tid: hash,  // fix issue #83\n\t\t\t\t\thistory: false,\n\t\t\t\t\tcontainer: container,\n\t\t\t\t\ttarget: document.activeElement,  // add on v2.7.7\n\t\t\t\t\tquery: _queryToObject(hash.split(\'?\')[1])\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (!pageIn) {\n\t\t\tif (isSimple.test(hash) == false) {\n\t\t\t\t// as a url\n\t\t\t\tMobilebone.ajax({\n\t\t\t\t\turl: hash,\n\t\t\t\t\tdataType: "unknown",\n\t\t\t\t\t// no cache url, usually reload ajax\n\t\t\t\t\tback: Mobilebone.isBack(),\n\t\t\t\t\tcontainer: container\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpageIn = document.querySelector("#" + key) || document.querySelector("#" + hash);\n\t\t}\n\n\t\tif ((pageIn && pageIn == pageOut) || Mobilebone.pushStateEnabled == false) return;\n\n\t\t// hash ↔ id\n\t\tif (pageIn) {\n\t\t\tMobilebone.transition(pageIn, pageOut, Mobilebone.isBack(pageIn, pageOut), {\n\t\t\t\tid: hash,  // fix issue #83\n\t\t\t\thistory: false,\n\t\t\t\ttarget: document.activeElement,  // add on v2.7.7\n\t\t\t\tquery: _queryToObject(hash.split(\'?\')[1])\n\t\t\t});\n\t\t}\n\t});\n\n\tdocument.MBLOADED = true;\n\n\treturn Mobilebone;\n});', 'message': "change version\n\nchange version to '1.0.0'\n", 'author_name': 'zhangxinxu', 'lang': 'js'}