js篇
// 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;
/**
* For ajax request to get HTML or JSON.
* @params aOrFormOrObj - 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(aOrFormOrObj) {
if (!aOrFormOrObj) return;
// default params
var defaults = {
url: "",
type: "",
dataType: "",
data: {},
timeout: 10000,
async: true,
username: "",
password: "",
success: function() {},
error: function() {},
complete: function() {}
};
var params = {}, eleMask = null, formData = null;
// classname of mask
var classMask = this.classMask;
// if 'aOrFormOrObj' is a element, we should turn it to options-object
var paramsFromTrigger = {}, attrMask;
if (aOrFormOrObj.nodeType == 1) {
paramsFromTrigger = _queryToObject(aOrFormOrObj.getAttribute("data-params") || "");
// get params
for (key in defaults) {
// data-* > data-params > defaults
params[key] = aOrFormOrObj.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
params.url = this.getCleanUrl(aOrFormOrObj, params.url);
var queryFromUrl = _queryToObject(params.url.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 = aOrFormOrObj;
// v2.5.2
// is back? for issues #128
params.back = aOrFormOrObj.getAttribute("data-rel") == "back";
var tagName = aOrFormOrObj.tagName.toLowerCase();
if (tagName == "form") {
params.type = aOrFormOrObj.method;
formData = new FormData(aOrFormOrObj);
} else if (tagName == "a") {
// v2.5.8 for issues #157
var idContainer = aOrFormOrObj.getAttribute("data-container"),
classPageInside = aOrFormOrObj.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;
}
}
// get mask element
attrMask = aOrFormOrObj.getAttribute("data-mask");
if (attrMask == "true" || attrMask == "") {
eleMask = aOrFormOrObj.querySelector("." + classMask);
}
}
// if 'aOrFormOrObj' is a object
else if (aOrFormOrObj.url) {
// get params
for (key2 in defaults) {
params[key2] = aOrFormOrObj[key2] || defaults[key2];
}
// get url
params.url = this.getCleanUrl(null, params.url, params.data);
// here params.title will become page title;
params.title = aOrFormOrObj.title;
// v2.5.2
// is back? for issues #128
// when history.back()
params.back = aOrFormOrObj.back;
// v2.6.1
params.container = aOrFormOrObj.container;
// v2.7.4
params.query = _queryToObject(aOrFormOrObj.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 = '<i class="loading"></i>';
if (typeof attrMask == "string") {
aOrFormOrObj.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(), params.async, params.username, params.password);
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 (xhr.status == 200) {
if (params.dataType == "json" || params.dataType == "JSON") {
try {
response = JSON.parse(xhr.response);
params.response = response;
Mobilebone.createPage(Mobilebone.jsonHandle(response, params), aOrFormOrObj, 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), aOrFormOrObj, params);
} catch (e) {
// as html
response = xhr.response;
Mobilebone.createPage(response, aOrFormOrObj, params);
}
} else {
response = xhr.response;
// 'response' is string
Mobilebone.createPage(response, aOrFormOrObj, params);
}
params.success.call(params, response, xhr.status);
} else {
params.message = "The status code exception!";
params.error.call(params, xhr, xhr.status);
}
params.complete.call(params, xhr, xhr.status);
// hide loading
eleMask.style.display = "none";
if (this.hideLoading) {
this.hideLoading();
}
}
xhr.onerror = function(e) {
params.message = "Illegal request address or an unexpected network error!";
params.error.call(params, xhr, xhr.status);
// hide loading
eleMask.style.display = "none";
if (this.hideLoading) {
this.hideLoading();
}
}
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);
};
/**
* 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;
};
css篇
.loading { /* more info: [http://www.zhangxinxu.com/wordpress/?p=3357](http://www.zhangxinxu.com/wordpress/?p=3357) */
width: 3px; height:3px;
border-radius: 100%;
box-shadow: 0 -10px 0 1px currentColor, /* top, 1px expand */
10px 0px currentColor, /* right */
0 10px currentColor, /* bottom */
-10px 0 currentColor, /* left */
-7px -7px 0 .5px currentColor, /* left-top, 0.5px expand */
7px -7px 0 1.5px currentColor, /* right-top, 1.5px expand */
7px 7px currentColor, /* right-bottom */
-7px 7px currentColor; /* left-bottom */
-webkit-animation: spin 1s steps(8) infinite;
animation: spin 1s steps(8) infinite;
/*center*/
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
margin: auto;
}
/* chrysanthemum loading effect */
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}