
2 changed files with 712 additions and 69 deletions
-
535public/ajax.js
-
246public/index.html
@ -0,0 +1,535 @@ |
|||
// a simple ajax
|
|||
!(function () { |
|||
|
|||
var jsonType = 'application/json'; |
|||
var htmlType = 'text/html'; |
|||
var xmlTypeRE = /^(?:text|application)\/xml/i; |
|||
var blankRE = /^\s*$/; // \s
|
|||
|
|||
/* |
|||
* default setting |
|||
* */ |
|||
var _settings = { |
|||
|
|||
type: "GET", |
|||
|
|||
beforeSend: noop, |
|||
|
|||
success: noop, |
|||
|
|||
error: noop, |
|||
|
|||
complete: noop, |
|||
|
|||
context: null, |
|||
|
|||
xhr: function () { |
|||
return new window.XMLHttpRequest(); |
|||
}, |
|||
|
|||
accepts: { |
|||
json: jsonType, |
|||
xml: 'application/xml, text/xml', |
|||
html: htmlType, |
|||
text: 'text/plain' |
|||
}, |
|||
|
|||
crossDomain: false, |
|||
|
|||
timeout: 0, |
|||
|
|||
username: null, |
|||
|
|||
password: null, |
|||
|
|||
processData: true, |
|||
|
|||
promise: noop |
|||
}; |
|||
|
|||
function noop() { |
|||
} |
|||
|
|||
var ajax = function (options) { |
|||
|
|||
//
|
|||
var settings = extend({}, options || {}); |
|||
|
|||
//
|
|||
for (var key in _settings) { |
|||
if (settings[key] === undefined) { |
|||
settings[key] = _settings[key]; |
|||
} |
|||
} |
|||
|
|||
//
|
|||
try { |
|||
var q = {}; |
|||
var promise = new Promise(function (resolve, reject) { |
|||
q.resolve = resolve; |
|||
q.reject = reject; |
|||
}); |
|||
|
|||
promise.resolve = q.resolve; |
|||
promise.reject = q.reject; |
|||
|
|||
settings.promise = promise; |
|||
} |
|||
catch (e) { |
|||
//
|
|||
settings.promise = { |
|||
resolve: noop, |
|||
reject: noop |
|||
}; |
|||
} |
|||
|
|||
|
|||
//
|
|||
if (!settings.crossDomain) { |
|||
settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) && RegExp.$2 !== window.location.href; |
|||
} |
|||
|
|||
var dataType = settings.dataType; |
|||
// jsonp
|
|||
if (dataType === 'jsonp') { |
|||
//
|
|||
var hasPlaceholder = /=\?/.test(settings.url); |
|||
if (!hasPlaceholder) { |
|||
var jsonpCallback = (settings.jsonp || 'callback') + '=?'; |
|||
|
|||
settings.url = appendQuery(settings.url, jsonpCallback) |
|||
} |
|||
return JSONP(settings); |
|||
} |
|||
|
|||
// url
|
|||
if (!settings.url) { |
|||
settings.url = window.location.toString(); |
|||
} |
|||
|
|||
//
|
|||
serializeData(settings); |
|||
|
|||
var mime = settings.accepts[dataType]; // mime
|
|||
var baseHeader = {}; // header
|
|||
var protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol; // protocol
|
|||
var xhr = _settings.xhr(); |
|||
var abortTimeout; |
|||
|
|||
// X-Requested-With header
|
|||
// For cross-domain requests, seeing as conditions for a preflight are
|
|||
// akin to a jigsaw puzzle, we simply never set it to be sure.
|
|||
// (it can always be set on a per-request basis or even using ajaxSetup)
|
|||
// For same-domain requests, won't change header if already provided.
|
|||
if (!settings.crossDomain && !baseHeader['X-Requested-With']) { |
|||
baseHeader['X-Requested-With'] = 'XMLHttpRequest'; |
|||
} |
|||
|
|||
// mime
|
|||
if (mime) { |
|||
//
|
|||
baseHeader['Accept'] = mime; |
|||
|
|||
if (mime.indexOf(',') > -1) { |
|||
mime = mime.split(',', 2)[0] |
|||
} |
|||
//
|
|||
xhr.overrideMimeType && xhr.overrideMimeType(mime); |
|||
} |
|||
|
|||
// contentType
|
|||
if (settings.contentType || (settings.data && settings.type.toUpperCase() !== 'GET')) { |
|||
baseHeader['Content-Type'] = (settings.contentType || 'application/x-www-form-urlencoded; charset=UTF-8'); |
|||
} |
|||
|
|||
// headers
|
|||
settings.headers = extend(baseHeader, settings.headers || {}); |
|||
|
|||
// on ready state change
|
|||
xhr.onreadystatechange = function () { |
|||
// readystate
|
|||
if (xhr.readyState === 4) { |
|||
clearTimeout(abortTimeout); |
|||
var result; |
|||
var error = false; |
|||
//
|
|||
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { |
|||
dataType = dataType || mimeToDataType(xhr.getResponseHeader('content-type')); |
|||
result = xhr.responseText; |
|||
|
|||
try { |
|||
// xml
|
|||
if (dataType === 'xml') { |
|||
result = xhr.responseXML; |
|||
} |
|||
// json
|
|||
else if (dataType === 'json') { |
|||
result = blankRE.test(result) ? null : JSON.parse(result); |
|||
} |
|||
} |
|||
catch (e) { |
|||
error = e; |
|||
} |
|||
|
|||
if (error) { |
|||
ajaxError(error, 'parseerror', xhr, settings); |
|||
} |
|||
else { |
|||
ajaxSuccess(result, xhr, settings); |
|||
} |
|||
} |
|||
else { |
|||
ajaxError(null, 'error', xhr, settings); |
|||
} |
|||
|
|||
} |
|||
}; |
|||
|
|||
// async
|
|||
var async = 'async' in settings ? settings.async : true; |
|||
|
|||
// open
|
|||
xhr.open(settings.type, settings.url, async, settings.username, settings.password); |
|||
|
|||
// xhrFields
|
|||
if (settings.xhrFields) { |
|||
for (var name in settings.xhrFields) { |
|||
xhr[name] = settings.xhrFields[name]; |
|||
} |
|||
} |
|||
|
|||
// Override mime type if needed
|
|||
if (settings.mimeType && xhr.overrideMimeType) { |
|||
xhr.overrideMimeType(settings.mimeType); |
|||
} |
|||
|
|||
|
|||
// set request header
|
|||
for (var name in settings.headers) { |
|||
// Support: IE<9
|
|||
// IE's ActiveXObject throws a 'Type Mismatch' exception when setting
|
|||
// request header to a null-value.
|
|||
//
|
|||
// To keep consistent with other XHR implementations, cast the value
|
|||
// to string and ignore `undefined`.
|
|||
if (settings.headers[name] !== undefined) { |
|||
xhr.setRequestHeader(name, settings.headers[name] + ""); |
|||
} |
|||
} |
|||
|
|||
// before send
|
|||
if (ajaxBeforeSend(xhr, settings) === false) { |
|||
xhr.abort(); |
|||
return false; |
|||
} |
|||
|
|||
// timeout
|
|||
if (settings.timeout > 0) { |
|||
abortTimeout = window.setTimeout(function () { |
|||
xhr.onreadystatechange = noop; |
|||
xhr.abort(); |
|||
ajaxError(null, 'timeout', xhr, settings); |
|||
}, settings.timeout); |
|||
} |
|||
|
|||
// send
|
|||
xhr.send(settings.data ? settings.data : null); |
|||
|
|||
return settings.promise; |
|||
}; |
|||
|
|||
/* |
|||
* method get |
|||
* */ |
|||
ajax.get = function (url, data, success, dataType) { |
|||
if (isFunction(data)) { |
|||
dataType = dataType || success; |
|||
success = data; |
|||
data = undefined; |
|||
} |
|||
|
|||
return ajax({ |
|||
url: url, |
|||
data: data, |
|||
success: success, |
|||
dataType: dataType |
|||
}); |
|||
}; |
|||
|
|||
/* |
|||
* method post |
|||
* |
|||
* dataType: |
|||
* */ |
|||
ajax.post = function (url, data, success, dataType) { |
|||
if (isFunction(data)) { |
|||
dataType = dataType || success; |
|||
success = data; |
|||
data = undefined; |
|||
} |
|||
return ajax({ |
|||
type: 'POST', |
|||
url: url, |
|||
data: data, |
|||
success: success, |
|||
dataType: dataType |
|||
}) |
|||
}; |
|||
|
|||
/* |
|||
* method getJSON |
|||
* */ |
|||
ajax.getJSON = function (url, data, success) { |
|||
|
|||
if (isFunction(data)) { |
|||
success = data; |
|||
data = undefined; |
|||
} |
|||
|
|||
return ajax({ |
|||
url: url, |
|||
data: data, |
|||
success: success, |
|||
dataType: 'json' |
|||
}) |
|||
}; |
|||
|
|||
/* |
|||
* method ajaxSetup |
|||
* */ |
|||
ajax.ajaxSetup = function (target, settings) { |
|||
return settings ? extend(extend(target, _settings), settings) : extend(_settings, target); |
|||
}; |
|||
|
|||
/* |
|||
* utils |
|||
* |
|||
* */ |
|||
|
|||
|
|||
// triggers and extra global event ajaxBeforeSend that's like ajaxSend but cancelable
|
|||
function ajaxBeforeSend(xhr, settings) { |
|||
var context = settings.context; |
|||
//
|
|||
if (settings.beforeSend.call(context, xhr, settings) === false) { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
// ajax success
|
|||
function ajaxSuccess(data, xhr, settings) { |
|||
var context = settings.context; |
|||
var status = 'success'; |
|||
settings.success.call(context, data, status, xhr); |
|||
settings.promise.resolve(data, status, xhr); |
|||
ajaxComplete(status, xhr, settings); |
|||
} |
|||
|
|||
// status: "success", "notmodified", "error", "timeout", "abort", "parsererror"
|
|||
function ajaxComplete(status, xhr, settings) { |
|||
var context = settings.context; |
|||
settings.complete.call(context, xhr, status); |
|||
} |
|||
|
|||
// type: "timeout", "error", "abort", "parsererror"
|
|||
function ajaxError(error, type, xhr, settings) { |
|||
var context = settings.context; |
|||
settings.error.call(context, xhr, type, error); |
|||
settings.promise.reject(xhr, type, error); |
|||
ajaxComplete(type, xhr, settings); |
|||
} |
|||
|
|||
|
|||
// jsonp
|
|||
/* |
|||
* tks: https://www.cnblogs.com/rubylouvre/archive/2011/02/13/1953087.html
|
|||
* */ |
|||
function JSONP(options) { |
|||
//
|
|||
var callbackName = options.jsonpCallback || 'jsonp' + (new Date().getTime()); |
|||
|
|||
var script = window.document.createElement('script'); |
|||
|
|||
var abort = function () { |
|||
// 设置 window.xxx = noop
|
|||
if (callbackName in window) { |
|||
window[callbackName] = noop; |
|||
} |
|||
}; |
|||
|
|||
var xhr = {abort: abort}; |
|||
var abortTimeout; |
|||
|
|||
var head = window.document.getElementsByTagName('head')[0] || window.document.documentElement; |
|||
|
|||
// ie8+
|
|||
script.onerror = function (error) { |
|||
_error(error); |
|||
}; |
|||
|
|||
function _error(error) { |
|||
window.clearTimeout(abortTimeout); |
|||
xhr.abort(); |
|||
ajaxError(error.type, xhr, error.type, options); |
|||
_removeScript(); |
|||
} |
|||
|
|||
window[callbackName] = function (data) { |
|||
window.clearTimeout(abortTimeout); |
|||
ajaxSuccess(data, xhr, options); |
|||
_removeScript(); |
|||
}; |
|||
|
|||
//
|
|||
serializeData(options); |
|||
|
|||
script.src = options.url.replace(/=\?/, '=' + callbackName); |
|||
//
|
|||
script.src = appendQuery(script.src, '_=' + (new Date()).getTime()); |
|||
//
|
|||
script.async = true; |
|||
|
|||
// script charset
|
|||
if (options.scriptCharset) { |
|||
script.charset = options.scriptCharset; |
|||
} |
|||
|
|||
//
|
|||
head.insertBefore(script, head.firstChild); |
|||
|
|||
//
|
|||
if (options.timeout > 0) { |
|||
abortTimeout = window.setTimeout(function () { |
|||
xhr.abort(); |
|||
ajaxError('timeout', xhr, 'timeout', options); |
|||
_removeScript(); |
|||
}, options.timeout); |
|||
} |
|||
|
|||
// remove script
|
|||
function _removeScript() { |
|||
if (script.clearAttributes) { |
|||
script.clearAttributes(); |
|||
} else { |
|||
script.onload = script.onreadystatechange = script.onerror = null; |
|||
} |
|||
|
|||
if (script.parentNode) { |
|||
script.parentNode.removeChild(script); |
|||
} |
|||
//
|
|||
script = null; |
|||
|
|||
delete window[callbackName]; |
|||
} |
|||
|
|||
return options.promise; |
|||
} |
|||
|
|||
// mime to data type
|
|||
function mimeToDataType(mime) { |
|||
return mime && (mime === htmlType ? 'html' : mime === jsonType ? 'json' : xmlTypeRE.test(mime) && 'xml') || 'text' |
|||
} |
|||
|
|||
// append query
|
|||
function appendQuery(url, query) { |
|||
return (url + '&' + query).replace(/[&?]{1,2}/, '?'); |
|||
} |
|||
|
|||
// serialize data
|
|||
function serializeData(options) { |
|||
// formData
|
|||
if (isObject(options) && !isFormData(options.data) && options.processData) { |
|||
options.data = param(options.data); |
|||
} |
|||
|
|||
if (options.data && (!options.type || options.type.toUpperCase() === 'GET')) { |
|||
options.url = appendQuery(options.url, options.data); |
|||
} |
|||
} |
|||
|
|||
// serialize
|
|||
function serialize(params, obj, traditional, scope) { |
|||
var _isArray = isArray(obj); |
|||
|
|||
for (var key in obj) { |
|||
var value = obj[key]; |
|||
|
|||
if (scope) { |
|||
key = traditional ? scope : scope + '[' + (_isArray ? '' : key) + ']'; |
|||
} |
|||
|
|||
// handle data in serializeArray format
|
|||
if (!scope && _isArray) { |
|||
params.add(value.name, value.value); |
|||
|
|||
} |
|||
else if (traditional ? _isArray(value) : isObject(value)) { |
|||
serialize(params, value, traditional, key); |
|||
} |
|||
else { |
|||
params.add(key, value); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
// param
|
|||
function param(obj, traditional) { |
|||
var params = []; |
|||
//
|
|||
params.add = function (k, v) { |
|||
this.push(encodeURIComponent(k) + '=' + encodeURIComponent(v)); |
|||
}; |
|||
serialize(params, obj, traditional); |
|||
return params.join('&').replace('%20', '+'); |
|||
} |
|||
|
|||
// extend
|
|||
function extend(target) { |
|||
var slice = Array.prototype.slice; |
|||
var args = slice.call(arguments, 1); |
|||
//
|
|||
for (var i = 0, length = args.length; i < length; i++) { |
|||
var source = args[i] || {}; |
|||
for (var key in source) { |
|||
if (source.hasOwnProperty(key) && source[key] !== undefined) { |
|||
target[key] = source[key]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return target; |
|||
} |
|||
|
|||
// is object
|
|||
function isObject(obj) { |
|||
var type = typeof obj; |
|||
return type === 'function' || type === 'object' && !!obj; |
|||
} |
|||
|
|||
// is formData
|
|||
function isFormData(obj) { |
|||
return obj instanceof FormData; |
|||
} |
|||
|
|||
// is array
|
|||
function isArray(value) { |
|||
return Object.prototype.toString.call(value) === "[object Array]"; |
|||
} |
|||
|
|||
// is function
|
|||
function isFunction(value) { |
|||
return typeof value === "function"; |
|||
} |
|||
|
|||
// browser
|
|||
window.ajax = ajax; |
|||
})(); |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue