(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
|
module.exports = {
|
|
oauthd_url: "https://oauth.io",
|
|
oauthd_api: "https://oauth.io/api",
|
|
version: "web-0.4.3",
|
|
options: {}
|
|
};
|
|
|
|
},{}],2:[function(require,module,exports){
|
|
"use strict";
|
|
module.exports = function(Materia) {
|
|
var $;
|
|
$ = Materia.getJquery();
|
|
return {
|
|
get: (function(_this) {
|
|
return function(url, params) {
|
|
var base;
|
|
base = Materia.getOAuthdURL();
|
|
return $.ajax({
|
|
url: base + url,
|
|
type: 'get',
|
|
data: params
|
|
});
|
|
};
|
|
})(this),
|
|
post: (function(_this) {
|
|
return function(url, params) {
|
|
var base;
|
|
base = Materia.getOAuthdURL();
|
|
return $.ajax({
|
|
url: base + url,
|
|
type: 'post',
|
|
data: params
|
|
});
|
|
};
|
|
})(this),
|
|
put: (function(_this) {
|
|
return function(url, params) {
|
|
var base;
|
|
base = Materia.getOAuthdURL();
|
|
return $.ajax({
|
|
url: base + url,
|
|
type: 'put',
|
|
data: params
|
|
});
|
|
};
|
|
})(this),
|
|
del: (function(_this) {
|
|
return function(url, params) {
|
|
var base;
|
|
base = Materia.getOAuthdURL();
|
|
return $.ajax({
|
|
url: base + url,
|
|
type: 'delete',
|
|
data: params
|
|
});
|
|
};
|
|
})(this)
|
|
};
|
|
};
|
|
|
|
},{}],3:[function(require,module,exports){
|
|
"use strict";
|
|
var Location, Url, cache, config, cookies;
|
|
|
|
config = require('../config');
|
|
|
|
Url = require("../tools/url");
|
|
|
|
Location = require('../tools/location_operations');
|
|
|
|
cookies = require("../tools/cookies");
|
|
|
|
cache = require("../tools/cache");
|
|
|
|
module.exports = function(window, document, jquery, navigator) {
|
|
var Materia, location_operations;
|
|
Url = Url(document);
|
|
cookies.init(config, document);
|
|
location_operations = Location(document);
|
|
cache.init(cookies, config);
|
|
Materia = {
|
|
initialize: function(public_key, options) {
|
|
var i;
|
|
config.key = public_key;
|
|
if (options) {
|
|
for (i in options) {
|
|
config.options[i] = options[i];
|
|
}
|
|
}
|
|
},
|
|
setOAuthdURL: function(url) {
|
|
config.oauthd_url = url;
|
|
config.oauthd_base = Url.getAbsUrl(config.oauthd_url).match(/^.{2,5}:\/\/[^/]+/)[0];
|
|
},
|
|
getOAuthdURL: function() {
|
|
return config.oauthd_url;
|
|
},
|
|
getVersion: function() {
|
|
return config.version;
|
|
},
|
|
extend: function(name, module) {
|
|
return this[name] = module(this);
|
|
},
|
|
getConfig: function() {
|
|
return config;
|
|
},
|
|
getWindow: function() {
|
|
return window;
|
|
},
|
|
getDocument: function() {
|
|
return document;
|
|
},
|
|
getNavigator: function() {
|
|
return navigator;
|
|
},
|
|
getJquery: function() {
|
|
return jquery;
|
|
},
|
|
getUrl: function() {
|
|
return Url;
|
|
},
|
|
getCache: function() {
|
|
return cache;
|
|
},
|
|
getCookies: function() {
|
|
return cookies;
|
|
},
|
|
getLocationOperations: function() {
|
|
return location_operations;
|
|
}
|
|
};
|
|
return Materia;
|
|
};
|
|
|
|
},{"../config":1,"../tools/cache":9,"../tools/cookies":10,"../tools/location_operations":12,"../tools/url":14}],4:[function(require,module,exports){
|
|
"use strict";
|
|
var cookies, oauthio_requests, sha1;
|
|
|
|
cookies = require("../tools/cookies");
|
|
|
|
oauthio_requests = require("./request");
|
|
|
|
sha1 = require("../tools/sha1");
|
|
|
|
module.exports = function(Materia) {
|
|
var $, Url, cache, client_states, config, document, location_operations, oauth, oauth_result, oauthio, parse_urlfragment, providers_api, window;
|
|
Url = Materia.getUrl();
|
|
config = Materia.getConfig();
|
|
document = Materia.getDocument();
|
|
window = Materia.getWindow();
|
|
$ = Materia.getJquery();
|
|
cache = Materia.getCache();
|
|
providers_api = require('./providers')(Materia);
|
|
config.oauthd_base = Url.getAbsUrl(config.oauthd_url).match(/^.{2,5}:\/\/[^/]+/)[0];
|
|
client_states = [];
|
|
oauth_result = void 0;
|
|
(parse_urlfragment = function() {
|
|
var cookie_state, results;
|
|
results = /[\\#&]oauthio=([^&]*)/.exec(document.location.hash);
|
|
if (results) {
|
|
document.location.hash = document.location.hash.replace(/&?oauthio=[^&]*/, "");
|
|
oauth_result = decodeURIComponent(results[1].replace(/\+/g, " "));
|
|
cookie_state = cookies.readCookie("oauthio_state");
|
|
if (cookie_state) {
|
|
client_states.push(cookie_state);
|
|
cookies.eraseCookie("oauthio_state");
|
|
}
|
|
}
|
|
})();
|
|
location_operations = Materia.getLocationOperations();
|
|
oauthio = {
|
|
request: oauthio_requests(Materia, client_states, providers_api)
|
|
};
|
|
oauth = {
|
|
initialize: function(public_key, options) {
|
|
return Materia.initialize(public_key, options);
|
|
},
|
|
setOAuthdURL: function(url) {
|
|
config.oauthd_url = url;
|
|
config.oauthd_base = Url.getAbsUrl(config.oauthd_url).match(/^.{2,5}:\/\/[^/]+/)[0];
|
|
},
|
|
create: function(provider, tokens, request) {
|
|
var i, make_res, make_res_endpoint, res;
|
|
if (!tokens) {
|
|
return cache.tryCache(oauth, provider, true);
|
|
}
|
|
if (typeof request !== "object") {
|
|
providers_api.fetchDescription(provider);
|
|
}
|
|
make_res = function(method) {
|
|
return oauthio.request.mkHttp(provider, tokens, request, method);
|
|
};
|
|
make_res_endpoint = function(method, url) {
|
|
return oauthio.request.mkHttpEndpoint(provider, tokens, request, method, url);
|
|
};
|
|
res = {};
|
|
for (i in tokens) {
|
|
res[i] = tokens[i];
|
|
}
|
|
res.get = make_res("GET");
|
|
res.post = make_res("POST");
|
|
res.put = make_res("PUT");
|
|
res.patch = make_res("PATCH");
|
|
res.del = make_res("DELETE");
|
|
res.me = oauthio.request.mkHttpMe(provider, tokens, request, "GET");
|
|
return res;
|
|
},
|
|
popup: function(provider, opts, callback) {
|
|
var defer, frm, getMessage, gotmessage, interval, res, url, wnd, wndTimeout, wnd_options, wnd_settings;
|
|
gotmessage = false;
|
|
getMessage = function(e) {
|
|
if (!gotmessage) {
|
|
if (e.origin !== config.oauthd_base) {
|
|
return;
|
|
}
|
|
try {
|
|
wnd.close();
|
|
} catch (_error) {}
|
|
opts.data = e.data;
|
|
oauthio.request.sendCallback(opts, defer);
|
|
return gotmessage = true;
|
|
}
|
|
};
|
|
wnd = void 0;
|
|
frm = void 0;
|
|
wndTimeout = void 0;
|
|
defer = $.Deferred();
|
|
opts = opts || {};
|
|
if (!config.key) {
|
|
if (defer != null) {
|
|
defer.reject(new Error("OAuth object must be initialized"));
|
|
}
|
|
if (callback == null) {
|
|
return defer.promise();
|
|
} else {
|
|
return callback(new Error("OAuth object must be initialized"));
|
|
}
|
|
}
|
|
if (arguments.length === 2 && typeof opts === 'function') {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
if (cache.cacheEnabled(opts.cache)) {
|
|
res = cache.tryCache(oauth, provider, opts.cache);
|
|
if (res) {
|
|
if (defer != null) {
|
|
defer.resolve(res);
|
|
}
|
|
if (callback) {
|
|
return callback(null, res);
|
|
} else {
|
|
return defer.promise();
|
|
}
|
|
}
|
|
}
|
|
if (!opts.state) {
|
|
opts.state = sha1.create_hash();
|
|
opts.state_type = "client";
|
|
}
|
|
client_states.push(opts.state);
|
|
url = config.oauthd_url + "/auth/" + provider + "?k=" + config.key;
|
|
url += "&d=" + encodeURIComponent(Url.getAbsUrl("/"));
|
|
if (opts) {
|
|
url += "&opts=" + encodeURIComponent(JSON.stringify(opts));
|
|
}
|
|
if (opts.wnd_settings) {
|
|
wnd_settings = opts.wnd_settings;
|
|
delete opts.wnd_settings;
|
|
} else {
|
|
wnd_settings = {
|
|
width: Math.floor(window.outerWidth * 0.8),
|
|
height: Math.floor(window.outerHeight * 0.5)
|
|
};
|
|
}
|
|
if (wnd_settings.width < 1000) {
|
|
wnd_settings.width = 1000;
|
|
}
|
|
if (wnd_settings.height < 630) {
|
|
wnd_settings.height = 630;
|
|
}
|
|
wnd_settings.left = Math.floor(window.screenX + (window.outerWidth - wnd_settings.width) / 2);
|
|
wnd_settings.top = Math.floor(window.screenY + (window.outerHeight - wnd_settings.height) / 8);
|
|
wnd_options = "width=" + wnd_settings.width + ",height=" + wnd_settings.height;
|
|
wnd_options += ",toolbar=0,scrollbars=1,status=1,resizable=1,location=1,menuBar=0";
|
|
wnd_options += ",left=" + wnd_settings.left + ",top=" + wnd_settings.top;
|
|
opts = {
|
|
provider: provider,
|
|
cache: opts.cache
|
|
};
|
|
opts.callback = function(e, r) {
|
|
if (window.removeEventListener) {
|
|
window.removeEventListener("message", getMessage, false);
|
|
} else if (window.detachEvent) {
|
|
window.detachEvent("onmessage", getMessage);
|
|
} else {
|
|
if (document.detachEvent) {
|
|
document.detachEvent("onmessage", getMessage);
|
|
}
|
|
}
|
|
opts.callback = function() {};
|
|
if (wndTimeout) {
|
|
clearTimeout(wndTimeout);
|
|
wndTimeout = undefined;
|
|
}
|
|
if (callback) {
|
|
return callback(e, r);
|
|
} else {
|
|
return undefined;
|
|
}
|
|
};
|
|
if (window.attachEvent) {
|
|
window.attachEvent("onmessage", getMessage);
|
|
} else if (document.attachEvent) {
|
|
document.attachEvent("onmessage", getMessage);
|
|
} else {
|
|
if (window.addEventListener) {
|
|
window.addEventListener("message", getMessage, false);
|
|
}
|
|
}
|
|
if (typeof chrome !== "undefined" && chrome.runtime && chrome.runtime.onMessageExternal) {
|
|
chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse) {
|
|
request.origin = sender.url.match(/^.{2,5}:\/\/[^/]+/)[0];
|
|
return getMessage(request);
|
|
});
|
|
}
|
|
if (!frm && (navigator.userAgent.indexOf("MSIE") !== -1 || navigator.appVersion.indexOf("Trident/") > 0)) {
|
|
frm = document.createElement("iframe");
|
|
frm.src = config.oauthd_url + "/auth/iframe?d=" + encodeURIComponent(Url.getAbsUrl("/"));
|
|
frm.width = 0;
|
|
frm.height = 0;
|
|
frm.frameBorder = 0;
|
|
frm.style.visibility = "hidden";
|
|
document.body.appendChild(frm);
|
|
}
|
|
wndTimeout = setTimeout(function() {
|
|
if (defer != null) {
|
|
defer.reject(new Error("Authorization timed out"));
|
|
}
|
|
if (opts.callback && typeof opts.callback === "function") {
|
|
opts.callback(new Error("Authorization timed out"));
|
|
}
|
|
try {
|
|
wnd.close();
|
|
} catch (_error) {}
|
|
}, 1200 * 1000);
|
|
wnd = window.open(url, "Authorization", wnd_options);
|
|
if (wnd) {
|
|
wnd.focus();
|
|
interval = window.setInterval(function() {
|
|
if (wnd === null || wnd.closed) {
|
|
window.clearInterval(interval);
|
|
if (!gotmessage) {
|
|
if (defer != null) {
|
|
defer.reject(new Error("The popup was closed"));
|
|
}
|
|
if (opts.callback && typeof opts.callback === "function") {
|
|
return opts.callback(new Error("The popup was closed"));
|
|
}
|
|
}
|
|
}
|
|
}, 500);
|
|
} else {
|
|
if (defer != null) {
|
|
defer.reject(new Error("Could not open a popup"));
|
|
}
|
|
if (opts.callback && typeof opts.callback === "function") {
|
|
opts.callback(new Error("Could not open a popup"));
|
|
}
|
|
}
|
|
return defer != null ? defer.promise() : void 0;
|
|
},
|
|
redirect: function(provider, opts, url) {
|
|
var redirect_uri, res;
|
|
if (arguments.length === 2) {
|
|
url = opts;
|
|
opts = {};
|
|
}
|
|
if (cache.cacheEnabled(opts.cache)) {
|
|
res = cache.tryCache(oauth, provider, opts.cache);
|
|
if (res) {
|
|
url = Url.getAbsUrl(url) + (url.indexOf("#") === -1 ? "#" : "&") + "oauthio=cache";
|
|
location_operations.changeHref(url);
|
|
location_operations.reload();
|
|
return;
|
|
}
|
|
}
|
|
if (!opts.state) {
|
|
opts.state = sha1.create_hash();
|
|
opts.state_type = "client";
|
|
}
|
|
cookies.createCookie("oauthio_state", opts.state);
|
|
redirect_uri = encodeURIComponent(Url.getAbsUrl(url));
|
|
url = config.oauthd_url + "/auth/" + provider + "?k=" + config.key;
|
|
url += "&redirect_uri=" + redirect_uri;
|
|
if (opts) {
|
|
url += "&opts=" + encodeURIComponent(JSON.stringify(opts));
|
|
}
|
|
location_operations.changeHref(url);
|
|
},
|
|
callback: function(provider, opts, callback) {
|
|
var defer, res;
|
|
defer = $.Deferred();
|
|
if (arguments.length === 1 && typeof provider === "function") {
|
|
callback = provider;
|
|
provider = undefined;
|
|
opts = {};
|
|
}
|
|
if (arguments.length === 1 && typeof provider === "string") {
|
|
opts = {};
|
|
}
|
|
if (arguments.length === 2 && typeof opts === "function") {
|
|
callback = opts;
|
|
opts = {};
|
|
}
|
|
if (cache.cacheEnabled(opts.cache) || oauth_result === "cache") {
|
|
res = cache.tryCache(oauth, provider, opts.cache);
|
|
if (oauth_result === "cache" && (typeof provider !== "string" || !provider)) {
|
|
if (defer != null) {
|
|
defer.reject(new Error("You must set a provider when using the cache"));
|
|
}
|
|
if (callback) {
|
|
return callback(new Error("You must set a provider when using the cache"));
|
|
} else {
|
|
return defer != null ? defer.promise() : void 0;
|
|
}
|
|
}
|
|
if (res) {
|
|
if (callback) {
|
|
if (res) {
|
|
return callback(null, res);
|
|
}
|
|
} else {
|
|
if (defer != null) {
|
|
defer.resolve(res);
|
|
}
|
|
return defer != null ? defer.promise() : void 0;
|
|
}
|
|
}
|
|
}
|
|
if (!oauth_result) {
|
|
return;
|
|
}
|
|
oauthio.request.sendCallback({
|
|
data: oauth_result,
|
|
provider: provider,
|
|
cache: opts.cache,
|
|
callback: callback
|
|
}, defer);
|
|
return defer != null ? defer.promise() : void 0;
|
|
},
|
|
clearCache: function(provider) {
|
|
cookies.eraseCookie("oauthio_provider_" + provider);
|
|
},
|
|
http_me: function(opts) {
|
|
if (oauthio.request.http_me) {
|
|
oauthio.request.http_me(opts);
|
|
}
|
|
},
|
|
http: function(opts) {
|
|
if (oauthio.request.http) {
|
|
oauthio.request.http(opts);
|
|
}
|
|
},
|
|
getVersion: function() {
|
|
return Materia.getVersion.apply(this);
|
|
}
|
|
};
|
|
return oauth;
|
|
};
|
|
|
|
},{"../tools/cookies":10,"../tools/sha1":13,"./providers":5,"./request":6}],5:[function(require,module,exports){
|
|
"use strict";
|
|
var config;
|
|
|
|
config = require("../config");
|
|
|
|
module.exports = function(Materia) {
|
|
var $, providers_api, providers_cb, providers_desc;
|
|
$ = Materia.getJquery();
|
|
providers_desc = {};
|
|
providers_cb = {};
|
|
providers_api = {
|
|
execProvidersCb: function(provider, e, r) {
|
|
var cbs, i;
|
|
if (providers_cb[provider]) {
|
|
cbs = providers_cb[provider];
|
|
delete providers_cb[provider];
|
|
for (i in cbs) {
|
|
cbs[i](e, r);
|
|
}
|
|
}
|
|
},
|
|
fetchDescription: function(provider) {
|
|
if (providers_desc[provider]) {
|
|
return;
|
|
}
|
|
providers_desc[provider] = true;
|
|
$.ajax({
|
|
url: config.oauthd_api + "/providers/" + provider,
|
|
data: {
|
|
extend: true
|
|
},
|
|
dataType: "json"
|
|
}).done(function(data) {
|
|
providers_desc[provider] = data.data;
|
|
providers_api.execProvidersCb(provider, null, data.data);
|
|
}).always(function() {
|
|
if (typeof providers_desc[provider] !== "object") {
|
|
delete providers_desc[provider];
|
|
providers_api.execProvidersCb(provider, new Error("Unable to fetch request description"));
|
|
}
|
|
});
|
|
},
|
|
getDescription: function(provider, opts, callback) {
|
|
opts = opts || {};
|
|
if (typeof providers_desc[provider] === "object") {
|
|
return callback(null, providers_desc[provider]);
|
|
}
|
|
if (!providers_desc[provider]) {
|
|
providers_api.fetchDescription(provider);
|
|
}
|
|
if (!opts.wait) {
|
|
return callback(null, {});
|
|
}
|
|
providers_cb[provider] = providers_cb[provider] || [];
|
|
providers_cb[provider].push(callback);
|
|
}
|
|
};
|
|
return providers_api;
|
|
};
|
|
|
|
},{"../config":1}],6:[function(require,module,exports){
|
|
"use strict";
|
|
var Q, Url,
|
|
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
|
|
|
Url = require('../tools/url')();
|
|
|
|
Q = require('q');
|
|
|
|
module.exports = function(Materia, client_states, providers_api) {
|
|
var $, cache, config, extended_methods, fetched_methods;
|
|
$ = Materia.getJquery();
|
|
config = Materia.getConfig();
|
|
cache = Materia.getCache();
|
|
extended_methods = [];
|
|
fetched_methods = false;
|
|
return {
|
|
retrieveMethods: function() {
|
|
var defer;
|
|
defer = Q.defer();
|
|
if (!fetched_methods) {
|
|
$.ajax(config.oauthd_url + '/api/extended-endpoints').then(function(data) {
|
|
extended_methods = data.data;
|
|
fetched_methods = true;
|
|
return defer.resolve();
|
|
}).fail(function(e) {
|
|
fetched_methods = true;
|
|
return defer.reject(e);
|
|
});
|
|
} else {
|
|
defer.resolve(extended_methods);
|
|
}
|
|
return defer.promise;
|
|
},
|
|
generateMethods: function(request_object, tokens, provider) {
|
|
var k, kk, name_array, pt, v, vv, _i, _len, _results;
|
|
if (extended_methods != null) {
|
|
_results = [];
|
|
for (k = _i = 0, _len = extended_methods.length; _i < _len; k = ++_i) {
|
|
v = extended_methods[k];
|
|
name_array = v.name.split('.');
|
|
pt = request_object;
|
|
_results.push((function() {
|
|
var _j, _len1, _results1;
|
|
_results1 = [];
|
|
for (kk = _j = 0, _len1 = name_array.length; _j < _len1; kk = ++_j) {
|
|
vv = name_array[kk];
|
|
if (kk < name_array.length - 1) {
|
|
if (pt[vv] == null) {
|
|
pt[vv] = {};
|
|
}
|
|
_results1.push(pt = pt[vv]);
|
|
} else {
|
|
_results1.push(pt[vv] = this.mkHttpAll(provider, tokens, v, arguments));
|
|
}
|
|
}
|
|
return _results1;
|
|
}).apply(this, arguments));
|
|
}
|
|
return _results;
|
|
}
|
|
},
|
|
http: function(opts) {
|
|
var defer, desc_opts, doRequest, i, options;
|
|
doRequest = function() {
|
|
var i, k, qs, request;
|
|
request = options.oauthio.request || {};
|
|
if (!request.cors) {
|
|
options.url = encodeURIComponent(options.url);
|
|
if (options.url[0] !== "/") {
|
|
options.url = "/" + options.url;
|
|
}
|
|
options.url = config.oauthd_url + "/request/" + options.oauthio.provider + options.url;
|
|
options.headers = options.headers || {};
|
|
options.headers.oauthio = "k=" + config.key;
|
|
if (options.oauthio.tokens.oauth_token && options.oauthio.tokens.oauth_token_secret) {
|
|
options.headers.oauthio += "&oauthv=1";
|
|
}
|
|
for (k in options.oauthio.tokens) {
|
|
options.headers.oauthio += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(options.oauthio.tokens[k]);
|
|
}
|
|
delete options.oauthio;
|
|
return $.ajax(options);
|
|
}
|
|
if (options.oauthio.tokens) {
|
|
if (options.oauthio.tokens.access_token) {
|
|
options.oauthio.tokens.token = options.oauthio.tokens.access_token;
|
|
}
|
|
if (!options.url.match(/^[a-z]{2,16}:\/\//)) {
|
|
if (options.url[0] !== "/") {
|
|
options.url = "/" + options.url;
|
|
}
|
|
options.url = request.url + options.url;
|
|
}
|
|
options.url = Url.replaceParam(options.url, options.oauthio.tokens, request.parameters);
|
|
if (request.query) {
|
|
qs = [];
|
|
for (i in request.query) {
|
|
qs.push(encodeURIComponent(i) + "=" + encodeURIComponent(Url.replaceParam(request.query[i], options.oauthio.tokens, request.parameters)));
|
|
}
|
|
if (__indexOf.call(options.url, "?") >= 0) {
|
|
options.url += "&" + qs;
|
|
} else {
|
|
options.url += "?" + qs;
|
|
}
|
|
}
|
|
if (request.headers) {
|
|
options.headers = options.headers || {};
|
|
for (i in request.headers) {
|
|
options.headers[i] = Url.replaceParam(request.headers[i], options.oauthio.tokens, request.parameters);
|
|
}
|
|
}
|
|
delete options.oauthio;
|
|
return $.ajax(options);
|
|
}
|
|
};
|
|
options = {};
|
|
i = void 0;
|
|
for (i in opts) {
|
|
options[i] = opts[i];
|
|
}
|
|
if (!options.oauthio.request || options.oauthio.request === true) {
|
|
desc_opts = {
|
|
wait: !!options.oauthio.request
|
|
};
|
|
defer = $.Deferred();
|
|
providers_api.getDescription(options.oauthio.provider, desc_opts, function(e, desc) {
|
|
if (e) {
|
|
return defer.reject(e);
|
|
}
|
|
if (options.oauthio.tokens.oauth_token && options.oauthio.tokens.oauth_token_secret) {
|
|
options.oauthio.request = desc.oauth1 && desc.oauth1.request;
|
|
} else {
|
|
options.oauthio.request = desc.oauth2 && desc.oauth2.request;
|
|
}
|
|
defer.resolve();
|
|
});
|
|
return defer.then(doRequest);
|
|
} else {
|
|
return doRequest();
|
|
}
|
|
},
|
|
http_me: function(opts) {
|
|
var defer, desc_opts, doRequest, k, options;
|
|
doRequest = function() {
|
|
var defer, k, promise, request;
|
|
defer = $.Deferred();
|
|
request = options.oauthio.request || {};
|
|
options.url = config.oauthd_url + "/auth/" + options.oauthio.provider + "/me";
|
|
options.headers = options.headers || {};
|
|
options.headers.oauthio = "k=" + config.key;
|
|
if (options.oauthio.tokens.oauth_token && options.oauthio.tokens.oauth_token_secret) {
|
|
options.headers.oauthio += "&oauthv=1";
|
|
}
|
|
for (k in options.oauthio.tokens) {
|
|
options.headers.oauthio += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(options.oauthio.tokens[k]);
|
|
}
|
|
delete options.oauthio;
|
|
promise = $.ajax(options);
|
|
$.when(promise).done(function(data) {
|
|
defer.resolve(data.data);
|
|
}).fail(function(data) {
|
|
if (data.responseJSON) {
|
|
defer.reject(data.responseJSON.data);
|
|
} else {
|
|
defer.reject(new Error("An error occured while trying to access the resource"));
|
|
}
|
|
});
|
|
return defer.promise();
|
|
};
|
|
options = {};
|
|
for (k in opts) {
|
|
options[k] = opts[k];
|
|
}
|
|
if (!options.oauthio.request || options.oauthio.request === true) {
|
|
desc_opts = {
|
|
wait: !!options.oauthio.request
|
|
};
|
|
defer = $.Deferred();
|
|
providers_api.getDescription(options.oauthio.provider, desc_opts, function(e, desc) {
|
|
if (e) {
|
|
return defer.reject(e);
|
|
}
|
|
if (options.oauthio.tokens.oauth_token && options.oauthio.tokens.oauth_token_secret) {
|
|
options.oauthio.request = desc.oauth1 && desc.oauth1.request;
|
|
} else {
|
|
options.oauthio.request = desc.oauth2 && desc.oauth2.request;
|
|
}
|
|
defer.resolve();
|
|
});
|
|
return defer.then(doRequest);
|
|
} else {
|
|
return doRequest();
|
|
}
|
|
},
|
|
http_all: function(options, endpoint_descriptor, parameters) {
|
|
var doRequest;
|
|
doRequest = function() {
|
|
var defer, k, promise, request;
|
|
defer = $.Deferred();
|
|
request = options.oauthio.request || {};
|
|
options.headers = options.headers || {};
|
|
options.headers.oauthio = "k=" + config.key;
|
|
if (options.oauthio.tokens.oauth_token && options.oauthio.tokens.oauth_token_secret) {
|
|
options.headers.oauthio += "&oauthv=1";
|
|
}
|
|
for (k in options.oauthio.tokens) {
|
|
options.headers.oauthio += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(options.oauthio.tokens[k]);
|
|
}
|
|
delete options.oauthio;
|
|
promise = $.ajax(options);
|
|
$.when(promise).done(function(data) {
|
|
var error;
|
|
if (typeof data.data === 'string') {
|
|
try {
|
|
data.data = JSON.parse(data.data);
|
|
} catch (_error) {
|
|
error = _error;
|
|
data.data = data.data;
|
|
} finally {
|
|
defer.resolve(data.data);
|
|
}
|
|
}
|
|
}).fail(function(data) {
|
|
if (data.responseJSON) {
|
|
defer.reject(data.responseJSON.data);
|
|
} else {
|
|
defer.reject(new Error("An error occured while trying to access the resource"));
|
|
}
|
|
});
|
|
return defer.promise();
|
|
};
|
|
return doRequest();
|
|
},
|
|
mkHttp: function(provider, tokens, request, method) {
|
|
var base;
|
|
base = this;
|
|
return function(opts, opts2) {
|
|
var i, options;
|
|
options = {};
|
|
if (typeof opts === "string") {
|
|
if (typeof opts2 === "object") {
|
|
for (i in opts2) {
|
|
options[i] = opts2[i];
|
|
}
|
|
}
|
|
options.url = opts;
|
|
} else if (typeof opts === "object") {
|
|
for (i in opts) {
|
|
options[i] = opts[i];
|
|
}
|
|
}
|
|
options.type = options.type || method;
|
|
options.oauthio = {
|
|
provider: provider,
|
|
tokens: tokens,
|
|
request: request
|
|
};
|
|
return base.http(options);
|
|
};
|
|
},
|
|
mkHttpMe: function(provider, tokens, request, method) {
|
|
var base;
|
|
base = this;
|
|
return function(filter) {
|
|
var options;
|
|
options = {};
|
|
options.type = options.type || method;
|
|
options.oauthio = {
|
|
provider: provider,
|
|
tokens: tokens,
|
|
request: request
|
|
};
|
|
options.data = options.data || {};
|
|
if (filter) {
|
|
options.data.filter = filter.join(",");
|
|
}
|
|
return base.http_me(options);
|
|
};
|
|
},
|
|
mkHttpAll: function(provider, tokens, endpoint_descriptor) {
|
|
var base;
|
|
base = this;
|
|
return function() {
|
|
var k, options, th_param, v;
|
|
options = {};
|
|
options.type = endpoint_descriptor.method;
|
|
options.url = config.oauthd_url + endpoint_descriptor.endpoint.replace(':provider', provider);
|
|
options.oauthio = {
|
|
provider: provider,
|
|
tokens: tokens
|
|
};
|
|
options.data = {};
|
|
for (k in arguments) {
|
|
v = arguments[k];
|
|
th_param = endpoint_descriptor.params[k];
|
|
if (th_param != null) {
|
|
options.data[th_param.name] = v;
|
|
}
|
|
}
|
|
options.data = options.data || {};
|
|
return base.http_all(options, endpoint_descriptor, arguments);
|
|
};
|
|
},
|
|
sendCallback: function(opts, defer) {
|
|
var base, data, e, err, i, make_res, request, res, tokens;
|
|
base = this;
|
|
data = void 0;
|
|
err = void 0;
|
|
try {
|
|
data = JSON.parse(opts.data);
|
|
} catch (_error) {
|
|
e = _error;
|
|
defer.reject(new Error("Error while parsing result"));
|
|
return opts.callback(new Error("Error while parsing result"));
|
|
}
|
|
if (!data || !data.provider) {
|
|
return;
|
|
}
|
|
if (opts.provider && data.provider.toLowerCase() !== opts.provider.toLowerCase()) {
|
|
err = new Error("Returned provider name does not match asked provider");
|
|
defer.reject(err);
|
|
if (opts.callback && typeof opts.callback === "function") {
|
|
return opts.callback(err);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
if (data.status === "error" || data.status === "fail") {
|
|
err = new Error(data.message);
|
|
err.body = data.data;
|
|
defer.reject(err);
|
|
if (opts.callback && typeof opts.callback === "function") {
|
|
return opts.callback(err);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
if (data.status !== "success" || !data.data) {
|
|
err = new Error();
|
|
err.body = data.data;
|
|
defer.reject(err);
|
|
if (opts.callback && typeof opts.callback === "function") {
|
|
return opts.callback(err);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
data.state = data.state.replace(/\s+/g, "");
|
|
i = 0;
|
|
while (i < client_states.length) {
|
|
client_states[i] = client_states[i].replace(/\s+/g, "");
|
|
i++;
|
|
}
|
|
if (!data.state || client_states.indexOf(data.state) === -1) {
|
|
defer.reject(new Error("State is not matching"));
|
|
if (opts.callback && typeof opts.callback === "function") {
|
|
return opts.callback(new Error("State is not matching"));
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
if (!opts.provider) {
|
|
data.data.provider = data.provider;
|
|
}
|
|
res = data.data;
|
|
res.provider = data.provider.toLowerCase();
|
|
if (cache.cacheEnabled(opts.cache) && res) {
|
|
cache.storeCache(data.provider, res);
|
|
}
|
|
request = res.request;
|
|
delete res.request;
|
|
tokens = void 0;
|
|
if (res.access_token) {
|
|
tokens = {
|
|
access_token: res.access_token
|
|
};
|
|
} else if (res.oauth_token && res.oauth_token_secret) {
|
|
tokens = {
|
|
oauth_token: res.oauth_token,
|
|
oauth_token_secret: res.oauth_token_secret
|
|
};
|
|
}
|
|
if (!request) {
|
|
defer.resolve(res);
|
|
if (opts.callback && typeof opts.callback === "function") {
|
|
return opts.callback(null, res);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
if (request.required) {
|
|
for (i in request.required) {
|
|
tokens[request.required[i]] = res[request.required[i]];
|
|
}
|
|
}
|
|
make_res = function(method) {
|
|
return base.mkHttp(data.provider, tokens, request, method);
|
|
};
|
|
res.toJson = function() {
|
|
var a;
|
|
a = {};
|
|
if (res.access_token != null) {
|
|
a.access_token = res.access_token;
|
|
}
|
|
if (res.oauth_token != null) {
|
|
a.oauth_token = res.oauth_token;
|
|
}
|
|
if (res.oauth_token_secret != null) {
|
|
a.oauth_token_secret = res.oauth_token_secret;
|
|
}
|
|
if (res.expires_in != null) {
|
|
a.expires_in = res.expires_in;
|
|
}
|
|
if (res.token_type != null) {
|
|
a.token_type = res.token_type;
|
|
}
|
|
if (res.id_token != null) {
|
|
a.id_token = res.id_token;
|
|
}
|
|
if (res.provider != null) {
|
|
a.provider = res.provider;
|
|
}
|
|
if (res.email != null) {
|
|
a.email = res.email;
|
|
}
|
|
return a;
|
|
};
|
|
res.get = make_res("GET");
|
|
res.post = make_res("POST");
|
|
res.put = make_res("PUT");
|
|
res.patch = make_res("PATCH");
|
|
res.del = make_res("DELETE");
|
|
res.me = base.mkHttpMe(data.provider, tokens, request, "GET");
|
|
return this.retrieveMethods().then((function(_this) {
|
|
return function() {
|
|
_this.generateMethods(res, tokens, data.provider);
|
|
defer.resolve(res);
|
|
if (opts.callback && typeof opts.callback === "function") {
|
|
return opts.callback(null, res);
|
|
} else {
|
|
|
|
}
|
|
};
|
|
})(this)).fail((function(_this) {
|
|
return function(e) {
|
|
console.log('Could not retrieve methods', e);
|
|
defer.resolve(res);
|
|
if (opts.callback && typeof opts.callback === "function") {
|
|
return opts.callback(null, res);
|
|
} else {
|
|
|
|
}
|
|
};
|
|
})(this));
|
|
}
|
|
};
|
|
};
|
|
|
|
},{"../tools/url":14,"q":16}],7:[function(require,module,exports){
|
|
"use strict";
|
|
module.exports = function(Materia) {
|
|
var $, UserObject, config, cookieStore, lastSave;
|
|
$ = Materia.getJquery();
|
|
config = Materia.getConfig();
|
|
cookieStore = Materia.getCookies();
|
|
lastSave = null;
|
|
UserObject = (function() {
|
|
function UserObject(data) {
|
|
this.token = data.token;
|
|
this.data = data.user;
|
|
this.providers = data.providers;
|
|
lastSave = this.getEditableData();
|
|
}
|
|
|
|
UserObject.prototype.getEditableData = function() {
|
|
var data, key;
|
|
data = [];
|
|
for (key in this.data) {
|
|
if (['id', 'email'].indexOf(key) === -1) {
|
|
data.push({
|
|
key: key,
|
|
value: this.data[key]
|
|
});
|
|
}
|
|
}
|
|
return data;
|
|
};
|
|
|
|
UserObject.prototype.save = function() {
|
|
var d, dataToSave, keyIsInLastSave, _i, _j, _len, _len1, _ref;
|
|
dataToSave = {};
|
|
for (_i = 0, _len = lastSave.length; _i < _len; _i++) {
|
|
d = lastSave[_i];
|
|
if (this.data[d.key] !== d.value) {
|
|
dataToSave[d.key] = this.data[d.key];
|
|
}
|
|
if (this.data[d.key] === null) {
|
|
delete this.data[d.key];
|
|
}
|
|
}
|
|
keyIsInLastSave = function(key) {
|
|
var o, _j, _len1;
|
|
for (_j = 0, _len1 = lastSave.length; _j < _len1; _j++) {
|
|
o = lastSave[_j];
|
|
if (o.key === key) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
_ref = this.getEditableData();
|
|
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
|
|
d = _ref[_j];
|
|
if (!keyIsInLastSave(d.key)) {
|
|
dataToSave[d.key] = this.data[d.key];
|
|
}
|
|
}
|
|
this.saveLocal();
|
|
return Materia.API.put('/api/usermanagement/user?k=' + config.key + '&token=' + this.token, dataToSave);
|
|
};
|
|
|
|
UserObject.prototype.select = function(provider) {
|
|
var OAuthResult;
|
|
OAuthResult = null;
|
|
return OAuthResult;
|
|
};
|
|
|
|
UserObject.prototype.saveLocal = function() {
|
|
var copy;
|
|
copy = {
|
|
token: this.token,
|
|
user: this.data,
|
|
providers: this.providers
|
|
};
|
|
cookieStore.eraseCookie('oio_auth');
|
|
return cookieStore.createCookie('oio_auth', JSON.stringify(copy), 21600);
|
|
};
|
|
|
|
UserObject.prototype.hasProvider = function(provider) {
|
|
var _ref;
|
|
return ((_ref = this.providers) != null ? _ref.indexOf(provider) : void 0) !== -1;
|
|
};
|
|
|
|
UserObject.prototype.getProviders = function() {
|
|
var defer;
|
|
defer = $.Deferred();
|
|
Materia.API.get('/api/usermanagement/user/providers?k=' + config.key + '&token=' + this.token).done((function(_this) {
|
|
return function(providers) {
|
|
_this.providers = providers.data;
|
|
_this.saveLocal();
|
|
return defer.resolve(_this.providers);
|
|
};
|
|
})(this)).fail(function(err) {
|
|
return defer.reject(err);
|
|
});
|
|
return defer.promise();
|
|
};
|
|
|
|
UserObject.prototype.addProvider = function(oauthRes) {
|
|
var defer;
|
|
defer = $.Deferred();
|
|
if (typeof oauthRes.toJson === 'function') {
|
|
oauthRes = oauthRes.toJson();
|
|
}
|
|
oauthRes.email = this.data.email;
|
|
this.providers.push(oauthRes.provider);
|
|
Materia.API.post('/api/usermanagement/user/providers?k=' + config.key + '&token=' + this.token, oauthRes).done((function(_this) {
|
|
return function(res) {
|
|
_this.data = res.data;
|
|
_this.saveLocal();
|
|
return defer.resolve();
|
|
};
|
|
})(this)).fail((function(_this) {
|
|
return function(err) {
|
|
_this.providers.splice(_this.providers.indexOf(oauthRes.provider), 1);
|
|
return defer.reject(err);
|
|
};
|
|
})(this));
|
|
return defer.promise();
|
|
};
|
|
|
|
UserObject.prototype.removeProvider = function(provider) {
|
|
var defer;
|
|
defer = $.Deferred();
|
|
this.providers.splice(this.providers.indexOf(provider), 1);
|
|
Materia.API.del('/api/usermanagement/user/providers/' + provider + '?k=' + config.key + '&token=' + this.token).done((function(_this) {
|
|
return function(res) {
|
|
_this.saveLocal();
|
|
return defer.resolve(res);
|
|
};
|
|
})(this)).fail((function(_this) {
|
|
return function(err) {
|
|
_this.providers.push(provider);
|
|
return defer.reject(err);
|
|
};
|
|
})(this));
|
|
return defer.promise();
|
|
};
|
|
|
|
UserObject.prototype.changePassword = function(oldPassword, newPassword) {
|
|
return Materia.API.post('/api/usermanagement/user/password?k=' + config.key + '&token=' + this.token, {
|
|
password: newPassword
|
|
});
|
|
};
|
|
|
|
UserObject.prototype.isLoggued = function() {
|
|
return Materia.User.isLogged();
|
|
};
|
|
|
|
UserObject.prototype.isLogged = function() {
|
|
return Materia.User.isLogged();
|
|
};
|
|
|
|
UserObject.prototype.logout = function() {
|
|
var defer;
|
|
defer = $.Deferred();
|
|
cookieStore.eraseCookie('oio_auth');
|
|
Materia.API.post('/api/usermanagement/user/logout?k=' + config.key + '&token=' + this.token).done(function() {
|
|
return defer.resolve();
|
|
}).fail(function(err) {
|
|
return defer.reject(err);
|
|
});
|
|
return defer.promise();
|
|
};
|
|
|
|
return UserObject;
|
|
|
|
})();
|
|
return {
|
|
initialize: function(public_key, options) {
|
|
return Materia.initialize(public_key, options);
|
|
},
|
|
setOAuthdURL: function(url) {
|
|
return Materia.setOAuthdURL(url);
|
|
},
|
|
signup: function(data) {
|
|
var defer;
|
|
defer = $.Deferred();
|
|
if (typeof data.toJson === 'function') {
|
|
data = data.toJson();
|
|
}
|
|
Materia.API.post('/api/usermanagement/signup?k=' + config.key, data).done(function(res) {
|
|
cookieStore.createCookie('oio_auth', JSON.stringify(res.data), res.data.expires_in || 21600);
|
|
return defer.resolve(new UserObject(res.data));
|
|
}).fail(function(err) {
|
|
return defer.reject(err);
|
|
});
|
|
return defer.promise();
|
|
},
|
|
signin: function(email, password) {
|
|
var defer, signinData;
|
|
defer = $.Deferred();
|
|
if (typeof email !== "string" && !password) {
|
|
signinData = email;
|
|
if (typeof signinData.toJson === 'function') {
|
|
signinData = signinData.toJson();
|
|
}
|
|
Materia.API.post('/api/usermanagement/signin?k=' + config.key, signinData).done(function(res) {
|
|
cookieStore.createCookie('oio_auth', JSON.stringify(res.data), res.data.expires_in || 21600);
|
|
return defer.resolve(new UserObject(res.data));
|
|
}).fail(function(err) {
|
|
return defer.reject(err);
|
|
});
|
|
} else {
|
|
Materia.API.post('/api/usermanagement/signin?k=' + config.key, {
|
|
email: email,
|
|
password: password
|
|
}).done(function(res) {
|
|
cookieStore.createCookie('oio_auth', JSON.stringify(res.data), res.data.expires_in || 21600);
|
|
return defer.resolve(new UserObject(res.data));
|
|
}).fail(function(err) {
|
|
return defer.reject(err);
|
|
});
|
|
}
|
|
return defer.promise();
|
|
},
|
|
confirmResetPassword: function(newPassword, sptoken) {
|
|
return Materia.API.post('/api/usermanagement/user/password?k=' + config.key, {
|
|
password: newPassword,
|
|
token: sptoken
|
|
});
|
|
},
|
|
resetPassword: function(email, callback) {
|
|
return Materia.API.post('/api/usermanagement/user/password/reset?k=' + config.key, {
|
|
email: email
|
|
});
|
|
},
|
|
refreshIdentity: function() {
|
|
var defer;
|
|
defer = $.Deferred();
|
|
Materia.API.get('/api/usermanagement/user?k=' + config.key + '&token=' + JSON.parse(cookieStore.readCookie('oio_auth')).token).done(function(res) {
|
|
return defer.resolve(new UserObject(res.data));
|
|
}).fail(function(err) {
|
|
return defer.reject(err);
|
|
});
|
|
return defer.promise();
|
|
},
|
|
getIdentity: function() {
|
|
var user;
|
|
user = cookieStore.readCookie('oio_auth');
|
|
if (!user) {
|
|
return null;
|
|
}
|
|
return new UserObject(JSON.parse(user));
|
|
},
|
|
isLogged: function() {
|
|
var a;
|
|
a = cookieStore.readCookie('oio_auth');
|
|
if (a) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
};
|
|
|
|
},{}],8:[function(require,module,exports){
|
|
(function() {
|
|
var Materia, jquery;
|
|
jquery = require('./tools/jquery-lite.js');
|
|
Materia = require('./lib/core')(window, document, jquery, navigator);
|
|
Materia.extend('OAuth', require('./lib/oauth'));
|
|
Materia.extend('API', require('./lib/api'));
|
|
Materia.extend('User', require('./lib/user'));
|
|
if (typeof angular !== "undefined" && angular !== null) {
|
|
angular.module('oauthio', []).factory('Materia', [
|
|
function() {
|
|
return Materia;
|
|
}
|
|
]).factory('OAuth', [
|
|
function() {
|
|
return Materia.OAuth;
|
|
}
|
|
]).factory('User', [
|
|
function() {
|
|
return Materia.User;
|
|
}
|
|
]);
|
|
}
|
|
window.Materia = Materia;
|
|
window.User = window.Materia.User;
|
|
return window.OAuth = window.Materia.OAuth;
|
|
})();
|
|
|
|
},{"./lib/api":2,"./lib/core":3,"./lib/oauth":4,"./lib/user":7,"./tools/jquery-lite.js":11}],9:[function(require,module,exports){
|
|
"use strict";
|
|
module.exports = {
|
|
init: function(cookies_module, config) {
|
|
this.config = config;
|
|
return this.cookies = cookies_module;
|
|
},
|
|
tryCache: function(OAuth, provider, cache) {
|
|
var e, i, res;
|
|
if (this.cacheEnabled(cache)) {
|
|
cache = this.cookies.readCookie("oauthio_provider_" + provider);
|
|
if (!cache) {
|
|
return false;
|
|
}
|
|
cache = decodeURIComponent(cache);
|
|
}
|
|
if (typeof cache === "string") {
|
|
try {
|
|
cache = JSON.parse(cache);
|
|
} catch (_error) {
|
|
e = _error;
|
|
return false;
|
|
}
|
|
}
|
|
if (typeof cache === "object") {
|
|
res = {};
|
|
for (i in cache) {
|
|
if (i !== "request" && typeof cache[i] !== "function") {
|
|
res[i] = cache[i];
|
|
}
|
|
}
|
|
return OAuth.create(provider, res, cache.request);
|
|
}
|
|
return false;
|
|
},
|
|
storeCache: function(provider, cache) {
|
|
this.cookies.createCookie("oauthio_provider_" + provider, encodeURIComponent(JSON.stringify(cache)), cache.expires_in - 10 || 3600);
|
|
},
|
|
cacheEnabled: function(cache) {
|
|
if (typeof cache === "undefined") {
|
|
return this.config.options.cache;
|
|
}
|
|
return cache;
|
|
}
|
|
};
|
|
|
|
},{}],10:[function(require,module,exports){
|
|
"use strict";
|
|
module.exports = {
|
|
init: function(config, document) {
|
|
this.config = config;
|
|
return this.document = document;
|
|
},
|
|
createCookie: function(name, value, expires) {
|
|
var date;
|
|
this.eraseCookie(name);
|
|
date = new Date();
|
|
date.setTime(date.getTime() + (expires || 1200) * 1000);
|
|
expires = "; expires=" + date.toGMTString();
|
|
this.document.cookie = name + "=" + value + expires + "; path=/";
|
|
},
|
|
readCookie: function(name) {
|
|
var c, ca, i, nameEQ;
|
|
nameEQ = name + "=";
|
|
ca = this.document.cookie.split(";");
|
|
i = 0;
|
|
while (i < ca.length) {
|
|
c = ca[i];
|
|
while (c.charAt(0) === " ") {
|
|
c = c.substring(1, c.length);
|
|
}
|
|
if (c.indexOf(nameEQ) === 0) {
|
|
return c.substring(nameEQ.length, c.length);
|
|
}
|
|
i++;
|
|
}
|
|
return null;
|
|
},
|
|
eraseCookie: function(name) {
|
|
var date;
|
|
date = new Date();
|
|
date.setTime(date.getTime() - 86400000);
|
|
this.document.cookie = name + "=; expires=" + date.toGMTString() + "; path=/";
|
|
}
|
|
};
|
|
|
|
},{}],11:[function(require,module,exports){
|
|
/*!
|
|
* jQuery JavaScript Library v2.1.1 -attributes,-attributes/attr,-attributes/classes,-attributes/prop,-attributes/support,-attributes/val,-css/addGetHookIf,-css/curCSS,-css/defaultDisplay,-css/hiddenVisibleSelectors,-css/support,-css/swap,-css/var,-css/var/cssExpand,-css/var/getStyles,-css/var/isHidden,-css/var/rmargin,-css/var/rnumnonpx,-css,-effects,-effects/Tween,-effects/animatedSelector,-dimensions,-offset,-data/var/data_user,-deprecated,-event/alias,-event/support,-intro,-manipulation/_evalUrl,-manipulation/support,-manipulation/var,-manipulation/var/rcheckableType,-manipulation,-outro,-queue,-queue/delay,-selector-native,-selector-sizzle,-sizzle/dist,-sizzle/dist/sizzle,-sizzle/dist/min,-sizzle/test,-sizzle/test/jquery,-traversing,-traversing/findFilter,-traversing/var/rneedsContext,-traversing/var,-wrap,-exports,-exports/amd
|
|
* http://jquery.com/
|
|
*
|
|
* Includes Sizzle.js
|
|
* http://sizzlejs.com/
|
|
*
|
|
* Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
|
|
* Released under the MIT license
|
|
* http://jquery.org/license
|
|
*
|
|
* Date: 2015-06-29T10:49Z
|
|
*/
|
|
|
|
(function( global, factory ) {
|
|
|
|
if ( typeof module === "object" && typeof module.exports === "object" ) {
|
|
// For CommonJS and CommonJS-like environments where a proper window is present,
|
|
// execute the factory and get jQuery
|
|
// For environments that do not inherently posses a window with a document
|
|
// (such as Node.js), expose a jQuery-making factory as module.exports
|
|
// This accentuates the need for the creation of a real window
|
|
// e.g. var jQuery = require("jquery")(window);
|
|
// See ticket #14549 for more info
|
|
module.exports = global.document ?
|
|
factory( global, true ) :
|
|
function( w ) {
|
|
if ( !w.document ) {
|
|
throw new Error( "jQuery requires a window with a document" );
|
|
}
|
|
return factory( w );
|
|
};
|
|
} else {
|
|
factory( global );
|
|
}
|
|
|
|
// Pass this if window is not defined yet
|
|
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
|
|
|
|
// Can't do this because several apps including ASP.NET trace
|
|
// the stack via arguments.caller.callee and Firefox dies if
|
|
// you try to trace through "use strict" call chains. (#13335)
|
|
// Support: Firefox 18+
|
|
//
|
|
|
|
var arr = [];
|
|
|
|
var slice = arr.slice;
|
|
|
|
var concat = arr.concat;
|
|
|
|
var push = arr.push;
|
|
|
|
var indexOf = arr.indexOf;
|
|
|
|
var class2type = {};
|
|
|
|
var toString = class2type.toString;
|
|
|
|
var hasOwn = class2type.hasOwnProperty;
|
|
|
|
var support = {};
|
|
|
|
|
|
|
|
var
|
|
// Use the correct document accordingly with window argument (sandbox)
|
|
document = window.document,
|
|
|
|
version = "2.1.1 -attributes,-attributes/attr,-attributes/classes,-attributes/prop,-attributes/support,-attributes/val,-css/addGetHookIf,-css/curCSS,-css/defaultDisplay,-css/hiddenVisibleSelectors,-css/support,-css/swap,-css/var,-css/var/cssExpand,-css/var/getStyles,-css/var/isHidden,-css/var/rmargin,-css/var/rnumnonpx,-css,-effects,-effects/Tween,-effects/animatedSelector,-dimensions,-offset,-data/var/data_user,-deprecated,-event/alias,-event/support,-intro,-manipulation/_evalUrl,-manipulation/support,-manipulation/var,-manipulation/var/rcheckableType,-manipulation,-outro,-queue,-queue/delay,-selector-native,-selector-sizzle,-sizzle/dist,-sizzle/dist/sizzle,-sizzle/dist/min,-sizzle/test,-sizzle/test/jquery,-traversing,-traversing/findFilter,-traversing/var/rneedsContext,-traversing/var,-wrap,-exports,-exports/amd",
|
|
|
|
// Define a local copy of jQuery
|
|
jQuery = function( selector, context ) {
|
|
// The jQuery object is actually just the init constructor 'enhanced'
|
|
// Need init if jQuery is called (just allow error to be thrown if not included)
|
|
return new jQuery.fn.init( selector, context );
|
|
},
|
|
|
|
// Support: Android<4.1
|
|
// Make sure we trim BOM and NBSP
|
|
rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
|
|
|
|
// Matches dashed string for camelizing
|
|
rmsPrefix = /^-ms-/,
|
|
rdashAlpha = /-([\da-z])/gi,
|
|
|
|
// Used by jQuery.camelCase as callback to replace()
|
|
fcamelCase = function( all, letter ) {
|
|
return letter.toUpperCase();
|
|
};
|
|
|
|
jQuery.fn = jQuery.prototype = {
|
|
// The current version of jQuery being used
|
|
jquery: version,
|
|
|
|
constructor: jQuery,
|
|
|
|
// Start with an empty selector
|
|
selector: "",
|
|
|
|
// The default length of a jQuery object is 0
|
|
length: 0,
|
|
|
|
toArray: function() {
|
|
return slice.call( this );
|
|
},
|
|
|
|
// Get the Nth element in the matched element set OR
|
|
// Get the whole matched element set as a clean array
|
|
get: function( num ) {
|
|
return num != null ?
|
|
|
|
// Return just the one element from the set
|
|
( num < 0 ? this[ num + this.length ] : this[ num ] ) :
|
|
|
|
// Return all the elements in a clean array
|
|
slice.call( this );
|
|
},
|
|
|
|
// Take an array of elements and push it onto the stack
|
|
// (returning the new matched element set)
|
|
pushStack: function( elems ) {
|
|
|
|
// Build a new jQuery matched element set
|
|
var ret = jQuery.merge( this.constructor(), elems );
|
|
|
|
// Add the old object onto the stack (as a reference)
|
|
ret.prevObject = this;
|
|
ret.context = this.context;
|
|
|
|
// Return the newly-formed element set
|
|
return ret;
|
|
},
|
|
|
|
// Execute a callback for every element in the matched set.
|
|
// (You can seed the arguments with an array of args, but this is
|
|
// only used internally.)
|
|
each: function( callback, args ) {
|
|
return jQuery.each( this, callback, args );
|
|
},
|
|
|
|
map: function( callback ) {
|
|
return this.pushStack( jQuery.map(this, function( elem, i ) {
|
|
return callback.call( elem, i, elem );
|
|
}));
|
|
},
|
|
|
|
slice: function() {
|
|
return this.pushStack( slice.apply( this, arguments ) );
|
|
},
|
|
|
|
first: function() {
|
|
return this.eq( 0 );
|
|
},
|
|
|
|
last: function() {
|
|
return this.eq( -1 );
|
|
},
|
|
|
|
eq: function( i ) {
|
|
var len = this.length,
|
|
j = +i + ( i < 0 ? len : 0 );
|
|
return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
|
|
},
|
|
|
|
end: function() {
|
|
return this.prevObject || this.constructor(null);
|
|
},
|
|
|
|
// For internal use only.
|
|
// Behaves like an Array's method, not like a jQuery method.
|
|
push: push,
|
|
sort: arr.sort,
|
|
splice: arr.splice
|
|
};
|
|
|
|
jQuery.extend = jQuery.fn.extend = function() {
|
|
var options, name, src, copy, copyIsArray, clone,
|
|
target = arguments[0] || {},
|
|
i = 1,
|
|
length = arguments.length,
|
|
deep = false;
|
|
|
|
// Handle a deep copy situation
|
|
if ( typeof target === "boolean" ) {
|
|
deep = target;
|
|
|
|
// skip the boolean and the target
|
|
target = arguments[ i ] || {};
|
|
i++;
|
|
}
|
|
|
|
// Handle case when target is a string or something (possible in deep copy)
|
|
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
|
|
target = {};
|
|
}
|
|
|
|
// extend jQuery itself if only one argument is passed
|
|
if ( i === length ) {
|
|
target = this;
|
|
i--;
|
|
}
|
|
|
|
for ( ; i < length; i++ ) {
|
|
// Only deal with non-null/undefined values
|
|
if ( (options = arguments[ i ]) != null ) {
|
|
// Extend the base object
|
|
for ( name in options ) {
|
|
src = target[ name ];
|
|
copy = options[ name ];
|
|
|
|
// Prevent never-ending loop
|
|
if ( target === copy ) {
|
|
continue;
|
|
}
|
|
|
|
// Recurse if we're merging plain objects or arrays
|
|
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
|
|
if ( copyIsArray ) {
|
|
copyIsArray = false;
|
|
clone = src && jQuery.isArray(src) ? src : [];
|
|
|
|
} else {
|
|
clone = src && jQuery.isPlainObject(src) ? src : {};
|
|
}
|
|
|
|
// Never move original objects, clone them
|
|
target[ name ] = jQuery.extend( deep, clone, copy );
|
|
|
|
// Don't bring in undefined values
|
|
} else if ( copy !== undefined ) {
|
|
target[ name ] = copy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return the modified object
|
|
return target;
|
|
};
|
|
|
|
jQuery.extend({
|
|
// Unique for each copy of jQuery on the page
|
|
expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
|
|
|
|
// Assume jQuery is ready without the ready module
|
|
isReady: true,
|
|
|
|
error: function( msg ) {
|
|
throw new Error( msg );
|
|
},
|
|
|
|
noop: function() {},
|
|
|
|
// See test/unit/core.js for details concerning isFunction.
|
|
// Since version 1.3, DOM methods and functions like alert
|
|
// aren't supported. They return false on IE (#2968).
|
|
isFunction: function( obj ) {
|
|
return jQuery.type(obj) === "function";
|
|
},
|
|
|
|
isArray: Array.isArray,
|
|
|
|
isWindow: function( obj ) {
|
|
return obj != null && obj === obj.window;
|
|
},
|
|
|
|
isNumeric: function( obj ) {
|
|
// parseFloat NaNs numeric-cast false positives (null|true|false|"")
|
|
// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
|
|
// subtraction forces infinities to NaN
|
|
return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0;
|
|
},
|
|
|
|
isPlainObject: function( obj ) {
|
|
// Not plain objects:
|
|
// - Any object or value whose internal [[Class]] property is not "[object Object]"
|
|
// - DOM nodes
|
|
// - window
|
|
if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( obj.constructor &&
|
|
!hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
|
|
return false;
|
|
}
|
|
|
|
// If the function hasn't returned already, we're confident that
|
|
// |obj| is a plain object, created by {} or constructed with new Object
|
|
return true;
|
|
},
|
|
|
|
isEmptyObject: function( obj ) {
|
|
var name;
|
|
for ( name in obj ) {
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
|
|
type: function( obj ) {
|
|
if ( obj == null ) {
|
|
return obj + "";
|
|
}
|
|
// Support: Android < 4.0, iOS < 6 (functionish RegExp)
|
|
return typeof obj === "object" || typeof obj === "function" ?
|
|
class2type[ toString.call(obj) ] || "object" :
|
|
typeof obj;
|
|
},
|
|
|
|
// Evaluates a script in a global context
|
|
globalEval: function( code ) {
|
|
var script,
|
|
indirect = eval;
|
|
|
|
code = jQuery.trim( code );
|
|
|
|
if ( code ) {
|
|
// If the code includes a valid, prologue position
|
|
// strict mode pragma, execute code by injecting a
|
|
// script tag into the document.
|
|
if ( code.indexOf("use strict") === 1 ) {
|
|
script = document.createElement("script");
|
|
script.text = code;
|
|
document.head.appendChild( script ).parentNode.removeChild( script );
|
|
} else {
|
|
// Otherwise, avoid the DOM node creation, insertion
|
|
// and removal by using an indirect global eval
|
|
indirect( code );
|
|
}
|
|
}
|
|
},
|
|
|
|
// Convert dashed to camelCase; used by the css and data modules
|
|
// Microsoft forgot to hump their vendor prefix (#9572)
|
|
camelCase: function( string ) {
|
|
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
|
|
},
|
|
|
|
nodeName: function( elem, name ) {
|
|
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
|
|
},
|
|
|
|
// args is for internal usage only
|
|
each: function( obj, callback, args ) {
|
|
var value,
|
|
i = 0,
|
|
length = obj.length,
|
|
isArray = isArraylike( obj );
|
|
|
|
if ( args ) {
|
|
if ( isArray ) {
|
|
for ( ; i < length; i++ ) {
|
|
value = callback.apply( obj[ i ], args );
|
|
|
|
if ( value === false ) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for ( i in obj ) {
|
|
value = callback.apply( obj[ i ], args );
|
|
|
|
if ( value === false ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// A special, fast, case for the most common use of each
|
|
} else {
|
|
if ( isArray ) {
|
|
for ( ; i < length; i++ ) {
|
|
value = callback.call( obj[ i ], i, obj[ i ] );
|
|
|
|
if ( value === false ) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for ( i in obj ) {
|
|
value = callback.call( obj[ i ], i, obj[ i ] );
|
|
|
|
if ( value === false ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
},
|
|
|
|
// Support: Android<4.1
|
|
trim: function( text ) {
|
|
return text == null ?
|
|
"" :
|
|
( text + "" ).replace( rtrim, "" );
|
|
},
|
|
|
|
// results is for internal usage only
|
|
makeArray: function( arr, results ) {
|
|
var ret = results || [];
|
|
|
|
if ( arr != null ) {
|
|
if ( isArraylike( Object(arr) ) ) {
|
|
jQuery.merge( ret,
|
|
typeof arr === "string" ?
|
|
[ arr ] : arr
|
|
);
|
|
} else {
|
|
push.call( ret, arr );
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
},
|
|
|
|
inArray: function( elem, arr, i ) {
|
|
return arr == null ? -1 : indexOf.call( arr, elem, i );
|
|
},
|
|
|
|
merge: function( first, second ) {
|
|
var len = +second.length,
|
|
j = 0,
|
|
i = first.length;
|
|
|
|
for ( ; j < len; j++ ) {
|
|
first[ i++ ] = second[ j ];
|
|
}
|
|
|
|
first.length = i;
|
|
|
|
return first;
|
|
},
|
|
|
|
grep: function( elems, callback, invert ) {
|
|
var callbackInverse,
|
|
matches = [],
|
|
i = 0,
|
|
length = elems.length,
|
|
callbackExpect = !invert;
|
|
|
|
// Go through the array, only saving the items
|
|
// that pass the validator function
|
|
for ( ; i < length; i++ ) {
|
|
callbackInverse = !callback( elems[ i ], i );
|
|
if ( callbackInverse !== callbackExpect ) {
|
|
matches.push( elems[ i ] );
|
|
}
|
|
}
|
|
|
|
return matches;
|
|
},
|
|
|
|
// arg is for internal usage only
|
|
map: function( elems, callback, arg ) {
|
|
var value,
|
|
i = 0,
|
|
length = elems.length,
|
|
isArray = isArraylike( elems ),
|
|
ret = [];
|
|
|
|
// Go through the array, translating each of the items to their new values
|
|
if ( isArray ) {
|
|
for ( ; i < length; i++ ) {
|
|
value = callback( elems[ i ], i, arg );
|
|
|
|
if ( value != null ) {
|
|
ret.push( value );
|
|
}
|
|
}
|
|
|
|
// Go through every key on the object,
|
|
} else {
|
|
for ( i in elems ) {
|
|
value = callback( elems[ i ], i, arg );
|
|
|
|
if ( value != null ) {
|
|
ret.push( value );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Flatten any nested arrays
|
|
return concat.apply( [], ret );
|
|
},
|
|
|
|
// A global GUID counter for objects
|
|
guid: 1,
|
|
|
|
// Bind a function to a context, optionally partially applying any
|
|
// arguments.
|
|
proxy: function( fn, context ) {
|
|
var tmp, args, proxy;
|
|
|
|
if ( typeof context === "string" ) {
|
|
tmp = fn[ context ];
|
|
context = fn;
|
|
fn = tmp;
|
|
}
|
|
|
|
// Quick check to determine if target is callable, in the spec
|
|
// this throws a TypeError, but we will just return undefined.
|
|
if ( !jQuery.isFunction( fn ) ) {
|
|
return undefined;
|
|
}
|
|
|
|
// Simulated bind
|
|
args = slice.call( arguments, 2 );
|
|
proxy = function() {
|
|
return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
|
|
};
|
|
|
|
// Set the guid of unique handler to the same of original handler, so it can be removed
|
|
proxy.guid = fn.guid = fn.guid || jQuery.guid++;
|
|
|
|
return proxy;
|
|
},
|
|
|
|
now: Date.now,
|
|
|
|
// jQuery.support is not used in Core but other projects attach their
|
|
// properties to it so it needs to exist.
|
|
support: support
|
|
});
|
|
|
|
// Populate the class2type map
|
|
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
|
|
class2type[ "[object " + name + "]" ] = name.toLowerCase();
|
|
});
|
|
|
|
function isArraylike( obj ) {
|
|
var length = obj.length,
|
|
type = jQuery.type( obj );
|
|
|
|
if ( type === "function" || jQuery.isWindow( obj ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( obj.nodeType === 1 && length ) {
|
|
return true;
|
|
}
|
|
|
|
return type === "array" || length === 0 ||
|
|
typeof length === "number" && length > 0 && ( length - 1 ) in obj;
|
|
}
|
|
|
|
var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
|
|
|
|
// Initialize a jQuery object
|
|
|
|
|
|
// A central reference to the root jQuery(document)
|
|
var rootjQuery,
|
|
|
|
// A simple way to check for HTML strings
|
|
// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
|
|
// Strict HTML recognition (#11290: must start with <)
|
|
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
|
|
|
|
init = jQuery.fn.init = function( selector, context ) {
|
|
var match, elem;
|
|
|
|
// HANDLE: $(""), $(null), $(undefined), $(false)
|
|
if ( !selector ) {
|
|
return this;
|
|
}
|
|
|
|
// Handle HTML strings
|
|
if ( typeof selector === "string" ) {
|
|
if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) {
|
|
// Assume that strings that start and end with <> are HTML and skip the regex check
|
|
match = [ null, selector, null ];
|
|
|
|
} else {
|
|
match = rquickExpr.exec( selector );
|
|
}
|
|
|
|
// Match html or make sure no context is specified for #id
|
|
if ( match && (match[1] || !context) ) {
|
|
|
|
// HANDLE: $(html) -> $(array)
|
|
if ( match[1] ) {
|
|
context = context instanceof jQuery ? context[0] : context;
|
|
|
|
// scripts is true for back-compat
|
|
// Intentionally let the error be thrown if parseHTML is not present
|
|
jQuery.merge( this, jQuery.parseHTML(
|
|
match[1],
|
|
context && context.nodeType ? context.ownerDocument || context : document,
|
|
true
|
|
) );
|
|
|
|
// HANDLE: $(html, props)
|
|
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
|
|
for ( match in context ) {
|
|
// Properties of context are called as methods if possible
|
|
if ( jQuery.isFunction( this[ match ] ) ) {
|
|
this[ match ]( context[ match ] );
|
|
|
|
// ...and otherwise set as attributes
|
|
} else {
|
|
this.attr( match, context[ match ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
|
|
// HANDLE: $(#id)
|
|
} else {
|
|
elem = document.getElementById( match[2] );
|
|
|
|
// Check parentNode to catch when Blackberry 4.6 returns
|
|
// nodes that are no longer in the document #6963
|
|
if ( elem && elem.parentNode ) {
|
|
// Inject the element directly into the jQuery object
|
|
this.length = 1;
|
|
this[0] = elem;
|
|
}
|
|
|
|
this.context = document;
|
|
this.selector = selector;
|
|
return this;
|
|
}
|
|
|
|
// HANDLE: $(expr, $(...))
|
|
} else if ( !context || context.jquery ) {
|
|
return ( context || rootjQuery ).find( selector );
|
|
|
|
// HANDLE: $(expr, context)
|
|
// (which is just equivalent to: $(context).find(expr)
|
|
} else {
|
|
return this.constructor( context ).find( selector );
|
|
}
|
|
|
|
// HANDLE: $(DOMElement)
|
|
} else if ( selector.nodeType ) {
|
|
this.context = this[0] = selector;
|
|
this.length = 1;
|
|
return this;
|
|
|
|
// HANDLE: $(function)
|
|
// Shortcut for document ready
|
|
} else if ( jQuery.isFunction( selector ) ) {
|
|
return typeof rootjQuery.ready !== "undefined" ?
|
|
rootjQuery.ready( selector ) :
|
|
// Execute immediately if ready is not present
|
|
selector( jQuery );
|
|
}
|
|
|
|
if ( selector.selector !== undefined ) {
|
|
this.selector = selector.selector;
|
|
this.context = selector.context;
|
|
}
|
|
|
|
return jQuery.makeArray( selector, this );
|
|
};
|
|
|
|
// Give the init function the jQuery prototype for later instantiation
|
|
init.prototype = jQuery.fn;
|
|
|
|
// Initialize central reference
|
|
rootjQuery = jQuery( document );
|
|
var rnotwhite = (/\S+/g);
|
|
|
|
|
|
|
|
// String to Object options format cache
|
|
var optionsCache = {};
|
|
|
|
// Convert String-formatted options into Object-formatted ones and store in cache
|
|
function createOptions( options ) {
|
|
var object = optionsCache[ options ] = {};
|
|
jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
|
|
object[ flag ] = true;
|
|
});
|
|
return object;
|
|
}
|
|
|
|
/*
|
|
* Create a callback list using the following parameters:
|
|
*
|
|
* options: an optional list of space-separated options that will change how
|
|
* the callback list behaves or a more traditional option object
|
|
*
|
|
* By default a callback list will act like an event callback list and can be
|
|
* "fired" multiple times.
|
|
*
|
|
* Possible options:
|
|
*
|
|
* once: will ensure the callback list can only be fired once (like a Deferred)
|
|
*
|
|
* memory: will keep track of previous values and will call any callback added
|
|
* after the list has been fired right away with the latest "memorized"
|
|
* values (like a Deferred)
|
|
*
|
|
* unique: will ensure a callback can only be added once (no duplicate in the list)
|
|
*
|
|
* stopOnFalse: interrupt callings when a callback returns false
|
|
*
|
|
*/
|
|
jQuery.Callbacks = function( options ) {
|
|
|
|
// Convert options from String-formatted to Object-formatted if needed
|
|
// (we check in cache first)
|
|
options = typeof options === "string" ?
|
|
( optionsCache[ options ] || createOptions( options ) ) :
|
|
jQuery.extend( {}, options );
|
|
|
|
var // Last fire value (for non-forgettable lists)
|
|
memory,
|
|
// Flag to know if list was already fired
|
|
fired,
|
|
// Flag to know if list is currently firing
|
|
firing,
|
|
// First callback to fire (used internally by add and fireWith)
|
|
firingStart,
|
|
// End of the loop when firing
|
|
firingLength,
|
|
// Index of currently firing callback (modified by remove if needed)
|
|
firingIndex,
|
|
// Actual callback list
|
|
list = [],
|
|
// Stack of fire calls for repeatable lists
|
|
stack = !options.once && [],
|
|
// Fire callbacks
|
|
fire = function( data ) {
|
|
memory = options.memory && data;
|
|
fired = true;
|
|
firingIndex = firingStart || 0;
|
|
firingStart = 0;
|
|
firingLength = list.length;
|
|
firing = true;
|
|
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
|
|
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
|
|
memory = false; // To prevent further calls using add
|
|
break;
|
|
}
|
|
}
|
|
firing = false;
|
|
if ( list ) {
|
|
if ( stack ) {
|
|
if ( stack.length ) {
|
|
fire( stack.shift() );
|
|
}
|
|
} else if ( memory ) {
|
|
list = [];
|
|
} else {
|
|
self.disable();
|
|
}
|
|
}
|
|
},
|
|
// Actual Callbacks object
|
|
self = {
|
|
// Add a callback or a collection of callbacks to the list
|
|
add: function() {
|
|
if ( list ) {
|
|
// First, we save the current length
|
|
var start = list.length;
|
|
(function add( args ) {
|
|
jQuery.each( args, function( _, arg ) {
|
|
var type = jQuery.type( arg );
|
|
if ( type === "function" ) {
|
|
if ( !options.unique || !self.has( arg ) ) {
|
|
list.push( arg );
|
|
}
|
|
} else if ( arg && arg.length && type !== "string" ) {
|
|
// Inspect recursively
|
|
add( arg );
|
|
}
|
|
});
|
|
})( arguments );
|
|
// Do we need to add the callbacks to the
|
|
// current firing batch?
|
|
if ( firing ) {
|
|
firingLength = list.length;
|
|
// With memory, if we're not firing then
|
|
// we should call right away
|
|
} else if ( memory ) {
|
|
firingStart = start;
|
|
fire( memory );
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
// Remove a callback from the list
|
|
remove: function() {
|
|
if ( list ) {
|
|
jQuery.each( arguments, function( _, arg ) {
|
|
var index;
|
|
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
|
|
list.splice( index, 1 );
|
|
// Handle firing indexes
|
|
if ( firing ) {
|
|
if ( index <= firingLength ) {
|
|
firingLength--;
|
|
}
|
|
if ( index <= firingIndex ) {
|
|
firingIndex--;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return this;
|
|
},
|
|
// Check if a given callback is in the list.
|
|
// If no argument is given, return whether or not list has callbacks attached.
|
|
has: function( fn ) {
|
|
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
|
|
},
|
|
// Remove all callbacks from the list
|
|
empty: function() {
|
|
list = [];
|
|
firingLength = 0;
|
|
return this;
|
|
},
|
|
// Have the list do nothing anymore
|
|
disable: function() {
|
|
list = stack = memory = undefined;
|
|
return this;
|
|
},
|
|
// Is it disabled?
|
|
disabled: function() {
|
|
return !list;
|
|
},
|
|
// Lock the list in its current state
|
|
lock: function() {
|
|
stack = undefined;
|
|
if ( !memory ) {
|
|
self.disable();
|
|
}
|
|
return this;
|
|
},
|
|
// Is it locked?
|
|
locked: function() {
|
|
return !stack;
|
|
},
|
|
// Call all callbacks with the given context and arguments
|
|
fireWith: function( context, args ) {
|
|
if ( list && ( !fired || stack ) ) {
|
|
args = args || [];
|
|
args = [ context, args.slice ? args.slice() : args ];
|
|
if ( firing ) {
|
|
stack.push( args );
|
|
} else {
|
|
fire( args );
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
// Call all the callbacks with the given arguments
|
|
fire: function() {
|
|
self.fireWith( this, arguments );
|
|
return this;
|
|
},
|
|
// To know if the callbacks have already been called at least once
|
|
fired: function() {
|
|
return !!fired;
|
|
}
|
|
};
|
|
|
|
return self;
|
|
};
|
|
|
|
|
|
jQuery.extend({
|
|
|
|
Deferred: function( func ) {
|
|
var tuples = [
|
|
// action, add listener, listener list, final state
|
|
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
|
|
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
|
|
[ "notify", "progress", jQuery.Callbacks("memory") ]
|
|
],
|
|
state = "pending",
|
|
promise = {
|
|
state: function() {
|
|
return state;
|
|
},
|
|
always: function() {
|
|
deferred.done( arguments ).fail( arguments );
|
|
return this;
|
|
},
|
|
then: function( /* fnDone, fnFail, fnProgress */ ) {
|
|
var fns = arguments;
|
|
return jQuery.Deferred(function( newDefer ) {
|
|
jQuery.each( tuples, function( i, tuple ) {
|
|
var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
|
|
// deferred[ done | fail | progress ] for forwarding actions to newDefer
|
|
deferred[ tuple[1] ](function() {
|
|
var returned = fn && fn.apply( this, arguments );
|
|
if ( returned && jQuery.isFunction( returned.promise ) ) {
|
|
returned.promise()
|
|
.done( newDefer.resolve )
|
|
.fail( newDefer.reject )
|
|
.progress( newDefer.notify );
|
|
} else {
|
|
newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
|
|
}
|
|
});
|
|
});
|
|
fns = null;
|
|
}).promise();
|
|
},
|
|
// Get a promise for this deferred
|
|
// If obj is provided, the promise aspect is added to the object
|
|
promise: function( obj ) {
|
|
return obj != null ? jQuery.extend( obj, promise ) : promise;
|
|
}
|
|
},
|
|
deferred = {};
|
|
|
|
// Keep pipe for back-compat
|
|
promise.pipe = promise.then;
|
|
|
|
// Add list-specific methods
|
|
jQuery.each( tuples, function( i, tuple ) {
|
|
var list = tuple[ 2 ],
|
|
stateString = tuple[ 3 ];
|
|
|
|
// promise[ done | fail | progress ] = list.add
|
|
promise[ tuple[1] ] = list.add;
|
|
|
|
// Handle state
|
|
if ( stateString ) {
|
|
list.add(function() {
|
|
// state = [ resolved | rejected ]
|
|
state = stateString;
|
|
|
|
// [ reject_list | resolve_list ].disable; progress_list.lock
|
|
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
|
|
}
|
|
|
|
// deferred[ resolve | reject | notify ]
|
|
deferred[ tuple[0] ] = function() {
|
|
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
|
|
return this;
|
|
};
|
|
deferred[ tuple[0] + "With" ] = list.fireWith;
|
|
});
|
|
|
|
// Make the deferred a promise
|
|
promise.promise( deferred );
|
|
|
|
// Call given func if any
|
|
if ( func ) {
|
|
func.call( deferred, deferred );
|
|
}
|
|
|
|
// All done!
|
|
return deferred;
|
|
},
|
|
|
|
// Deferred helper
|
|
when: function( subordinate /* , ..., subordinateN */ ) {
|
|
var i = 0,
|
|
resolveValues = slice.call( arguments ),
|
|
length = resolveValues.length,
|
|
|
|
// the count of uncompleted subordinates
|
|
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
|
|
|
|
// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
|
|
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
|
|
|
|
// Update function for both resolve and progress values
|
|
updateFunc = function( i, contexts, values ) {
|
|
return function( value ) {
|
|
contexts[ i ] = this;
|
|
values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
|
|
if ( values === progressValues ) {
|
|
deferred.notifyWith( contexts, values );
|
|
} else if ( !( --remaining ) ) {
|
|
deferred.resolveWith( contexts, values );
|
|
}
|
|
};
|
|
},
|
|
|
|
progressValues, progressContexts, resolveContexts;
|
|
|
|
// add listeners to Deferred subordinates; treat others as resolved
|
|
if ( length > 1 ) {
|
|
progressValues = new Array( length );
|
|
progressContexts = new Array( length );
|
|
resolveContexts = new Array( length );
|
|
for ( ; i < length; i++ ) {
|
|
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
|
|
resolveValues[ i ].promise()
|
|
.done( updateFunc( i, resolveContexts, resolveValues ) )
|
|
.fail( deferred.reject )
|
|
.progress( updateFunc( i, progressContexts, progressValues ) );
|
|
} else {
|
|
--remaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we're not waiting on anything, resolve the master
|
|
if ( !remaining ) {
|
|
deferred.resolveWith( resolveContexts, resolveValues );
|
|
}
|
|
|
|
return deferred.promise();
|
|
}
|
|
});
|
|
|
|
|
|
// The deferred used on DOM ready
|
|
var readyList;
|
|
|
|
jQuery.fn.ready = function( fn ) {
|
|
// Add the callback
|
|
jQuery.ready.promise().done( fn );
|
|
|
|
return this;
|
|
};
|
|
|
|
jQuery.extend({
|
|
// Is the DOM ready to be used? Set to true once it occurs.
|
|
isReady: false,
|
|
|
|
// A counter to track how many items to wait for before
|
|
// the ready event fires. See #6781
|
|
readyWait: 1,
|
|
|
|
// Hold (or release) the ready event
|
|
holdReady: function( hold ) {
|
|
if ( hold ) {
|
|
jQuery.readyWait++;
|
|
} else {
|
|
jQuery.ready( true );
|
|
}
|
|
},
|
|
|
|
// Handle when the DOM is ready
|
|
ready: function( wait ) {
|
|
|
|
// Abort if there are pending holds or we're already ready
|
|
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
|
|
return;
|
|
}
|
|
|
|
// Remember that the DOM is ready
|
|
jQuery.isReady = true;
|
|
|
|
// If a normal DOM Ready event fired, decrement, and wait if need be
|
|
if ( wait !== true && --jQuery.readyWait > 0 ) {
|
|
return;
|
|
}
|
|
|
|
// If there are functions bound, to execute
|
|
readyList.resolveWith( document, [ jQuery ] );
|
|
|
|
// Trigger any bound ready events
|
|
if ( jQuery.fn.triggerHandler ) {
|
|
jQuery( document ).triggerHandler( "ready" );
|
|
jQuery( document ).off( "ready" );
|
|
}
|
|
}
|
|
});
|
|
|
|
/**
|
|
* The ready event handler and self cleanup method
|
|
*/
|
|
function completed() {
|
|
document.removeEventListener( "DOMContentLoaded", completed, false );
|
|
window.removeEventListener( "load", completed, false );
|
|
jQuery.ready();
|
|
}
|
|
|
|
jQuery.ready.promise = function( obj ) {
|
|
if ( !readyList ) {
|
|
|
|
readyList = jQuery.Deferred();
|
|
|
|
// Catch cases where $(document).ready() is called after the browser event has already occurred.
|
|
// we once tried to use readyState "interactive" here, but it caused issues like the one
|
|
// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
|
|
if ( document.readyState === "complete" ) {
|
|
// Handle it asynchronously to allow scripts the opportunity to delay ready
|
|
setTimeout( jQuery.ready );
|
|
|
|
} else {
|
|
|
|
// Use the handy event callback
|
|
document.addEventListener( "DOMContentLoaded", completed, false );
|
|
|
|
// A fallback to window.onload, that will always work
|
|
window.addEventListener( "load", completed, false );
|
|
}
|
|
}
|
|
return readyList.promise( obj );
|
|
};
|
|
|
|
// Kick off the DOM ready check even if the user does not
|
|
jQuery.ready.promise();
|
|
|
|
|
|
|
|
|
|
// Multifunctional method to get and set values of a collection
|
|
// The value/s can optionally be executed if it's a function
|
|
var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
|
|
var i = 0,
|
|
len = elems.length,
|
|
bulk = key == null;
|
|
|
|
// Sets many values
|
|
if ( jQuery.type( key ) === "object" ) {
|
|
chainable = true;
|
|
for ( i in key ) {
|
|
jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
|
|
}
|
|
|
|
// Sets one value
|
|
} else if ( value !== undefined ) {
|
|
chainable = true;
|
|
|
|
if ( !jQuery.isFunction( value ) ) {
|
|
raw = true;
|
|
}
|
|
|
|
if ( bulk ) {
|
|
// Bulk operations run against the entire set
|
|
if ( raw ) {
|
|
fn.call( elems, value );
|
|
fn = null;
|
|
|
|
// ...except when executing function values
|
|
} else {
|
|
bulk = fn;
|
|
fn = function( elem, key, value ) {
|
|
return bulk.call( jQuery( elem ), value );
|
|
};
|
|
}
|
|
}
|
|
|
|
if ( fn ) {
|
|
for ( ; i < len; i++ ) {
|
|
fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
return chainable ?
|
|
elems :
|
|
|
|
// Gets
|
|
bulk ?
|
|
fn.call( elems ) :
|
|
len ? fn( elems[0], key ) : emptyGet;
|
|
};
|
|
|
|
|
|
/**
|
|
* Determines whether an object can have data
|
|
*/
|
|
jQuery.acceptData = function( owner ) {
|
|
// Accepts only:
|
|
// - Node
|
|
// - Node.ELEMENT_NODE
|
|
// - Node.DOCUMENT_NODE
|
|
// - Object
|
|
// - Any
|
|
/* jshint -W018 */
|
|
return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
|
|
};
|
|
|
|
|
|
function Data() {
|
|
// Support: Android < 4,
|
|
// Old WebKit does not have Object.preventExtensions/freeze method,
|
|
// return new empty object instead with no [[set]] accessor
|
|
Object.defineProperty( this.cache = {}, 0, {
|
|
get: function() {
|
|
return {};
|
|
}
|
|
});
|
|
|
|
this.expando = jQuery.expando + Math.random();
|
|
}
|
|
|
|
Data.uid = 1;
|
|
Data.accepts = jQuery.acceptData;
|
|
|
|
Data.prototype = {
|
|
key: function( owner ) {
|
|
// We can accept data for non-element nodes in modern browsers,
|
|
// but we should not, see #8335.
|
|
// Always return the key for a frozen object.
|
|
if ( !Data.accepts( owner ) ) {
|
|
return 0;
|
|
}
|
|
|
|
var descriptor = {},
|
|
// Check if the owner object already has a cache key
|
|
unlock = owner[ this.expando ];
|
|
|
|
// If not, create one
|
|
if ( !unlock ) {
|
|
unlock = Data.uid++;
|
|
|
|
// Secure it in a non-enumerable, non-writable property
|
|
try {
|
|
descriptor[ this.expando ] = { value: unlock };
|
|
Object.defineProperties( owner, descriptor );
|
|
|
|
// Support: Android < 4
|
|
// Fallback to a less secure definition
|
|
} catch ( e ) {
|
|
descriptor[ this.expando ] = unlock;
|
|
jQuery.extend( owner, descriptor );
|
|
}
|
|
}
|
|
|
|
// Ensure the cache object
|
|
if ( !this.cache[ unlock ] ) {
|
|
this.cache[ unlock ] = {};
|
|
}
|
|
|
|
return unlock;
|
|
},
|
|
set: function( owner, data, value ) {
|
|
var prop,
|
|
// There may be an unlock assigned to this node,
|
|
// if there is no entry for this "owner", create one inline
|
|
// and set the unlock as though an owner entry had always existed
|
|
unlock = this.key( owner ),
|
|
cache = this.cache[ unlock ];
|
|
|
|
// Handle: [ owner, key, value ] args
|
|
if ( typeof data === "string" ) {
|
|
cache[ data ] = value;
|
|
|
|
// Handle: [ owner, { properties } ] args
|
|
} else {
|
|
// Fresh assignments by object are shallow copied
|
|
if ( jQuery.isEmptyObject( cache ) ) {
|
|
jQuery.extend( this.cache[ unlock ], data );
|
|
// Otherwise, copy the properties one-by-one to the cache object
|
|
} else {
|
|
for ( prop in data ) {
|
|
cache[ prop ] = data[ prop ];
|
|
}
|
|
}
|
|
}
|
|
return cache;
|
|
},
|
|
get: function( owner, key ) {
|
|
// Either a valid cache is found, or will be created.
|
|
// New caches will be created and the unlock returned,
|
|
// allowing direct access to the newly created
|
|
// empty data object. A valid owner object must be provided.
|
|
var cache = this.cache[ this.key( owner ) ];
|
|
|
|
return key === undefined ?
|
|
cache : cache[ key ];
|
|
},
|
|
access: function( owner, key, value ) {
|
|
var stored;
|
|
// In cases where either:
|
|
//
|
|
// 1. No key was specified
|
|
// 2. A string key was specified, but no value provided
|
|
//
|
|
// Take the "read" path and allow the get method to determine
|
|
// which value to return, respectively either:
|
|
//
|
|
// 1. The entire cache object
|
|
// 2. The data stored at the key
|
|
//
|
|
if ( key === undefined ||
|
|
((key && typeof key === "string") && value === undefined) ) {
|
|
|
|
stored = this.get( owner, key );
|
|
|
|
return stored !== undefined ?
|
|
stored : this.get( owner, jQuery.camelCase(key) );
|
|
}
|
|
|
|
// [*]When the key is not a string, or both a key and value
|
|
// are specified, set or extend (existing objects) with either:
|
|
//
|
|
// 1. An object of properties
|
|
// 2. A key and value
|
|
//
|
|
this.set( owner, key, value );
|
|
|
|
// Since the "set" path can have two possible entry points
|
|
// return the expected data based on which path was taken[*]
|
|
return value !== undefined ? value : key;
|
|
},
|
|
remove: function( owner, key ) {
|
|
var i, name, camel,
|
|
unlock = this.key( owner ),
|
|
cache = this.cache[ unlock ];
|
|
|
|
if ( key === undefined ) {
|
|
this.cache[ unlock ] = {};
|
|
|
|
} else {
|
|
// Support array or space separated string of keys
|
|
if ( jQuery.isArray( key ) ) {
|
|
// If "name" is an array of keys...
|
|
// When data is initially created, via ("key", "val") signature,
|
|
// keys will be converted to camelCase.
|
|
// Since there is no way to tell _how_ a key was added, remove
|
|
// both plain key and camelCase key. #12786
|
|
// This will only penalize the array argument path.
|
|
name = key.concat( key.map( jQuery.camelCase ) );
|
|
} else {
|
|
camel = jQuery.camelCase( key );
|
|
// Try the string as a key before any manipulation
|
|
if ( key in cache ) {
|
|
name = [ key, camel ];
|
|
} else {
|
|
// If a key with the spaces exists, use it.
|
|
// Otherwise, create an array by matching non-whitespace
|
|
name = camel;
|
|
name = name in cache ?
|
|
[ name ] : ( name.match( rnotwhite ) || [] );
|
|
}
|
|
}
|
|
|
|
i = name.length;
|
|
while ( i-- ) {
|
|
delete cache[ name[ i ] ];
|
|
}
|
|
}
|
|
},
|
|
hasData: function( owner ) {
|
|
return !jQuery.isEmptyObject(
|
|
this.cache[ owner[ this.expando ] ] || {}
|
|
);
|
|
},
|
|
discard: function( owner ) {
|
|
if ( owner[ this.expando ] ) {
|
|
delete this.cache[ owner[ this.expando ] ];
|
|
}
|
|
}
|
|
};
|
|
var data_priv = new Data();
|
|
|
|
|
|
|
|
/*
|
|
Implementation Summary
|
|
|
|
1. Enforce API surface and semantic compatibility with 1.9.x branch
|
|
2. Improve the module's maintainability by reducing the storage
|
|
paths to a single mechanism.
|
|
3. Use the same single mechanism to support "private" and "user" data.
|
|
4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
|
|
5. Avoid exposing implementation details on user objects (eg. expando properties)
|
|
6. Provide a clear path for implementation upgrade to WeakMap in 2014
|
|
*/
|
|
var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
|
|
rmultiDash = /([A-Z])/g;
|
|
|
|
function dataAttr( elem, key, data ) {
|
|
var name;
|
|
|
|
// If nothing was found internally, try to fetch any
|
|
// data from the HTML5 data-* attribute
|
|
if ( data === undefined && elem.nodeType === 1 ) {
|
|
name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
|
|
data = elem.getAttribute( name );
|
|
|
|
if ( typeof data === "string" ) {
|
|
try {
|
|
data = data === "true" ? true :
|
|
data === "false" ? false :
|
|
data === "null" ? null :
|
|
// Only convert to a number if it doesn't change the string
|
|
+data + "" === data ? +data :
|
|
rbrace.test( data ) ? jQuery.parseJSON( data ) :
|
|
data;
|
|
} catch( e ) {}
|
|
|
|
// Make sure we set the data so it isn't changed later
|
|
data_user.set( elem, key, data );
|
|
} else {
|
|
data = undefined;
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
jQuery.extend({
|
|
hasData: function( elem ) {
|
|
return data_user.hasData( elem ) || data_priv.hasData( elem );
|
|
},
|
|
|
|
data: function( elem, name, data ) {
|
|
return data_user.access( elem, name, data );
|
|
},
|
|
|
|
removeData: function( elem, name ) {
|
|
data_user.remove( elem, name );
|
|
},
|
|
|
|
// TODO: Now that all calls to _data and _removeData have been replaced
|
|
// with direct calls to data_priv methods, these can be deprecated.
|
|
_data: function( elem, name, data ) {
|
|
return data_priv.access( elem, name, data );
|
|
},
|
|
|
|
_removeData: function( elem, name ) {
|
|
data_priv.remove( elem, name );
|
|
}
|
|
});
|
|
|
|
jQuery.fn.extend({
|
|
data: function( key, value ) {
|
|
var i, name, data,
|
|
elem = this[ 0 ],
|
|
attrs = elem && elem.attributes;
|
|
|
|
// Gets all values
|
|
if ( key === undefined ) {
|
|
if ( this.length ) {
|
|
data = data_user.get( elem );
|
|
|
|
if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
|
|
i = attrs.length;
|
|
while ( i-- ) {
|
|
|
|
// Support: IE11+
|
|
// The attrs elements can be null (#14894)
|
|
if ( attrs[ i ] ) {
|
|
name = attrs[ i ].name;
|
|
if ( name.indexOf( "data-" ) === 0 ) {
|
|
name = jQuery.camelCase( name.slice(5) );
|
|
dataAttr( elem, name, data[ name ] );
|
|
}
|
|
}
|
|
}
|
|
data_priv.set( elem, "hasDataAttrs", true );
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
// Sets multiple values
|
|
if ( typeof key === "object" ) {
|
|
return this.each(function() {
|
|
data_user.set( this, key );
|
|
});
|
|
}
|
|
|
|
return access( this, function( value ) {
|
|
var data,
|
|
camelKey = jQuery.camelCase( key );
|
|
|
|
// The calling jQuery object (element matches) is not empty
|
|
// (and therefore has an element appears at this[ 0 ]) and the
|
|
// `value` parameter was not undefined. An empty jQuery object
|
|
// will result in `undefined` for elem = this[ 0 ] which will
|
|
// throw an exception if an attempt to read a data cache is made.
|
|
if ( elem && value === undefined ) {
|
|
// Attempt to get data from the cache
|
|
// with the key as-is
|
|
data = data_user.get( elem, key );
|
|
if ( data !== undefined ) {
|
|
return data;
|
|
}
|
|
|
|
// Attempt to get data from the cache
|
|
// with the key camelized
|
|
data = data_user.get( elem, camelKey );
|
|
if ( data !== undefined ) {
|
|
return data;
|
|
}
|
|
|
|
// Attempt to "discover" the data in
|
|
// HTML5 custom data-* attrs
|
|
data = dataAttr( elem, camelKey, undefined );
|
|
if ( data !== undefined ) {
|
|
return data;
|
|
}
|
|
|
|
// We tried really hard, but the data doesn't exist.
|
|
return;
|
|
}
|
|
|
|
// Set the data...
|
|
this.each(function() {
|
|
// First, attempt to store a copy or reference of any
|
|
// data that might've been store with a camelCased key.
|
|
var data = data_user.get( this, camelKey );
|
|
|
|
// For HTML5 data-* attribute interop, we have to
|
|
// store property names with dashes in a camelCase form.
|
|
// This might not apply to all properties...*
|
|
data_user.set( this, camelKey, value );
|
|
|
|
// *... In the case of properties that might _actually_
|
|
// have dashes, we need to also store a copy of that
|
|
// unchanged property.
|
|
if ( key.indexOf("-") !== -1 && data !== undefined ) {
|
|
data_user.set( this, key, value );
|
|
}
|
|
});
|
|
}, null, value, arguments.length > 1, null, true );
|
|
},
|
|
|
|
removeData: function( key ) {
|
|
return this.each(function() {
|
|
data_user.remove( this, key );
|
|
});
|
|
}
|
|
});
|
|
var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;
|
|
|
|
var strundefined = typeof undefined;
|
|
|
|
|
|
|
|
var
|
|
rkeyEvent = /^key/,
|
|
rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
|
|
rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
|
|
rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
|
|
|
|
function returnTrue() {
|
|
return true;
|
|
}
|
|
|
|
function returnFalse() {
|
|
return false;
|
|
}
|
|
|
|
function safeActiveElement() {
|
|
try {
|
|
return document.activeElement;
|
|
} catch ( err ) { }
|
|
}
|
|
|
|
/*
|
|
* Helper functions for managing events -- not part of the public interface.
|
|
* Props to Dean Edwards' addEvent library for many of the ideas.
|
|
*/
|
|
jQuery.event = {
|
|
|
|
global: {},
|
|
|
|
add: function( elem, types, handler, data, selector ) {
|
|
|
|
var handleObjIn, eventHandle, tmp,
|
|
events, t, handleObj,
|
|
special, handlers, type, namespaces, origType,
|
|
elemData = data_priv.get( elem );
|
|
|
|
// Don't attach events to noData or text/comment nodes (but allow plain objects)
|
|
if ( !elemData ) {
|
|
return;
|
|
}
|
|
|
|
// Caller can pass in an object of custom data in lieu of the handler
|
|
if ( handler.handler ) {
|
|
handleObjIn = handler;
|
|
handler = handleObjIn.handler;
|
|
selector = handleObjIn.selector;
|
|
}
|
|
|
|
// Make sure that the handler has a unique ID, used to find/remove it later
|
|
if ( !handler.guid ) {
|
|
handler.guid = jQuery.guid++;
|
|
}
|
|
|
|
// Init the element's event structure and main handler, if this is the first
|
|
if ( !(events = elemData.events) ) {
|
|
events = elemData.events = {};
|
|
}
|
|
if ( !(eventHandle = elemData.handle) ) {
|
|
eventHandle = elemData.handle = function( e ) {
|
|
// Discard the second event of a jQuery.event.trigger() and
|
|
// when an event is called after a page has unloaded
|
|
return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ?
|
|
jQuery.event.dispatch.apply( elem, arguments ) : undefined;
|
|
};
|
|
}
|
|
|
|
// Handle multiple events separated by a space
|
|
types = ( types || "" ).match( rnotwhite ) || [ "" ];
|
|
t = types.length;
|
|
while ( t-- ) {
|
|
tmp = rtypenamespace.exec( types[t] ) || [];
|
|
type = origType = tmp[1];
|
|
namespaces = ( tmp[2] || "" ).split( "." ).sort();
|
|
|
|
// There *must* be a type, no attaching namespace-only handlers
|
|
if ( !type ) {
|
|
continue;
|
|
}
|
|
|
|
// If event changes its type, use the special event handlers for the changed type
|
|
special = jQuery.event.special[ type ] || {};
|
|
|
|
// If selector defined, determine special event api type, otherwise given type
|
|
type = ( selector ? special.delegateType : special.bindType ) || type;
|
|
|
|
// Update special based on newly reset type
|
|
special = jQuery.event.special[ type ] || {};
|
|
|
|
// handleObj is passed to all event handlers
|
|
handleObj = jQuery.extend({
|
|
type: type,
|
|
origType: origType,
|
|
data: data,
|
|
handler: handler,
|
|
guid: handler.guid,
|
|
selector: selector,
|
|
needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
|
|
namespace: namespaces.join(".")
|
|
}, handleObjIn );
|
|
|
|
// Init the event handler queue if we're the first
|
|
if ( !(handlers = events[ type ]) ) {
|
|
handlers = events[ type ] = [];
|
|
handlers.delegateCount = 0;
|
|
|
|
// Only use addEventListener if the special events handler returns false
|
|
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
|
|
if ( elem.addEventListener ) {
|
|
elem.addEventListener( type, eventHandle, false );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( special.add ) {
|
|
special.add.call( elem, handleObj );
|
|
|
|
if ( !handleObj.handler.guid ) {
|
|
handleObj.handler.guid = handler.guid;
|
|
}
|
|
}
|
|
|
|
// Add to the element's handler list, delegates in front
|
|
if ( selector ) {
|
|
handlers.splice( handlers.delegateCount++, 0, handleObj );
|
|
} else {
|
|
handlers.push( handleObj );
|
|
}
|
|
|
|
// Keep track of which events have ever been used, for event optimization
|
|
jQuery.event.global[ type ] = true;
|
|
}
|
|
|
|
},
|
|
|
|
// Detach an event or set of events from an element
|
|
remove: function( elem, types, handler, selector, mappedTypes ) {
|
|
|
|
var j, origCount, tmp,
|
|
events, t, handleObj,
|
|
special, handlers, type, namespaces, origType,
|
|
elemData = data_priv.hasData( elem ) && data_priv.get( elem );
|
|
|
|
if ( !elemData || !(events = elemData.events) ) {
|
|
return;
|
|
}
|
|
|
|
// Once for each type.namespace in types; type may be omitted
|
|
types = ( types || "" ).match( rnotwhite ) || [ "" ];
|
|
t = types.length;
|
|
while ( t-- ) {
|
|
tmp = rtypenamespace.exec( types[t] ) || [];
|
|
type = origType = tmp[1];
|
|
namespaces = ( tmp[2] || "" ).split( "." ).sort();
|
|
|
|
// Unbind all events (on this namespace, if provided) for the element
|
|
if ( !type ) {
|
|
for ( type in events ) {
|
|
jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
|
|
}
|
|
continue;
|
|
}
|
|
|
|
special = jQuery.event.special[ type ] || {};
|
|
type = ( selector ? special.delegateType : special.bindType ) || type;
|
|
handlers = events[ type ] || [];
|
|
tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
|
|
|
|
// Remove matching events
|
|
origCount = j = handlers.length;
|
|
while ( j-- ) {
|
|
handleObj = handlers[ j ];
|
|
|
|
if ( ( mappedTypes || origType === handleObj.origType ) &&
|
|
( !handler || handler.guid === handleObj.guid ) &&
|
|
( !tmp || tmp.test( handleObj.namespace ) ) &&
|
|
( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
|
|
handlers.splice( j, 1 );
|
|
|
|
if ( handleObj.selector ) {
|
|
handlers.delegateCount--;
|
|
}
|
|
if ( special.remove ) {
|
|
special.remove.call( elem, handleObj );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove generic event handler if we removed something and no more handlers exist
|
|
// (avoids potential for endless recursion during removal of special event handlers)
|
|
if ( origCount && !handlers.length ) {
|
|
if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
|
|
jQuery.removeEvent( elem, type, elemData.handle );
|
|
}
|
|
|
|
delete events[ type ];
|
|
}
|
|
}
|
|
|
|
// Remove the expando if it's no longer used
|
|
if ( jQuery.isEmptyObject( events ) ) {
|
|
delete elemData.handle;
|
|
data_priv.remove( elem, "events" );
|
|
}
|
|
},
|
|
|
|
trigger: function( event, data, elem, onlyHandlers ) {
|
|
|
|
var i, cur, tmp, bubbleType, ontype, handle, special,
|
|
eventPath = [ elem || document ],
|
|
type = hasOwn.call( event, "type" ) ? event.type : event,
|
|
namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
|
|
|
|
cur = tmp = elem = elem || document;
|
|
|
|
// Don't do events on text and comment nodes
|
|
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
|
|
return;
|
|
}
|
|
|
|
// focus/blur morphs to focusin/out; ensure we're not firing them right now
|
|
if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( type.indexOf(".") >= 0 ) {
|
|
// Namespaced trigger; create a regexp to match event type in handle()
|
|
namespaces = type.split(".");
|
|
type = namespaces.shift();
|
|
namespaces.sort();
|
|
}
|
|
ontype = type.indexOf(":") < 0 && "on" + type;
|
|
|
|
// Caller can pass in a jQuery.Event object, Object, or just an event type string
|
|
event = event[ jQuery.expando ] ?
|
|
event :
|
|
new jQuery.Event( type, typeof event === "object" && event );
|
|
|
|
// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
|
|
event.isTrigger = onlyHandlers ? 2 : 3;
|
|
event.namespace = namespaces.join(".");
|
|
event.namespace_re = event.namespace ?
|
|
new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
|
|
null;
|
|
|
|
// Clean up the event in case it is being reused
|
|
event.result = undefined;
|
|
if ( !event.target ) {
|
|
event.target = elem;
|
|
}
|
|
|
|
// Clone any incoming data and prepend the event, creating the handler arg list
|
|
data = data == null ?
|
|
[ event ] :
|
|
jQuery.makeArray( data, [ event ] );
|
|
|
|
// Allow special events to draw outside the lines
|
|
special = jQuery.event.special[ type ] || {};
|
|
if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
|
|
return;
|
|
}
|
|
|
|
// Determine event propagation path in advance, per W3C events spec (#9951)
|
|
// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
|
|
if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
|
|
|
|
bubbleType = special.delegateType || type;
|
|
if ( !rfocusMorph.test( bubbleType + type ) ) {
|
|
cur = cur.parentNode;
|
|
}
|
|
for ( ; cur; cur = cur.parentNode ) {
|
|
eventPath.push( cur );
|
|
tmp = cur;
|
|
}
|
|
|
|
// Only add window if we got to document (e.g., not plain obj or detached DOM)
|
|
if ( tmp === (elem.ownerDocument || document) ) {
|
|
eventPath.push( tmp.defaultView || tmp.parentWindow || window );
|
|
}
|
|
}
|
|
|
|
// Fire handlers on the event path
|
|
i = 0;
|
|
while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
|
|
|
|
event.type = i > 1 ?
|
|
bubbleType :
|
|
special.bindType || type;
|
|
|
|
// jQuery handler
|
|
handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" );
|
|
if ( handle ) {
|
|
handle.apply( cur, data );
|
|
}
|
|
|
|
// Native handler
|
|
handle = ontype && cur[ ontype ];
|
|
if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
|
|
event.result = handle.apply( cur, data );
|
|
if ( event.result === false ) {
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
event.type = type;
|
|
|
|
// If nobody prevented the default action, do it now
|
|
if ( !onlyHandlers && !event.isDefaultPrevented() ) {
|
|
|
|
if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
|
|
jQuery.acceptData( elem ) ) {
|
|
|
|
// Call a native DOM method on the target with the same name name as the event.
|
|
// Don't do default actions on window, that's where global variables be (#6170)
|
|
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
|
|
|
|
// Don't re-trigger an onFOO event when we call its FOO() method
|
|
tmp = elem[ ontype ];
|
|
|
|
if ( tmp ) {
|
|
elem[ ontype ] = null;
|
|
}
|
|
|
|
// Prevent re-triggering of the same event, since we already bubbled it above
|
|
jQuery.event.triggered = type;
|
|
elem[ type ]();
|
|
jQuery.event.triggered = undefined;
|
|
|
|
if ( tmp ) {
|
|
elem[ ontype ] = tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return event.result;
|
|
},
|
|
|
|
dispatch: function( event ) {
|
|
|
|
// Make a writable jQuery.Event from the native event object
|
|
event = jQuery.event.fix( event );
|
|
|
|
var i, j, ret, matched, handleObj,
|
|
handlerQueue = [],
|
|
args = slice.call( arguments ),
|
|
handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],
|
|
special = jQuery.event.special[ event.type ] || {};
|
|
|
|
// Use the fix-ed jQuery.Event rather than the (read-only) native event
|
|
args[0] = event;
|
|
event.delegateTarget = this;
|
|
|
|
// Call the preDispatch hook for the mapped type, and let it bail if desired
|
|
if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
|
|
return;
|
|
}
|
|
|
|
// Determine handlers
|
|
handlerQueue = jQuery.event.handlers.call( this, event, handlers );
|
|
|
|
// Run delegates first; they may want to stop propagation beneath us
|
|
i = 0;
|
|
while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
|
|
event.currentTarget = matched.elem;
|
|
|
|
j = 0;
|
|
while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
|
|
|
|
// Triggered event must either 1) have no namespace, or
|
|
// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
|
|
if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
|
|
|
|
event.handleObj = handleObj;
|
|
event.data = handleObj.data;
|
|
|
|
ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
|
|
.apply( matched.elem, args );
|
|
|
|
if ( ret !== undefined ) {
|
|
if ( (event.result = ret) === false ) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Call the postDispatch hook for the mapped type
|
|
if ( special.postDispatch ) {
|
|
special.postDispatch.call( this, event );
|
|
}
|
|
|
|
return event.result;
|
|
},
|
|
|
|
handlers: function( event, handlers ) {
|
|
var i, matches, sel, handleObj,
|
|
handlerQueue = [],
|
|
delegateCount = handlers.delegateCount,
|
|
cur = event.target;
|
|
|
|
// Find delegate handlers
|
|
// Black-hole SVG <use> instance trees (#13180)
|
|
// Avoid non-left-click bubbling in Firefox (#3861)
|
|
if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
|
|
|
|
for ( ; cur !== this; cur = cur.parentNode || this ) {
|
|
|
|
// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
|
|
if ( cur.disabled !== true || event.type !== "click" ) {
|
|
matches = [];
|
|
for ( i = 0; i < delegateCount; i++ ) {
|
|
handleObj = handlers[ i ];
|
|
|
|
// Don't conflict with Object.prototype properties (#13203)
|
|
sel = handleObj.selector + " ";
|
|
|
|
if ( matches[ sel ] === undefined ) {
|
|
matches[ sel ] = handleObj.needsContext ?
|
|
jQuery( sel, this ).index( cur ) >= 0 :
|
|
jQuery.find( sel, this, null, [ cur ] ).length;
|
|
}
|
|
if ( matches[ sel ] ) {
|
|
matches.push( handleObj );
|
|
}
|
|
}
|
|
if ( matches.length ) {
|
|
handlerQueue.push({ elem: cur, handlers: matches });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the remaining (directly-bound) handlers
|
|
if ( delegateCount < handlers.length ) {
|
|
handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
|
|
}
|
|
|
|
return handlerQueue;
|
|
},
|
|
|
|
// Includes some event props shared by KeyEvent and MouseEvent
|
|
props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
|
|
|
|
fixHooks: {},
|
|
|
|
keyHooks: {
|
|
props: "char charCode key keyCode".split(" "),
|
|
filter: function( event, original ) {
|
|
|
|
// Add which for key events
|
|
if ( event.which == null ) {
|
|
event.which = original.charCode != null ? original.charCode : original.keyCode;
|
|
}
|
|
|
|
return event;
|
|
}
|
|
},
|
|
|
|
mouseHooks: {
|
|
props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
|
|
filter: function( event, original ) {
|
|
var eventDoc, doc, body,
|
|
button = original.button;
|
|
|
|
// Calculate pageX/Y if missing and clientX/Y available
|
|
if ( event.pageX == null && original.clientX != null ) {
|
|
eventDoc = event.target.ownerDocument || document;
|
|
doc = eventDoc.documentElement;
|
|
body = eventDoc.body;
|
|
|
|
event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
|
|
event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
|
|
}
|
|
|
|
// Add which for click: 1 === left; 2 === middle; 3 === right
|
|
// Note: button is not normalized, so don't use it
|
|
if ( !event.which && button !== undefined ) {
|
|
event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
|
|
}
|
|
|
|
return event;
|
|
}
|
|
},
|
|
|
|
fix: function( event ) {
|
|
if ( event[ jQuery.expando ] ) {
|
|
return event;
|
|
}
|
|
|
|
// Create a writable copy of the event object and normalize some properties
|
|
var i, prop, copy,
|
|
type = event.type,
|
|
originalEvent = event,
|
|
fixHook = this.fixHooks[ type ];
|
|
|
|
if ( !fixHook ) {
|
|
this.fixHooks[ type ] = fixHook =
|
|
rmouseEvent.test( type ) ? this.mouseHooks :
|
|
rkeyEvent.test( type ) ? this.keyHooks :
|
|
{};
|
|
}
|
|
copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
|
|
|
|
event = new jQuery.Event( originalEvent );
|
|
|
|
i = copy.length;
|
|
while ( i-- ) {
|
|
prop = copy[ i ];
|
|
event[ prop ] = originalEvent[ prop ];
|
|
}
|
|
|
|
// Support: Cordova 2.5 (WebKit) (#13255)
|
|
// All events should have a target; Cordova deviceready doesn't
|
|
if ( !event.target ) {
|
|
event.target = document;
|
|
}
|
|
|
|
// Support: Safari 6.0+, Chrome < 28
|
|
// Target should not be a text node (#504, #13143)
|
|
if ( event.target.nodeType === 3 ) {
|
|
event.target = event.target.parentNode;
|
|
}
|
|
|
|
return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
|
|
},
|
|
|
|
special: {
|
|
load: {
|
|
// Prevent triggered image.load events from bubbling to window.load
|
|
noBubble: true
|
|
},
|
|
focus: {
|
|
// Fire native event if possible so blur/focus sequence is correct
|
|
trigger: function() {
|
|
if ( this !== safeActiveElement() && this.focus ) {
|
|
this.focus();
|
|
return false;
|
|
}
|
|
},
|
|
delegateType: "focusin"
|
|
},
|
|
blur: {
|
|
trigger: function() {
|
|
if ( this === safeActiveElement() && this.blur ) {
|
|
this.blur();
|
|
return false;
|
|
}
|
|
},
|
|
delegateType: "focusout"
|
|
},
|
|
click: {
|
|
// For checkbox, fire native event so checked state will be right
|
|
trigger: function() {
|
|
if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
|
|
this.click();
|
|
return false;
|
|
}
|
|
},
|
|
|
|
// For cross-browser consistency, don't fire native .click() on links
|
|
_default: function( event ) {
|
|
return jQuery.nodeName( event.target, "a" );
|
|
}
|
|
},
|
|
|
|
beforeunload: {
|
|
postDispatch: function( event ) {
|
|
|
|
// Support: Firefox 20+
|
|
// Firefox doesn't alert if the returnValue field is not set.
|
|
if ( event.result !== undefined && event.originalEvent ) {
|
|
event.originalEvent.returnValue = event.result;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
simulate: function( type, elem, event, bubble ) {
|
|
// Piggyback on a donor event to simulate a different one.
|
|
// Fake originalEvent to avoid donor's stopPropagation, but if the
|
|
// simulated event prevents default then we do the same on the donor.
|
|
var e = jQuery.extend(
|
|
new jQuery.Event(),
|
|
event,
|
|
{
|
|
type: type,
|
|
isSimulated: true,
|
|
originalEvent: {}
|
|
}
|
|
);
|
|
if ( bubble ) {
|
|
jQuery.event.trigger( e, null, elem );
|
|
} else {
|
|
jQuery.event.dispatch.call( elem, e );
|
|
}
|
|
if ( e.isDefaultPrevented() ) {
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
};
|
|
|
|
jQuery.removeEvent = function( elem, type, handle ) {
|
|
if ( elem.removeEventListener ) {
|
|
elem.removeEventListener( type, handle, false );
|
|
}
|
|
};
|
|
|
|
jQuery.Event = function( src, props ) {
|
|
// Allow instantiation without the 'new' keyword
|
|
if ( !(this instanceof jQuery.Event) ) {
|
|
return new jQuery.Event( src, props );
|
|
}
|
|
|
|
// Event object
|
|
if ( src && src.type ) {
|
|
this.originalEvent = src;
|
|
this.type = src.type;
|
|
|
|
// Events bubbling up the document may have been marked as prevented
|
|
// by a handler lower down the tree; reflect the correct value.
|
|
this.isDefaultPrevented = src.defaultPrevented ||
|
|
src.defaultPrevented === undefined &&
|
|
// Support: Android < 4.0
|
|
src.returnValue === false ?
|
|
returnTrue :
|
|
returnFalse;
|
|
|
|
// Event type
|
|
} else {
|
|
this.type = src;
|
|
}
|
|
|
|
// Put explicitly provided properties onto the event object
|
|
if ( props ) {
|
|
jQuery.extend( this, props );
|
|
}
|
|
|
|
// Create a timestamp if incoming event doesn't have one
|
|
this.timeStamp = src && src.timeStamp || jQuery.now();
|
|
|
|
// Mark it as fixed
|
|
this[ jQuery.expando ] = true;
|
|
};
|
|
|
|
// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
|
|
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
|
|
jQuery.Event.prototype = {
|
|
isDefaultPrevented: returnFalse,
|
|
isPropagationStopped: returnFalse,
|
|
isImmediatePropagationStopped: returnFalse,
|
|
|
|
preventDefault: function() {
|
|
var e = this.originalEvent;
|
|
|
|
this.isDefaultPrevented = returnTrue;
|
|
|
|
if ( e && e.preventDefault ) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
stopPropagation: function() {
|
|
var e = this.originalEvent;
|
|
|
|
this.isPropagationStopped = returnTrue;
|
|
|
|
if ( e && e.stopPropagation ) {
|
|
e.stopPropagation();
|
|
}
|
|
},
|
|
stopImmediatePropagation: function() {
|
|
var e = this.originalEvent;
|
|
|
|
this.isImmediatePropagationStopped = returnTrue;
|
|
|
|
if ( e && e.stopImmediatePropagation ) {
|
|
e.stopImmediatePropagation();
|
|
}
|
|
|
|
this.stopPropagation();
|
|
}
|
|
};
|
|
|
|
// Create mouseenter/leave events using mouseover/out and event-time checks
|
|
// Support: Chrome 15+
|
|
jQuery.each({
|
|
mouseenter: "mouseover",
|
|
mouseleave: "mouseout",
|
|
pointerenter: "pointerover",
|
|
pointerleave: "pointerout"
|
|
}, function( orig, fix ) {
|
|
jQuery.event.special[ orig ] = {
|
|
delegateType: fix,
|
|
bindType: fix,
|
|
|
|
handle: function( event ) {
|
|
var ret,
|
|
target = this,
|
|
related = event.relatedTarget,
|
|
handleObj = event.handleObj;
|
|
|
|
// For mousenter/leave call the handler if related is outside the target.
|
|
// NB: No relatedTarget if the mouse left/entered the browser window
|
|
if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
|
|
event.type = handleObj.origType;
|
|
ret = handleObj.handler.apply( this, arguments );
|
|
event.type = fix;
|
|
}
|
|
return ret;
|
|
}
|
|
};
|
|
});
|
|
|
|
// Create "bubbling" focus and blur events
|
|
// Support: Firefox, Chrome, Safari
|
|
if ( !support.focusinBubbles ) {
|
|
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
|
|
|
|
// Attach a single capturing handler on the document while someone wants focusin/focusout
|
|
var handler = function( event ) {
|
|
jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
|
|
};
|
|
|
|
jQuery.event.special[ fix ] = {
|
|
setup: function() {
|
|
var doc = this.ownerDocument || this,
|
|
attaches = data_priv.access( doc, fix );
|
|
|
|
if ( !attaches ) {
|
|
doc.addEventListener( orig, handler, true );
|
|
}
|
|
data_priv.access( doc, fix, ( attaches || 0 ) + 1 );
|
|
},
|
|
teardown: function() {
|
|
var doc = this.ownerDocument || this,
|
|
attaches = data_priv.access( doc, fix ) - 1;
|
|
|
|
if ( !attaches ) {
|
|
doc.removeEventListener( orig, handler, true );
|
|
data_priv.remove( doc, fix );
|
|
|
|
} else {
|
|
data_priv.access( doc, fix, attaches );
|
|
}
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
jQuery.fn.extend({
|
|
|
|
on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
|
|
var origFn, type;
|
|
|
|
// Types can be a map of types/handlers
|
|
if ( typeof types === "object" ) {
|
|
// ( types-Object, selector, data )
|
|
if ( typeof selector !== "string" ) {
|
|
// ( types-Object, data )
|
|
data = data || selector;
|
|
selector = undefined;
|
|
}
|
|
for ( type in types ) {
|
|
this.on( type, selector, data, types[ type ], one );
|
|
}
|
|
return this;
|
|
}
|
|
|
|
if ( data == null && fn == null ) {
|
|
// ( types, fn )
|
|
fn = selector;
|
|
data = selector = undefined;
|
|
} else if ( fn == null ) {
|
|
if ( typeof selector === "string" ) {
|
|
// ( types, selector, fn )
|
|
fn = data;
|
|
data = undefined;
|
|
} else {
|
|
// ( types, data, fn )
|
|
fn = data;
|
|
data = selector;
|
|
selector = undefined;
|
|
}
|
|
}
|
|
if ( fn === false ) {
|
|
fn = returnFalse;
|
|
} else if ( !fn ) {
|
|
return this;
|
|
}
|
|
|
|
if ( one === 1 ) {
|
|
origFn = fn;
|
|
fn = function( event ) {
|
|
// Can use an empty set, since event contains the info
|
|
jQuery().off( event );
|
|
return origFn.apply( this, arguments );
|
|
};
|
|
// Use same guid so caller can remove using origFn
|
|
fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
|
|
}
|
|
return this.each( function() {
|
|
jQuery.event.add( this, types, fn, data, selector );
|
|
});
|
|
},
|
|
one: function( types, selector, data, fn ) {
|
|
return this.on( types, selector, data, fn, 1 );
|
|
},
|
|
off: function( types, selector, fn ) {
|
|
var handleObj, type;
|
|
if ( types && types.preventDefault && types.handleObj ) {
|
|
// ( event ) dispatched jQuery.Event
|
|
handleObj = types.handleObj;
|
|
jQuery( types.delegateTarget ).off(
|
|
handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
|
|
handleObj.selector,
|
|
handleObj.handler
|
|
);
|
|
return this;
|
|
}
|
|
if ( typeof types === "object" ) {
|
|
// ( types-object [, selector] )
|
|
for ( type in types ) {
|
|
this.off( type, selector, types[ type ] );
|
|
}
|
|
return this;
|
|
}
|
|
if ( selector === false || typeof selector === "function" ) {
|
|
// ( types [, fn] )
|
|
fn = selector;
|
|
selector = undefined;
|
|
}
|
|
if ( fn === false ) {
|
|
fn = returnFalse;
|
|
}
|
|
return this.each(function() {
|
|
jQuery.event.remove( this, types, fn, selector );
|
|
});
|
|
},
|
|
|
|
trigger: function( type, data ) {
|
|
return this.each(function() {
|
|
jQuery.event.trigger( type, data, this );
|
|
});
|
|
},
|
|
triggerHandler: function( type, data ) {
|
|
var elem = this[0];
|
|
if ( elem ) {
|
|
return jQuery.event.trigger( type, data, elem, true );
|
|
}
|
|
}
|
|
});
|
|
var nonce = jQuery.now();
|
|
|
|
var rquery = (/\?/);
|
|
|
|
|
|
|
|
// Support: Android 2.3
|
|
// Workaround failure to string-cast null input
|
|
jQuery.parseJSON = function( data ) {
|
|
return JSON.parse( data + "" );
|
|
};
|
|
|
|
|
|
// Cross-browser xml parsing
|
|
jQuery.parseXML = function( data ) {
|
|
var xml, tmp;
|
|
if ( !data || typeof data !== "string" ) {
|
|
return null;
|
|
}
|
|
|
|
// Support: IE9
|
|
try {
|
|
tmp = new DOMParser();
|
|
xml = tmp.parseFromString( data, "text/xml" );
|
|
} catch ( e ) {
|
|
xml = undefined;
|
|
}
|
|
|
|
if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
|
|
jQuery.error( "Invalid XML: " + data );
|
|
}
|
|
return xml;
|
|
};
|
|
|
|
|
|
var
|
|
// Document location
|
|
ajaxLocParts,
|
|
ajaxLocation,
|
|
|
|
rhash = /#.*$/,
|
|
rts = /([?&])_=[^&]*/,
|
|
rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
|
|
// #7653, #8125, #8152: local protocol detection
|
|
rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
|
|
rnoContent = /^(?:GET|HEAD)$/,
|
|
rprotocol = /^\/\//,
|
|
rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,
|
|
|
|
/* Prefilters
|
|
* 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
|
|
* 2) These are called:
|
|
* - BEFORE asking for a transport
|
|
* - AFTER param serialization (s.data is a string if s.processData is true)
|
|
* 3) key is the dataType
|
|
* 4) the catchall symbol "*" can be used
|
|
* 5) execution will start with transport dataType and THEN continue down to "*" if needed
|
|
*/
|
|
prefilters = {},
|
|
|
|
/* Transports bindings
|
|
* 1) key is the dataType
|
|
* 2) the catchall symbol "*" can be used
|
|
* 3) selection will start with transport dataType and THEN go to "*" if needed
|
|
*/
|
|
transports = {},
|
|
|
|
// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
|
|
allTypes = "*/".concat("*");
|
|
|
|
// #8138, IE may throw an exception when accessing
|
|
// a field from window.location if document.domain has been set
|
|
try {
|
|
ajaxLocation = location.href;
|
|
} catch( e ) {
|
|
// Use the href attribute of an A element
|
|
// since IE will modify it given document.location
|
|
ajaxLocation = document.createElement( "a" );
|
|
ajaxLocation.href = "";
|
|
ajaxLocation = ajaxLocation.href;
|
|
}
|
|
|
|
// Segment location into parts
|
|
ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
|
|
|
|
// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
|
|
function addToPrefiltersOrTransports( structure ) {
|
|
|
|
// dataTypeExpression is optional and defaults to "*"
|
|
return function( dataTypeExpression, func ) {
|
|
|
|
if ( typeof dataTypeExpression !== "string" ) {
|
|
func = dataTypeExpression;
|
|
dataTypeExpression = "*";
|
|
}
|
|
|
|
var dataType,
|
|
i = 0,
|
|
dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
|
|
|
|
if ( jQuery.isFunction( func ) ) {
|
|
// For each dataType in the dataTypeExpression
|
|
while ( (dataType = dataTypes[i++]) ) {
|
|
// Prepend if requested
|
|
if ( dataType[0] === "+" ) {
|
|
dataType = dataType.slice( 1 ) || "*";
|
|
(structure[ dataType ] = structure[ dataType ] || []).unshift( func );
|
|
|
|
// Otherwise append
|
|
} else {
|
|
(structure[ dataType ] = structure[ dataType ] || []).push( func );
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// Base inspection function for prefilters and transports
|
|
function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
|
|
|
|
var inspected = {},
|
|
seekingTransport = ( structure === transports );
|
|
|
|
function inspect( dataType ) {
|
|
var selected;
|
|
inspected[ dataType ] = true;
|
|
jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
|
|
var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
|
|
if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
|
|
options.dataTypes.unshift( dataTypeOrTransport );
|
|
inspect( dataTypeOrTransport );
|
|
return false;
|
|
} else if ( seekingTransport ) {
|
|
return !( selected = dataTypeOrTransport );
|
|
}
|
|
});
|
|
return selected;
|
|
}
|
|
|
|
return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
|
|
}
|
|
|
|
// A special extend for ajax options
|
|
// that takes "flat" options (not to be deep extended)
|
|
// Fixes #9887
|
|
function ajaxExtend( target, src ) {
|
|
var key, deep,
|
|
flatOptions = jQuery.ajaxSettings.flatOptions || {};
|
|
|
|
for ( key in src ) {
|
|
if ( src[ key ] !== undefined ) {
|
|
( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
|
|
}
|
|
}
|
|
if ( deep ) {
|
|
jQuery.extend( true, target, deep );
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
/* Handles responses to an ajax request:
|
|
* - finds the right dataType (mediates between content-type and expected dataType)
|
|
* - returns the corresponding response
|
|
*/
|
|
function ajaxHandleResponses( s, jqXHR, responses ) {
|
|
|
|
var ct, type, finalDataType, firstDataType,
|
|
contents = s.contents,
|
|
dataTypes = s.dataTypes;
|
|
|
|
// Remove auto dataType and get content-type in the process
|
|
while ( dataTypes[ 0 ] === "*" ) {
|
|
dataTypes.shift();
|
|
if ( ct === undefined ) {
|
|
ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
|
|
}
|
|
}
|
|
|
|
// Check if we're dealing with a known content-type
|
|
if ( ct ) {
|
|
for ( type in contents ) {
|
|
if ( contents[ type ] && contents[ type ].test( ct ) ) {
|
|
dataTypes.unshift( type );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check to see if we have a response for the expected dataType
|
|
if ( dataTypes[ 0 ] in responses ) {
|
|
finalDataType = dataTypes[ 0 ];
|
|
} else {
|
|
// Try convertible dataTypes
|
|
for ( type in responses ) {
|
|
if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
|
|
finalDataType = type;
|
|
break;
|
|
}
|
|
if ( !firstDataType ) {
|
|
firstDataType = type;
|
|
}
|
|
}
|
|
// Or just use first one
|
|
finalDataType = finalDataType || firstDataType;
|
|
}
|
|
|
|
// If we found a dataType
|
|
// We add the dataType to the list if needed
|
|
// and return the corresponding response
|
|
if ( finalDataType ) {
|
|
if ( finalDataType !== dataTypes[ 0 ] ) {
|
|
dataTypes.unshift( finalDataType );
|
|
}
|
|
return responses[ finalDataType ];
|
|
}
|
|
}
|
|
|
|
/* Chain conversions given the request and the original response
|
|
* Also sets the responseXXX fields on the jqXHR instance
|
|
*/
|
|
function ajaxConvert( s, response, jqXHR, isSuccess ) {
|
|
var conv2, current, conv, tmp, prev,
|
|
converters = {},
|
|
// Work with a copy of dataTypes in case we need to modify it for conversion
|
|
dataTypes = s.dataTypes.slice();
|
|
|
|
// Create converters map with lowercased keys
|
|
if ( dataTypes[ 1 ] ) {
|
|
for ( conv in s.converters ) {
|
|
converters[ conv.toLowerCase() ] = s.converters[ conv ];
|
|
}
|
|
}
|
|
|
|
current = dataTypes.shift();
|
|
|
|
// Convert to each sequential dataType
|
|
while ( current ) {
|
|
|
|
if ( s.responseFields[ current ] ) {
|
|
jqXHR[ s.responseFields[ current ] ] = response;
|
|
}
|
|
|
|
// Apply the dataFilter if provided
|
|
if ( !prev && isSuccess && s.dataFilter ) {
|
|
response = s.dataFilter( response, s.dataType );
|
|
}
|
|
|
|
prev = current;
|
|
current = dataTypes.shift();
|
|
|
|
if ( current ) {
|
|
|
|
// There's only work to do if current dataType is non-auto
|
|
if ( current === "*" ) {
|
|
|
|
current = prev;
|
|
|
|
// Convert response if prev dataType is non-auto and differs from current
|
|
} else if ( prev !== "*" && prev !== current ) {
|
|
|
|
// Seek a direct converter
|
|
conv = converters[ prev + " " + current ] || converters[ "* " + current ];
|
|
|
|
// If none found, seek a pair
|
|
if ( !conv ) {
|
|
for ( conv2 in converters ) {
|
|
|
|
// If conv2 outputs current
|
|
tmp = conv2.split( " " );
|
|
if ( tmp[ 1 ] === current ) {
|
|
|
|
// If prev can be converted to accepted input
|
|
conv = converters[ prev + " " + tmp[ 0 ] ] ||
|
|
converters[ "* " + tmp[ 0 ] ];
|
|
if ( conv ) {
|
|
// Condense equivalence converters
|
|
if ( conv === true ) {
|
|
conv = converters[ conv2 ];
|
|
|
|
// Otherwise, insert the intermediate dataType
|
|
} else if ( converters[ conv2 ] !== true ) {
|
|
current = tmp[ 0 ];
|
|
dataTypes.unshift( tmp[ 1 ] );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply converter (if not an equivalence)
|
|
if ( conv !== true ) {
|
|
|
|
// Unless errors are allowed to bubble, catch and return them
|
|
if ( conv && s[ "throws" ] ) {
|
|
response = conv( response );
|
|
} else {
|
|
try {
|
|
response = conv( response );
|
|
} catch ( e ) {
|
|
return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return { state: "success", data: response };
|
|
}
|
|
|
|
jQuery.extend({
|
|
|
|
// Counter for holding the number of active queries
|
|
active: 0,
|
|
|
|
// Last-Modified header cache for next request
|
|
lastModified: {},
|
|
etag: {},
|
|
|
|
ajaxSettings: {
|
|
url: ajaxLocation,
|
|
type: "GET",
|
|
isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
|
|
global: true,
|
|
processData: true,
|
|
async: true,
|
|
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
|
|
/*
|
|
timeout: 0,
|
|
data: null,
|
|
dataType: null,
|
|
username: null,
|
|
password: null,
|
|
cache: null,
|
|
throws: false,
|
|
traditional: false,
|
|
headers: {},
|
|
*/
|
|
|
|
accepts: {
|
|
"*": allTypes,
|
|
text: "text/plain",
|
|
html: "text/html",
|
|
xml: "application/xml, text/xml",
|
|
json: "application/json, text/javascript"
|
|
},
|
|
|
|
contents: {
|
|
xml: /xml/,
|
|
html: /html/,
|
|
json: /json/
|
|
},
|
|
|
|
responseFields: {
|
|
xml: "responseXML",
|
|
text: "responseText",
|
|
json: "responseJSON"
|
|
},
|
|
|
|
// Data converters
|
|
// Keys separate source (or catchall "*") and destination types with a single space
|
|
converters: {
|
|
|
|
// Convert anything to text
|
|
"* text": String,
|
|
|
|
// Text to html (true = no transformation)
|
|
"text html": true,
|
|
|
|
// Evaluate text as a json expression
|
|
"text json": jQuery.parseJSON,
|
|
|
|
// Parse text as xml
|
|
"text xml": jQuery.parseXML
|
|
},
|
|
|
|
// For options that shouldn't be deep extended:
|
|
// you can add your own custom options here if
|
|
// and when you create one that shouldn't be
|
|
// deep extended (see ajaxExtend)
|
|
flatOptions: {
|
|
url: true,
|
|
context: true
|
|
}
|
|
},
|
|
|
|
// Creates a full fledged settings object into target
|
|
// with both ajaxSettings and settings fields.
|
|
// If target is omitted, writes into ajaxSettings.
|
|
ajaxSetup: function( target, settings ) {
|
|
return settings ?
|
|
|
|
// Building a settings object
|
|
ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
|
|
|
|
// Extending ajaxSettings
|
|
ajaxExtend( jQuery.ajaxSettings, target );
|
|
},
|
|
|
|
ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
|
|
ajaxTransport: addToPrefiltersOrTransports( transports ),
|
|
|
|
// Main method
|
|
ajax: function( url, options ) {
|
|
|
|
// If url is an object, simulate pre-1.5 signature
|
|
if ( typeof url === "object" ) {
|
|
options = url;
|
|
url = undefined;
|
|
}
|
|
|
|
// Force options to be an object
|
|
options = options || {};
|
|
|
|
var transport,
|
|
// URL without anti-cache param
|
|
cacheURL,
|
|
// Response headers
|
|
responseHeadersString,
|
|
responseHeaders,
|
|
// timeout handle
|
|
timeoutTimer,
|
|
// Cross-domain detection vars
|
|
parts,
|
|
// To know if global events are to be dispatched
|
|
fireGlobals,
|
|
// Loop variable
|
|
i,
|
|
// Create the final options object
|
|
s = jQuery.ajaxSetup( {}, options ),
|
|
// Callbacks context
|
|
callbackContext = s.context || s,
|
|
// Context for global events is callbackContext if it is a DOM node or jQuery collection
|
|
globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
|
|
jQuery( callbackContext ) :
|
|
jQuery.event,
|
|
// Deferreds
|
|
deferred = jQuery.Deferred(),
|
|
completeDeferred = jQuery.Callbacks("once memory"),
|
|
// Status-dependent callbacks
|
|
statusCode = s.statusCode || {},
|
|
// Headers (they are sent all at once)
|
|
requestHeaders = {},
|
|
requestHeadersNames = {},
|
|
// The jqXHR state
|
|
state = 0,
|
|
// Default abort message
|
|
strAbort = "canceled",
|
|
// Fake xhr
|
|
jqXHR = {
|
|
readyState: 0,
|
|
|
|
// Builds headers hashtable if needed
|
|
getResponseHeader: function( key ) {
|
|
var match;
|
|
if ( state === 2 ) {
|
|
if ( !responseHeaders ) {
|
|
responseHeaders = {};
|
|
while ( (match = rheaders.exec( responseHeadersString )) ) {
|
|
responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
|
|
}
|
|
}
|
|
match = responseHeaders[ key.toLowerCase() ];
|
|
}
|
|
return match == null ? null : match;
|
|
},
|
|
|
|
// Raw string
|
|
getAllResponseHeaders: function() {
|
|
return state === 2 ? responseHeadersString : null;
|
|
},
|
|
|
|
// Caches the header
|
|
setRequestHeader: function( name, value ) {
|
|
var lname = name.toLowerCase();
|
|
if ( !state ) {
|
|
name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
|
|
requestHeaders[ name ] = value;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Overrides response content-type header
|
|
overrideMimeType: function( type ) {
|
|
if ( !state ) {
|
|
s.mimeType = type;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Status-dependent callbacks
|
|
statusCode: function( map ) {
|
|
var code;
|
|
if ( map ) {
|
|
if ( state < 2 ) {
|
|
for ( code in map ) {
|
|
// Lazy-add the new callback in a way that preserves old ones
|
|
statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
|
|
}
|
|
} else {
|
|
// Execute the appropriate callbacks
|
|
jqXHR.always( map[ jqXHR.status ] );
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Cancel the request
|
|
abort: function( statusText ) {
|
|
var finalText = statusText || strAbort;
|
|
if ( transport ) {
|
|
transport.abort( finalText );
|
|
}
|
|
done( 0, finalText );
|
|
return this;
|
|
}
|
|
};
|
|
|
|
// Attach deferreds
|
|
deferred.promise( jqXHR ).complete = completeDeferred.add;
|
|
jqXHR.success = jqXHR.done;
|
|
jqXHR.error = jqXHR.fail;
|
|
|
|
// Remove hash character (#7531: and string promotion)
|
|
// Add protocol if not provided (prefilters might expect it)
|
|
// Handle falsy url in the settings object (#10093: consistency with old signature)
|
|
// We also use the url parameter if available
|
|
s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" )
|
|
.replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
|
|
|
|
// Alias method option to type as per ticket #12004
|
|
s.type = options.method || options.type || s.method || s.type;
|
|
|
|
// Extract dataTypes list
|
|
s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
|
|
|
|
// A cross-domain request is in order when we have a protocol:host:port mismatch
|
|
if ( s.crossDomain == null ) {
|
|
parts = rurl.exec( s.url.toLowerCase() );
|
|
s.crossDomain = !!( parts &&
|
|
( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
|
|
( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
|
|
( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
|
|
);
|
|
}
|
|
|
|
// Convert data if not already a string
|
|
if ( s.data && s.processData && typeof s.data !== "string" ) {
|
|
s.data = jQuery.param( s.data, s.traditional );
|
|
}
|
|
|
|
// Apply prefilters
|
|
inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
|
|
|
|
// If request was aborted inside a prefilter, stop there
|
|
if ( state === 2 ) {
|
|
return jqXHR;
|
|
}
|
|
|
|
// We can fire global events as of now if asked to
|
|
fireGlobals = s.global;
|
|
|
|
// Watch for a new set of requests
|
|
if ( fireGlobals && jQuery.active++ === 0 ) {
|
|
jQuery.event.trigger("ajaxStart");
|
|
}
|
|
|
|
// Uppercase the type
|
|
s.type = s.type.toUpperCase();
|
|
|
|
// Determine if request has content
|
|
s.hasContent = !rnoContent.test( s.type );
|
|
|
|
// Save the URL in case we're toying with the If-Modified-Since
|
|
// and/or If-None-Match header later on
|
|
cacheURL = s.url;
|
|
|
|
// More options handling for requests with no content
|
|
if ( !s.hasContent ) {
|
|
|
|
// If data is available, append data to url
|
|
if ( s.data ) {
|
|
cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
|
|
// #9682: remove data so that it's not used in an eventual retry
|
|
delete s.data;
|
|
}
|
|
|
|
// Add anti-cache in url if needed
|
|
if ( s.cache === false ) {
|
|
s.url = rts.test( cacheURL ) ?
|
|
|
|
// If there is already a '_' parameter, set its value
|
|
cacheURL.replace( rts, "$1_=" + nonce++ ) :
|
|
|
|
// Otherwise add one to the end
|
|
cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
|
|
}
|
|
}
|
|
|
|
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
|
|
if ( s.ifModified ) {
|
|
if ( jQuery.lastModified[ cacheURL ] ) {
|
|
jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
|
|
}
|
|
if ( jQuery.etag[ cacheURL ] ) {
|
|
jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
|
|
}
|
|
}
|
|
|
|
// Set the correct header, if data is being sent
|
|
if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
|
|
jqXHR.setRequestHeader( "Content-Type", s.contentType );
|
|
}
|
|
|
|
// Set the Accepts header for the server, depending on the dataType
|
|
jqXHR.setRequestHeader(
|
|
"Accept",
|
|
s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
|
|
s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
|
|
s.accepts[ "*" ]
|
|
);
|
|
|
|
// Check for headers option
|
|
for ( i in s.headers ) {
|
|
jqXHR.setRequestHeader( i, s.headers[ i ] );
|
|
}
|
|
|
|
// Allow custom headers/mimetypes and early abort
|
|
if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
|
|
// Abort if not done already and return
|
|
return jqXHR.abort();
|
|
}
|
|
|
|
// aborting is no longer a cancellation
|
|
strAbort = "abort";
|
|
|
|
// Install callbacks on deferreds
|
|
for ( i in { success: 1, error: 1, complete: 1 } ) {
|
|
jqXHR[ i ]( s[ i ] );
|
|
}
|
|
|
|
// Get transport
|
|
transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
|
|
|
|
// If no transport, we auto-abort
|
|
if ( !transport ) {
|
|
done( -1, "No Transport" );
|
|
} else {
|
|
jqXHR.readyState = 1;
|
|
|
|
// Send global event
|
|
if ( fireGlobals ) {
|
|
globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
|
|
}
|
|
// Timeout
|
|
if ( s.async && s.timeout > 0 ) {
|
|
timeoutTimer = setTimeout(function() {
|
|
jqXHR.abort("timeout");
|
|
}, s.timeout );
|
|
}
|
|
|
|
try {
|
|
state = 1;
|
|
transport.send( requestHeaders, done );
|
|
} catch ( e ) {
|
|
// Propagate exception as error if not done
|
|
if ( state < 2 ) {
|
|
done( -1, e );
|
|
// Simply rethrow otherwise
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Callback for when everything is done
|
|
function done( status, nativeStatusText, responses, headers ) {
|
|
var isSuccess, success, error, response, modified,
|
|
statusText = nativeStatusText;
|
|
|
|
// Called once
|
|
if ( state === 2 ) {
|
|
return;
|
|
}
|
|
|
|
// State is "done" now
|
|
state = 2;
|
|
|
|
// Clear timeout if it exists
|
|
if ( timeoutTimer ) {
|
|
clearTimeout( timeoutTimer );
|
|
}
|
|
|
|
// Dereference transport for early garbage collection
|
|
// (no matter how long the jqXHR object will be used)
|
|
transport = undefined;
|
|
|
|
// Cache response headers
|
|
responseHeadersString = headers || "";
|
|
|
|
// Set readyState
|
|
jqXHR.readyState = status > 0 ? 4 : 0;
|
|
|
|
// Determine if successful
|
|
isSuccess = status >= 200 && status < 300 || status === 304;
|
|
|
|
// Get response data
|
|
if ( responses ) {
|
|
response = ajaxHandleResponses( s, jqXHR, responses );
|
|
}
|
|
|
|
// Convert no matter what (that way responseXXX fields are always set)
|
|
response = ajaxConvert( s, response, jqXHR, isSuccess );
|
|
|
|
// If successful, handle type chaining
|
|
if ( isSuccess ) {
|
|
|
|
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
|
|
if ( s.ifModified ) {
|
|
modified = jqXHR.getResponseHeader("Last-Modified");
|
|
if ( modified ) {
|
|
jQuery.lastModified[ cacheURL ] = modified;
|
|
}
|
|
modified = jqXHR.getResponseHeader("etag");
|
|
if ( modified ) {
|
|
jQuery.etag[ cacheURL ] = modified;
|
|
}
|
|
}
|
|
|
|
// if no content
|
|
if ( status === 204 || s.type === "HEAD" ) {
|
|
statusText = "nocontent";
|
|
|
|
// if not modified
|
|
} else if ( status === 304 ) {
|
|
statusText = "notmodified";
|
|
|
|
// If we have data, let's convert it
|
|
} else {
|
|
statusText = response.state;
|
|
success = response.data;
|
|
error = response.error;
|
|
isSuccess = !error;
|
|
}
|
|
} else {
|
|
// We extract error from statusText
|
|
// then normalize statusText and status for non-aborts
|
|
error = statusText;
|
|
if ( status || !statusText ) {
|
|
statusText = "error";
|
|
if ( status < 0 ) {
|
|
status = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set data for the fake xhr object
|
|
jqXHR.status = status;
|
|
jqXHR.statusText = ( nativeStatusText || statusText ) + "";
|
|
|
|
// Success/Error
|
|
if ( isSuccess ) {
|
|
deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
|
|
} else {
|
|
deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
|
|
}
|
|
|
|
// Status-dependent callbacks
|
|
jqXHR.statusCode( statusCode );
|
|
statusCode = undefined;
|
|
|
|
if ( fireGlobals ) {
|
|
globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
|
|
[ jqXHR, s, isSuccess ? success : error ] );
|
|
}
|
|
|
|
// Complete
|
|
completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
|
|
|
|
if ( fireGlobals ) {
|
|
globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
|
|
// Handle the global AJAX counter
|
|
if ( !( --jQuery.active ) ) {
|
|
jQuery.event.trigger("ajaxStop");
|
|
}
|
|
}
|
|
}
|
|
|
|
return jqXHR;
|
|
},
|
|
|
|
getJSON: function( url, data, callback ) {
|
|
return jQuery.get( url, data, callback, "json" );
|
|
},
|
|
|
|
getScript: function( url, callback ) {
|
|
return jQuery.get( url, undefined, callback, "script" );
|
|
}
|
|
});
|
|
|
|
jQuery.each( [ "get", "post" ], function( i, method ) {
|
|
jQuery[ method ] = function( url, data, callback, type ) {
|
|
// shift arguments if data argument was omitted
|
|
if ( jQuery.isFunction( data ) ) {
|
|
type = type || callback;
|
|
callback = data;
|
|
data = undefined;
|
|
}
|
|
|
|
return jQuery.ajax({
|
|
url: url,
|
|
type: method,
|
|
dataType: type,
|
|
data: data,
|
|
success: callback
|
|
});
|
|
};
|
|
});
|
|
|
|
// Attach a bunch of functions for handling common AJAX events
|
|
jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
|
|
jQuery.fn[ type ] = function( fn ) {
|
|
return this.on( type, fn );
|
|
};
|
|
});
|
|
|
|
|
|
var r20 = /%20/g,
|
|
rbracket = /\[\]$/,
|
|
rCRLF = /\r?\n/g,
|
|
rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
|
|
rsubmittable = /^(?:input|select|textarea|keygen)/i;
|
|
|
|
function buildParams( prefix, obj, traditional, add ) {
|
|
var name;
|
|
|
|
if ( jQuery.isArray( obj ) ) {
|
|
// Serialize array item.
|
|
jQuery.each( obj, function( i, v ) {
|
|
if ( traditional || rbracket.test( prefix ) ) {
|
|
// Treat each array item as a scalar.
|
|
add( prefix, v );
|
|
|
|
} else {
|
|
// Item is non-scalar (array or object), encode its numeric index.
|
|
buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
|
|
}
|
|
});
|
|
|
|
} else if ( !traditional && jQuery.type( obj ) === "object" ) {
|
|
// Serialize object item.
|
|
for ( name in obj ) {
|
|
buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
|
|
}
|
|
|
|
} else {
|
|
// Serialize scalar item.
|
|
add( prefix, obj );
|
|
}
|
|
}
|
|
|
|
// Serialize an array of form elements or a set of
|
|
// key/values into a query string
|
|
jQuery.param = function( a, traditional ) {
|
|
var prefix,
|
|
s = [],
|
|
add = function( key, value ) {
|
|
// If value is a function, invoke it and return its value
|
|
value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
|
|
s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
|
|
};
|
|
|
|
// Set traditional to true for jQuery <= 1.3.2 behavior.
|
|
if ( traditional === undefined ) {
|
|
traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
|
|
}
|
|
|
|
// If an array was passed in, assume that it is an array of form elements.
|
|
if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
|
|
// Serialize the form elements
|
|
jQuery.each( a, function() {
|
|
add( this.name, this.value );
|
|
});
|
|
|
|
} else {
|
|
// If traditional, encode the "old" way (the way 1.3.2 or older
|
|
// did it), otherwise encode params recursively.
|
|
for ( prefix in a ) {
|
|
buildParams( prefix, a[ prefix ], traditional, add );
|
|
}
|
|
}
|
|
|
|
// Return the resulting serialization
|
|
return s.join( "&" ).replace( r20, "+" );
|
|
};
|
|
|
|
jQuery.fn.extend({
|
|
serialize: function() {
|
|
return jQuery.param( this.serializeArray() );
|
|
},
|
|
serializeArray: function() {
|
|
return this.map(function() {
|
|
// Can add propHook for "elements" to filter or add form elements
|
|
var elements = jQuery.prop( this, "elements" );
|
|
return elements ? jQuery.makeArray( elements ) : this;
|
|
})
|
|
.filter(function() {
|
|
var type = this.type;
|
|
|
|
// Use .is( ":disabled" ) so that fieldset[disabled] works
|
|
return this.name && !jQuery( this ).is( ":disabled" ) &&
|
|
rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
|
|
( this.checked || !rcheckableType.test( type ) );
|
|
})
|
|
.map(function( i, elem ) {
|
|
var val = jQuery( this ).val();
|
|
|
|
return val == null ?
|
|
null :
|
|
jQuery.isArray( val ) ?
|
|
jQuery.map( val, function( val ) {
|
|
return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
|
|
}) :
|
|
{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
|
|
}).get();
|
|
}
|
|
});
|
|
|
|
|
|
jQuery.ajaxSettings.xhr = function() {
|
|
try {
|
|
return new XMLHttpRequest();
|
|
} catch( e ) {}
|
|
};
|
|
|
|
var xhrId = 0,
|
|
xhrCallbacks = {},
|
|
xhrSuccessStatus = {
|
|
// file protocol always yields status code 0, assume 200
|
|
0: 200,
|
|
// Support: IE9
|
|
// #1450: sometimes IE returns 1223 when it should be 204
|
|
1223: 204
|
|
},
|
|
xhrSupported = jQuery.ajaxSettings.xhr();
|
|
|
|
// Support: IE9
|
|
// Open requests must be manually aborted on unload (#5280)
|
|
if ( window.ActiveXObject ) {
|
|
jQuery( window ).on( "unload", function() {
|
|
for ( var key in xhrCallbacks ) {
|
|
xhrCallbacks[ key ]();
|
|
}
|
|
});
|
|
}
|
|
|
|
support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
|
|
support.ajax = xhrSupported = !!xhrSupported;
|
|
|
|
jQuery.ajaxTransport(function( options ) {
|
|
var callback;
|
|
|
|
// Cross domain only allowed if supported through XMLHttpRequest
|
|
if ( support.cors || xhrSupported && !options.crossDomain ) {
|
|
return {
|
|
send: function( headers, complete ) {
|
|
var i,
|
|
xhr = options.xhr(),
|
|
id = ++xhrId;
|
|
|
|
xhr.open( options.type, options.url, options.async, options.username, options.password );
|
|
|
|
// Apply custom fields if provided
|
|
if ( options.xhrFields ) {
|
|
for ( i in options.xhrFields ) {
|
|
xhr[ i ] = options.xhrFields[ i ];
|
|
}
|
|
}
|
|
|
|
// Override mime type if needed
|
|
if ( options.mimeType && xhr.overrideMimeType ) {
|
|
xhr.overrideMimeType( options.mimeType );
|
|
}
|
|
|
|
// 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 ( !options.crossDomain && !headers["X-Requested-With"] ) {
|
|
headers["X-Requested-With"] = "XMLHttpRequest";
|
|
}
|
|
|
|
// Set headers
|
|
for ( i in headers ) {
|
|
xhr.setRequestHeader( i, headers[ i ] );
|
|
}
|
|
|
|
// Callback
|
|
callback = function( type ) {
|
|
return function() {
|
|
if ( callback ) {
|
|
delete xhrCallbacks[ id ];
|
|
callback = xhr.onload = xhr.onerror = null;
|
|
|
|
if ( type === "abort" ) {
|
|
xhr.abort();
|
|
} else if ( type === "error" ) {
|
|
complete(
|
|
// file: protocol always yields status 0; see #8605, #14207
|
|
xhr.status,
|
|
xhr.statusText
|
|
);
|
|
} else {
|
|
complete(
|
|
xhrSuccessStatus[ xhr.status ] || xhr.status,
|
|
xhr.statusText,
|
|
// Support: IE9
|
|
// Accessing binary-data responseText throws an exception
|
|
// (#11426)
|
|
typeof xhr.responseText === "string" ? {
|
|
text: xhr.responseText
|
|
} : undefined,
|
|
xhr.getAllResponseHeaders()
|
|
);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
// Listen to events
|
|
xhr.onload = callback();
|
|
xhr.onerror = callback("error");
|
|
|
|
// Create the abort callback
|
|
callback = xhrCallbacks[ id ] = callback("abort");
|
|
|
|
try {
|
|
// Do send the request (this may raise an exception)
|
|
xhr.send( options.hasContent && options.data || null );
|
|
} catch ( e ) {
|
|
// #14683: Only rethrow if this hasn't been notified as an error yet
|
|
if ( callback ) {
|
|
throw e;
|
|
}
|
|
}
|
|
},
|
|
|
|
abort: function() {
|
|
if ( callback ) {
|
|
callback();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
// Install script dataType
|
|
jQuery.ajaxSetup({
|
|
accepts: {
|
|
script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
|
|
},
|
|
contents: {
|
|
script: /(?:java|ecma)script/
|
|
},
|
|
converters: {
|
|
"text script": function( text ) {
|
|
jQuery.globalEval( text );
|
|
return text;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Handle cache's special case and crossDomain
|
|
jQuery.ajaxPrefilter( "script", function( s ) {
|
|
if ( s.cache === undefined ) {
|
|
s.cache = false;
|
|
}
|
|
if ( s.crossDomain ) {
|
|
s.type = "GET";
|
|
}
|
|
});
|
|
|
|
// Bind script tag hack transport
|
|
jQuery.ajaxTransport( "script", function( s ) {
|
|
// This transport only deals with cross domain requests
|
|
if ( s.crossDomain ) {
|
|
var script, callback;
|
|
return {
|
|
send: function( _, complete ) {
|
|
script = jQuery("<script>").prop({
|
|
async: true,
|
|
charset: s.scriptCharset,
|
|
src: s.url
|
|
}).on(
|
|
"load error",
|
|
callback = function( evt ) {
|
|
script.remove();
|
|
callback = null;
|
|
if ( evt ) {
|
|
complete( evt.type === "error" ? 404 : 200, evt.type );
|
|
}
|
|
}
|
|
);
|
|
document.head.appendChild( script[ 0 ] );
|
|
},
|
|
abort: function() {
|
|
if ( callback ) {
|
|
callback();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
var oldCallbacks = [],
|
|
rjsonp = /(=)\?(?=&|$)|\?\?/;
|
|
|
|
// Default jsonp settings
|
|
jQuery.ajaxSetup({
|
|
jsonp: "callback",
|
|
jsonpCallback: function() {
|
|
var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
|
|
this[ callback ] = true;
|
|
return callback;
|
|
}
|
|
});
|
|
|
|
// Detect, normalize options and install callbacks for jsonp requests
|
|
jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
|
|
|
|
var callbackName, overwritten, responseContainer,
|
|
jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
|
|
"url" :
|
|
typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
|
|
);
|
|
|
|
// Handle iff the expected data type is "jsonp" or we have a parameter to set
|
|
if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
|
|
|
|
// Get callback name, remembering preexisting value associated with it
|
|
callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
|
|
s.jsonpCallback() :
|
|
s.jsonpCallback;
|
|
|
|
// Insert callback into url or form data
|
|
if ( jsonProp ) {
|
|
s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
|
|
} else if ( s.jsonp !== false ) {
|
|
s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
|
|
}
|
|
|
|
// Use data converter to retrieve json after script execution
|
|
s.converters["script json"] = function() {
|
|
if ( !responseContainer ) {
|
|
jQuery.error( callbackName + " was not called" );
|
|
}
|
|
return responseContainer[ 0 ];
|
|
};
|
|
|
|
// force json dataType
|
|
s.dataTypes[ 0 ] = "json";
|
|
|
|
// Install callback
|
|
overwritten = window[ callbackName ];
|
|
window[ callbackName ] = function() {
|
|
responseContainer = arguments;
|
|
};
|
|
|
|
// Clean-up function (fires after converters)
|
|
jqXHR.always(function() {
|
|
// Restore preexisting value
|
|
window[ callbackName ] = overwritten;
|
|
|
|
// Save back as free
|
|
if ( s[ callbackName ] ) {
|
|
// make sure that re-using the options doesn't screw things around
|
|
s.jsonpCallback = originalSettings.jsonpCallback;
|
|
|
|
// save the callback name for future use
|
|
oldCallbacks.push( callbackName );
|
|
}
|
|
|
|
// Call if it was a function and we have a response
|
|
if ( responseContainer && jQuery.isFunction( overwritten ) ) {
|
|
overwritten( responseContainer[ 0 ] );
|
|
}
|
|
|
|
responseContainer = overwritten = undefined;
|
|
});
|
|
|
|
// Delegate to script
|
|
return "script";
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
// data: string of html
|
|
// context (optional): If specified, the fragment will be created in this context, defaults to document
|
|
// keepScripts (optional): If true, will include scripts passed in the html string
|
|
jQuery.parseHTML = function( data, context, keepScripts ) {
|
|
if ( !data || typeof data !== "string" ) {
|
|
return null;
|
|
}
|
|
if ( typeof context === "boolean" ) {
|
|
keepScripts = context;
|
|
context = false;
|
|
}
|
|
context = context || document;
|
|
|
|
var parsed = rsingleTag.exec( data ),
|
|
scripts = !keepScripts && [];
|
|
|
|
// Single tag
|
|
if ( parsed ) {
|
|
return [ context.createElement( parsed[1] ) ];
|
|
}
|
|
|
|
parsed = jQuery.buildFragment( [ data ], context, scripts );
|
|
|
|
if ( scripts && scripts.length ) {
|
|
jQuery( scripts ).remove();
|
|
}
|
|
|
|
return jQuery.merge( [], parsed.childNodes );
|
|
};
|
|
|
|
|
|
// Keep a copy of the old load method
|
|
var _load = jQuery.fn.load;
|
|
|
|
/**
|
|
* Load a url into a page
|
|
*/
|
|
jQuery.fn.load = function( url, params, callback ) {
|
|
if ( typeof url !== "string" && _load ) {
|
|
return _load.apply( this, arguments );
|
|
}
|
|
|
|
var selector, type, response,
|
|
self = this,
|
|
off = url.indexOf(" ");
|
|
|
|
if ( off >= 0 ) {
|
|
selector = jQuery.trim( url.slice( off ) );
|
|
url = url.slice( 0, off );
|
|
}
|
|
|
|
// If it's a function
|
|
if ( jQuery.isFunction( params ) ) {
|
|
|
|
// We assume that it's the callback
|
|
callback = params;
|
|
params = undefined;
|
|
|
|
// Otherwise, build a param string
|
|
} else if ( params && typeof params === "object" ) {
|
|
type = "POST";
|
|
}
|
|
|
|
// If we have elements to modify, make the request
|
|
if ( self.length > 0 ) {
|
|
jQuery.ajax({
|
|
url: url,
|
|
|
|
// if "type" variable is undefined, then "GET" method will be used
|
|
type: type,
|
|
dataType: "html",
|
|
data: params
|
|
}).done(function( responseText ) {
|
|
|
|
// Save response for use in complete callback
|
|
response = arguments;
|
|
|
|
self.html( selector ?
|
|
|
|
// If a selector was specified, locate the right elements in a dummy div
|
|
// Exclude scripts to avoid IE 'Permission Denied' errors
|
|
jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
|
|
|
|
// Otherwise use the full result
|
|
responseText );
|
|
|
|
}).complete( callback && function( jqXHR, status ) {
|
|
self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
|
|
});
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
|
|
|
|
jQuery.noConflict = function() {};
|
|
|
|
|
|
|
|
return jQuery;
|
|
return jQuery;
|
|
}));
|
|
|
|
},{}],12:[function(require,module,exports){
|
|
"use strict";
|
|
module.exports = function(document) {
|
|
return {
|
|
reload: function() {
|
|
return document.location.reload();
|
|
},
|
|
getHash: function() {
|
|
return document.location.hash;
|
|
},
|
|
setHash: function(newHash) {
|
|
return document.location.hash = newHash;
|
|
},
|
|
changeHref: function(newLocation) {
|
|
return document.location.href = newLocation;
|
|
}
|
|
};
|
|
};
|
|
|
|
},{}],13:[function(require,module,exports){
|
|
var b64pad, hexcase;
|
|
|
|
hexcase = 0;
|
|
|
|
b64pad = "";
|
|
|
|
|
|
/* istanbul ignore next */
|
|
|
|
module.exports = {
|
|
hex_sha1: function(s) {
|
|
return this.rstr2hex(this.rstr_sha1(this.str2rstr_utf8(s)));
|
|
},
|
|
b64_sha1: function(s) {
|
|
return this.rstr2b64(this.rstr_sha1(this.str2rstr_utf8(s)));
|
|
},
|
|
any_sha1: function(s, e) {
|
|
return this.rstr2any(this.rstr_sha1(this.str2rstr_utf8(s)), e);
|
|
},
|
|
hex_hmac_sha1: function(k, d) {
|
|
return this.rstr2hex(this.rstr_hmac_sha1(this.str2rstr_utf8(k), this.str2rstr_utf8(d)));
|
|
},
|
|
b64_hmac_sha1: function(k, d) {
|
|
return this.rstr2b64(this.rstr_hmac_sha1(this.str2rstr_utf8(k), this.str2rstr_utf8(d)));
|
|
},
|
|
any_hmac_sha1: function(k, d, e) {
|
|
return this.rstr2any(this.rstr_hmac_sha1(this.str2rstr_utf8(k), this.str2rstr_utf8(d)), e);
|
|
},
|
|
sha1_vm_test: function() {
|
|
return thishex_sha1("abc").toLowerCase() === "a9993e364706816aba3e25717850c26c9cd0d89d";
|
|
},
|
|
rstr_sha1: function(s) {
|
|
return this.binb2rstr(this.binb_sha1(this.rstr2binb(s), s.length * 8));
|
|
},
|
|
rstr_hmac_sha1: function(key, data) {
|
|
var bkey, hash, i, ipad, opad;
|
|
bkey = this.rstr2binb(key);
|
|
if (bkey.length > 16) {
|
|
bkey = this.binb_sha1(bkey, key.length * 8);
|
|
}
|
|
ipad = Array(16);
|
|
opad = Array(16);
|
|
i = 0;
|
|
while (i < 16) {
|
|
ipad[i] = bkey[i] ^ 0x36363636;
|
|
opad[i] = bkey[i] ^ 0x5C5C5C5C;
|
|
i++;
|
|
}
|
|
hash = this.binb_sha1(ipad.concat(this.rstr2binb(data)), 512 + data.length * 8);
|
|
return this.binb2rstr(this.binb_sha1(opad.concat(hash), 512 + 160));
|
|
},
|
|
rstr2hex: function(input) {
|
|
var e, hex_tab, i, output, x;
|
|
try {
|
|
hexcase;
|
|
} catch (_error) {
|
|
e = _error;
|
|
hexcase = 0;
|
|
}
|
|
hex_tab = (hexcase ? "0123456789ABCDEF" : "0123456789abcdef");
|
|
output = "";
|
|
x = void 0;
|
|
i = 0;
|
|
while (i < input.length) {
|
|
x = input.charCodeAt(i);
|
|
output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
|
|
i++;
|
|
}
|
|
return output;
|
|
},
|
|
rstr2b64: function(input) {
|
|
var e, i, j, len, output, tab, triplet;
|
|
try {
|
|
b64pad;
|
|
} catch (_error) {
|
|
e = _error;
|
|
b64pad = "";
|
|
}
|
|
tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
output = "";
|
|
len = input.length;
|
|
i = 0;
|
|
while (i < len) {
|
|
triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
|
|
j = 0;
|
|
while (j < 4) {
|
|
if (i * 8 + j * 6 > input.length * 8) {
|
|
output += b64pad;
|
|
} else {
|
|
output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
|
|
}
|
|
j++;
|
|
}
|
|
i += 3;
|
|
}
|
|
return output;
|
|
},
|
|
rstr2any: function(input, encoding) {
|
|
var dividend, divisor, full_length, i, output, q, quotient, remainders, x;
|
|
divisor = encoding.length;
|
|
remainders = Array();
|
|
i = void 0;
|
|
q = void 0;
|
|
x = void 0;
|
|
quotient = void 0;
|
|
dividend = Array(Math.ceil(input.length / 2));
|
|
i = 0;
|
|
while (i < dividend.length) {
|
|
dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
|
|
i++;
|
|
}
|
|
while (dividend.length > 0) {
|
|
quotient = Array();
|
|
x = 0;
|
|
i = 0;
|
|
while (i < dividend.length) {
|
|
x = (x << 16) + dividend[i];
|
|
q = Math.floor(x / divisor);
|
|
x -= q * divisor;
|
|
if (quotient.length > 0 || q > 0) {
|
|
quotient[quotient.length] = q;
|
|
}
|
|
i++;
|
|
}
|
|
remainders[remainders.length] = x;
|
|
dividend = quotient;
|
|
}
|
|
output = "";
|
|
i = remainders.length - 1;
|
|
while (i >= 0) {
|
|
output += encoding.charAt(remainders[i]);
|
|
i--;
|
|
}
|
|
full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
|
|
i = output.length;
|
|
while (i < full_length) {
|
|
output = encoding[0] + output;
|
|
i++;
|
|
}
|
|
return output;
|
|
},
|
|
str2rstr_utf8: function(input) {
|
|
var i, output, x, y;
|
|
output = "";
|
|
i = -1;
|
|
x = void 0;
|
|
y = void 0;
|
|
while (++i < input.length) {
|
|
x = input.charCodeAt(i);
|
|
y = (i + 1 < input.length ? input.charCodeAt(i + 1) : 0);
|
|
if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
|
|
x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
|
|
i++;
|
|
}
|
|
if (x <= 0x7F) {
|
|
output += String.fromCharCode(x);
|
|
} else if (x <= 0x7FF) {
|
|
output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F), 0x80 | (x & 0x3F));
|
|
} else if (x <= 0xFFFF) {
|
|
output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), 0x80 | ((x >>> 6) & 0x3F), 0x80 | (x & 0x3F));
|
|
} else {
|
|
if (x <= 0x1FFFFF) {
|
|
output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), 0x80 | ((x >>> 12) & 0x3F), 0x80 | ((x >>> 6) & 0x3F), 0x80 | (x & 0x3F));
|
|
}
|
|
}
|
|
}
|
|
return output;
|
|
},
|
|
str2rstr_utf16le: function(input) {
|
|
var i, output;
|
|
output = "";
|
|
i = 0;
|
|
while (i < input.length) {
|
|
output += String.fromCharCode(input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF);
|
|
i++;
|
|
}
|
|
return output;
|
|
},
|
|
str2rstr_utf16be: function(input) {
|
|
var i, output;
|
|
output = "";
|
|
i = 0;
|
|
while (i < input.length) {
|
|
output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF);
|
|
i++;
|
|
}
|
|
return output;
|
|
},
|
|
rstr2binb: function(input) {
|
|
var i, output;
|
|
output = Array(input.length >> 2);
|
|
i = 0;
|
|
while (i < output.length) {
|
|
output[i] = 0;
|
|
i++;
|
|
}
|
|
i = 0;
|
|
while (i < input.length * 8) {
|
|
output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
|
|
i += 8;
|
|
}
|
|
return output;
|
|
},
|
|
binb2rstr: function(input) {
|
|
var i, output;
|
|
output = "";
|
|
i = 0;
|
|
while (i < input.length * 32) {
|
|
output += String.fromCharCode((input[i >> 5] >>> (24 - i % 32)) & 0xFF);
|
|
i += 8;
|
|
}
|
|
return output;
|
|
},
|
|
binb_sha1: function(x, len) {
|
|
var a, b, c, d, e, i, j, olda, oldb, oldc, oldd, olde, t, w;
|
|
x[len >> 5] |= 0x80 << (24 - len % 32);
|
|
x[((len + 64 >> 9) << 4) + 15] = len;
|
|
w = Array(80);
|
|
a = 1732584193;
|
|
b = -271733879;
|
|
c = -1732584194;
|
|
d = 271733878;
|
|
e = -1009589776;
|
|
i = 0;
|
|
while (i < x.length) {
|
|
olda = a;
|
|
oldb = b;
|
|
oldc = c;
|
|
oldd = d;
|
|
olde = e;
|
|
j = 0;
|
|
while (j < 80) {
|
|
if (j < 16) {
|
|
w[j] = x[i + j];
|
|
} else {
|
|
w[j] = this.bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
|
|
}
|
|
t = this.safe_add(this.safe_add(this.bit_rol(a, 5), this.sha1_ft(j, b, c, d)), this.safe_add(this.safe_add(e, w[j]), this.sha1_kt(j)));
|
|
e = d;
|
|
d = c;
|
|
c = this.bit_rol(b, 30);
|
|
b = a;
|
|
a = t;
|
|
j++;
|
|
}
|
|
a = this.safe_add(a, olda);
|
|
b = this.safe_add(b, oldb);
|
|
c = this.safe_add(c, oldc);
|
|
d = this.safe_add(d, oldd);
|
|
e = this.safe_add(e, olde);
|
|
i += 16;
|
|
}
|
|
return Array(a, b, c, d, e);
|
|
},
|
|
sha1_ft: function(t, b, c, d) {
|
|
if (t < 20) {
|
|
return (b & c) | ((~b) & d);
|
|
}
|
|
if (t < 40) {
|
|
return b ^ c ^ d;
|
|
}
|
|
if (t < 60) {
|
|
return (b & c) | (b & d) | (c & d);
|
|
}
|
|
return b ^ c ^ d;
|
|
},
|
|
sha1_kt: function(t) {
|
|
if (t < 20) {
|
|
return 1518500249;
|
|
} else {
|
|
if (t < 40) {
|
|
return 1859775393;
|
|
} else {
|
|
if (t < 60) {
|
|
return -1894007588;
|
|
} else {
|
|
return -899497514;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
safe_add: function(x, y) {
|
|
var lsw, msw;
|
|
lsw = (x & 0xFFFF) + (y & 0xFFFF);
|
|
msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
|
return (msw << 16) | (lsw & 0xFFFF);
|
|
},
|
|
bit_rol: function(num, cnt) {
|
|
return (num << cnt) | (num >>> (32 - cnt));
|
|
},
|
|
create_hash: function() {
|
|
var hash;
|
|
hash = this.b64_sha1((new Date()).getTime() + ":" + Math.floor(Math.random() * 9999999));
|
|
return hash.replace(/\+/g, "-").replace(/\//g, "_").replace(/\=+$/, "");
|
|
}
|
|
};
|
|
|
|
},{}],14:[function(require,module,exports){
|
|
module.exports = function(document) {
|
|
return {
|
|
getAbsUrl: function(url) {
|
|
var base_url;
|
|
if (url.match(/^.{2,5}:\/\//)) {
|
|
return url;
|
|
}
|
|
if (url[0] === "/") {
|
|
return document.location.protocol + "//" + document.location.host + url;
|
|
}
|
|
base_url = document.location.protocol + "//" + document.location.host + document.location.pathname;
|
|
if (base_url[base_url.length - 1] !== "/" && url[0] !== "#") {
|
|
return base_url + "/" + url;
|
|
}
|
|
return base_url + url;
|
|
},
|
|
replaceParam: function(param, rep, rep2) {
|
|
param = param.replace(/\{\{(.*?)\}\}/g, function(m, v) {
|
|
return rep[v] || "";
|
|
});
|
|
if (rep2) {
|
|
param = param.replace(/\{(.*?)\}/g, function(m, v) {
|
|
return rep2[v] || "";
|
|
});
|
|
}
|
|
return param;
|
|
}
|
|
};
|
|
};
|
|
|
|
},{}],15:[function(require,module,exports){
|
|
// shim for using process in browser
|
|
|
|
var process = module.exports = {};
|
|
|
|
process.nextTick = (function () {
|
|
var canSetImmediate = typeof window !== 'undefined'
|
|
&& window.setImmediate;
|
|
var canPost = typeof window !== 'undefined'
|
|
&& window.postMessage && window.addEventListener
|
|
;
|
|
|
|
if (canSetImmediate) {
|
|
return function (f) { return window.setImmediate(f) };
|
|
}
|
|
|
|
if (canPost) {
|
|
var queue = [];
|
|
window.addEventListener('message', function (ev) {
|
|
var source = ev.source;
|
|
if ((source === window || source === null) && ev.data === 'process-tick') {
|
|
ev.stopPropagation();
|
|
if (queue.length > 0) {
|
|
var fn = queue.shift();
|
|
fn();
|
|
}
|
|
}
|
|
}, true);
|
|
|
|
return function nextTick(fn) {
|
|
queue.push(fn);
|
|
window.postMessage('process-tick', '*');
|
|
};
|
|
}
|
|
|
|
return function nextTick(fn) {
|
|
setTimeout(fn, 0);
|
|
};
|
|
})();
|
|
|
|
process.title = 'browser';
|
|
process.browser = true;
|
|
process.env = {};
|
|
process.argv = [];
|
|
|
|
process.binding = function (name) {
|
|
throw new Error('process.binding is not supported');
|
|
}
|
|
|
|
// TODO(shtylman)
|
|
process.cwd = function () { return '/' };
|
|
process.chdir = function (dir) {
|
|
throw new Error('process.chdir is not supported');
|
|
};
|
|
|
|
},{}],16:[function(require,module,exports){
|
|
(function (process){
|
|
// vim:ts=4:sts=4:sw=4:
|
|
/*!
|
|
*
|
|
* Copyright 2009-2012 Kris Kowal under the terms of the MIT
|
|
* license found at http://github.com/kriskowal/q/raw/master/LICENSE
|
|
*
|
|
* With parts by Tyler Close
|
|
* Copyright 2007-2009 Tyler Close under the terms of the MIT X license found
|
|
* at http://www.opensource.org/licenses/mit-license.html
|
|
* Forked at ref_send.js version: 2009-05-11
|
|
*
|
|
* With parts by Mark Miller
|
|
* Copyright (C) 2011 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
(function (definition) {
|
|
"use strict";
|
|
|
|
// This file will function properly as a <script> tag, or a module
|
|
// using CommonJS and NodeJS or RequireJS module formats. In
|
|
// Common/Node/RequireJS, the module exports the Q API and when
|
|
// executed as a simple <script>, it creates a Q global instead.
|
|
|
|
// Montage Require
|
|
if (typeof bootstrap === "function") {
|
|
bootstrap("promise", definition);
|
|
|
|
// CommonJS
|
|
} else if (typeof exports === "object" && typeof module === "object") {
|
|
module.exports = definition();
|
|
|
|
// RequireJS
|
|
} else if (typeof define === "function" && define.amd) {
|
|
define(definition);
|
|
|
|
// SES (Secure EcmaScript)
|
|
} else if (typeof ses !== "undefined") {
|
|
if (!ses.ok()) {
|
|
return;
|
|
} else {
|
|
ses.makeQ = definition;
|
|
}
|
|
|
|
// <script>
|
|
} else if (typeof self !== "undefined") {
|
|
self.Q = definition();
|
|
|
|
} else {
|
|
throw new Error("This environment was not anticipated by Q. Please file a bug.");
|
|
}
|
|
|
|
})(function () {
|
|
"use strict";
|
|
|
|
var hasStacks = false;
|
|
try {
|
|
throw new Error();
|
|
} catch (e) {
|
|
hasStacks = !!e.stack;
|
|
}
|
|
|
|
// All code after this point will be filtered from stack traces reported
|
|
// by Q.
|
|
var qStartingLine = captureLine();
|
|
var qFileName;
|
|
|
|
// shims
|
|
|
|
// used for fallback in "allResolved"
|
|
var noop = function () {};
|
|
|
|
// Use the fastest possible means to execute a task in a future turn
|
|
// of the event loop.
|
|
var nextTick =(function () {
|
|
// linked list of tasks (single, with head node)
|
|
var head = {task: void 0, next: null};
|
|
var tail = head;
|
|
var flushing = false;
|
|
var requestTick = void 0;
|
|
var isNodeJS = false;
|
|
// queue for late tasks, used by unhandled rejection tracking
|
|
var laterQueue = [];
|
|
|
|
function flush() {
|
|
/* jshint loopfunc: true */
|
|
var task, domain;
|
|
|
|
while (head.next) {
|
|
head = head.next;
|
|
task = head.task;
|
|
head.task = void 0;
|
|
domain = head.domain;
|
|
|
|
if (domain) {
|
|
head.domain = void 0;
|
|
domain.enter();
|
|
}
|
|
runSingle(task, domain);
|
|
|
|
}
|
|
while (laterQueue.length) {
|
|
task = laterQueue.pop();
|
|
runSingle(task);
|
|
}
|
|
flushing = false;
|
|
}
|
|
// runs a single function in the async queue
|
|
function runSingle(task, domain) {
|
|
try {
|
|
task();
|
|
|
|
} catch (e) {
|
|
if (isNodeJS) {
|
|
// In node, uncaught exceptions are considered fatal errors.
|
|
// Re-throw them synchronously to interrupt flushing!
|
|
|
|
// Ensure continuation if the uncaught exception is suppressed
|
|
// listening "uncaughtException" events (as domains does).
|
|
// Continue in next event to avoid tick recursion.
|
|
if (domain) {
|
|
domain.exit();
|
|
}
|
|
setTimeout(flush, 0);
|
|
if (domain) {
|
|
domain.enter();
|
|
}
|
|
|
|
throw e;
|
|
|
|
} else {
|
|
// In browsers, uncaught exceptions are not fatal.
|
|
// Re-throw them asynchronously to avoid slow-downs.
|
|
setTimeout(function () {
|
|
throw e;
|
|
}, 0);
|
|
}
|
|
}
|
|
|
|
if (domain) {
|
|
domain.exit();
|
|
}
|
|
}
|
|
|
|
nextTick = function (task) {
|
|
tail = tail.next = {
|
|
task: task,
|
|
domain: isNodeJS && process.domain,
|
|
next: null
|
|
};
|
|
|
|
if (!flushing) {
|
|
flushing = true;
|
|
requestTick();
|
|
}
|
|
};
|
|
|
|
if (typeof process === "object" &&
|
|
process.toString() === "[object process]" && process.nextTick) {
|
|
// Ensure Q is in a real Node environment, with a `process.nextTick`.
|
|
// To see through fake Node environments:
|
|
// * Mocha test runner - exposes a `process` global without a `nextTick`
|
|
// * Browserify - exposes a `process.nexTick` function that uses
|
|
// `setTimeout`. In this case `setImmediate` is preferred because
|
|
// it is faster. Browserify's `process.toString()` yields
|
|
// "[object Object]", while in a real Node environment
|
|
// `process.nextTick()` yields "[object process]".
|
|
isNodeJS = true;
|
|
|
|
requestTick = function () {
|
|
process.nextTick(flush);
|
|
};
|
|
|
|
} else if (typeof setImmediate === "function") {
|
|
// In IE10, Node.js 0.9+, or https://github.com/NobleJS/setImmediate
|
|
if (typeof window !== "undefined") {
|
|
requestTick = setImmediate.bind(window, flush);
|
|
} else {
|
|
requestTick = function () {
|
|
setImmediate(flush);
|
|
};
|
|
}
|
|
|
|
} else if (typeof MessageChannel !== "undefined") {
|
|
// modern browsers
|
|
// http://www.nonblocking.io/2011/06/windownexttick.html
|
|
var channel = new MessageChannel();
|
|
// At least Safari Version 6.0.5 (8536.30.1) intermittently cannot create
|
|
// working message ports the first time a page loads.
|
|
channel.port1.onmessage = function () {
|
|
requestTick = requestPortTick;
|
|
channel.port1.onmessage = flush;
|
|
flush();
|
|
};
|
|
var requestPortTick = function () {
|
|
// Opera requires us to provide a message payload, regardless of
|
|
// whether we use it.
|
|
channel.port2.postMessage(0);
|
|
};
|
|
requestTick = function () {
|
|
setTimeout(flush, 0);
|
|
requestPortTick();
|
|
};
|
|
|
|
} else {
|
|
// old browsers
|
|
requestTick = function () {
|
|
setTimeout(flush, 0);
|
|
};
|
|
}
|
|
// runs a task after all other tasks have been run
|
|
// this is useful for unhandled rejection tracking that needs to happen
|
|
// after all `then`d tasks have been run.
|
|
nextTick.runAfter = function (task) {
|
|
laterQueue.push(task);
|
|
if (!flushing) {
|
|
flushing = true;
|
|
requestTick();
|
|
}
|
|
};
|
|
return nextTick;
|
|
})();
|
|
|
|
// Attempt to make generics safe in the face of downstream
|
|
// modifications.
|
|
// There is no situation where this is necessary.
|
|
// If you need a security guarantee, these primordials need to be
|
|
// deeply frozen anyway, and if you don’t need a security guarantee,
|
|
// this is just plain paranoid.
|
|
// However, this **might** have the nice side-effect of reducing the size of
|
|
// the minified code by reducing x.call() to merely x()
|
|
// See Mark Miller’s explanation of what this does.
|
|
// http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming
|
|
var call = Function.call;
|
|
function uncurryThis(f) {
|
|
return function () {
|
|
return call.apply(f, arguments);
|
|
};
|
|
}
|
|
// This is equivalent, but slower:
|
|
// uncurryThis = Function_bind.bind(Function_bind.call);
|
|
// http://jsperf.com/uncurrythis
|
|
|
|
var array_slice = uncurryThis(Array.prototype.slice);
|
|
|
|
var array_reduce = uncurryThis(
|
|
Array.prototype.reduce || function (callback, basis) {
|
|
var index = 0,
|
|
length = this.length;
|
|
// concerning the initial value, if one is not provided
|
|
if (arguments.length === 1) {
|
|
// seek to the first value in the array, accounting
|
|
// for the possibility that is is a sparse array
|
|
do {
|
|
if (index in this) {
|
|
basis = this[index++];
|
|
break;
|
|
}
|
|
if (++index >= length) {
|
|
throw new TypeError();
|
|
}
|
|
} while (1);
|
|
}
|
|
// reduce
|
|
for (; index < length; index++) {
|
|
// account for the possibility that the array is sparse
|
|
if (index in this) {
|
|
basis = callback(basis, this[index], index);
|
|
}
|
|
}
|
|
return basis;
|
|
}
|
|
);
|
|
|
|
var array_indexOf = uncurryThis(
|
|
Array.prototype.indexOf || function (value) {
|
|
// not a very good shim, but good enough for our one use of it
|
|
for (var i = 0; i < this.length; i++) {
|
|
if (this[i] === value) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
);
|
|
|
|
var array_map = uncurryThis(
|
|
Array.prototype.map || function (callback, thisp) {
|
|
var self = this;
|
|
var collect = [];
|
|
array_reduce(self, function (undefined, value, index) {
|
|
collect.push(callback.call(thisp, value, index, self));
|
|
}, void 0);
|
|
return collect;
|
|
}
|
|
);
|
|
|
|
var object_create = Object.create || function (prototype) {
|
|
function Type() { }
|
|
Type.prototype = prototype;
|
|
return new Type();
|
|
};
|
|
|
|
var object_hasOwnProperty = uncurryThis(Object.prototype.hasOwnProperty);
|
|
|
|
var object_keys = Object.keys || function (object) {
|
|
var keys = [];
|
|
for (var key in object) {
|
|
if (object_hasOwnProperty(object, key)) {
|
|
keys.push(key);
|
|
}
|
|
}
|
|
return keys;
|
|
};
|
|
|
|
var object_toString = uncurryThis(Object.prototype.toString);
|
|
|
|
function isObject(value) {
|
|
return value === Object(value);
|
|
}
|
|
|
|
// generator related shims
|
|
|
|
// FIXME: Remove this function once ES6 generators are in SpiderMonkey.
|
|
function isStopIteration(exception) {
|
|
return (
|
|
object_toString(exception) === "[object StopIteration]" ||
|
|
exception instanceof QReturnValue
|
|
);
|
|
}
|
|
|
|
// FIXME: Remove this helper and Q.return once ES6 generators are in
|
|
// SpiderMonkey.
|
|
var QReturnValue;
|
|
if (typeof ReturnValue !== "undefined") {
|
|
QReturnValue = ReturnValue;
|
|
} else {
|
|
QReturnValue = function (value) {
|
|
this.value = value;
|
|
};
|
|
}
|
|
|
|
// long stack traces
|
|
|
|
var STACK_JUMP_SEPARATOR = "From previous event:";
|
|
|
|
function makeStackTraceLong(error, promise) {
|
|
// If possible, transform the error stack trace by removing Node and Q
|
|
// cruft, then concatenating with the stack trace of `promise`. See #57.
|
|
if (hasStacks &&
|
|
promise.stack &&
|
|
typeof error === "object" &&
|
|
error !== null &&
|
|
error.stack &&
|
|
error.stack.indexOf(STACK_JUMP_SEPARATOR) === -1
|
|
) {
|
|
var stacks = [];
|
|
for (var p = promise; !!p; p = p.source) {
|
|
if (p.stack) {
|
|
stacks.unshift(p.stack);
|
|
}
|
|
}
|
|
stacks.unshift(error.stack);
|
|
|
|
var concatedStacks = stacks.join("\n" + STACK_JUMP_SEPARATOR + "\n");
|
|
error.stack = filterStackString(concatedStacks);
|
|
}
|
|
}
|
|
|
|
function filterStackString(stackString) {
|
|
var lines = stackString.split("\n");
|
|
var desiredLines = [];
|
|
for (var i = 0; i < lines.length; ++i) {
|
|
var line = lines[i];
|
|
|
|
if (!isInternalFrame(line) && !isNodeFrame(line) && line) {
|
|
desiredLines.push(line);
|
|
}
|
|
}
|
|
return desiredLines.join("\n");
|
|
}
|
|
|
|
function isNodeFrame(stackLine) {
|
|
return stackLine.indexOf("(module.js:") !== -1 ||
|
|
stackLine.indexOf("(node.js:") !== -1;
|
|
}
|
|
|
|
function getFileNameAndLineNumber(stackLine) {
|
|
// Named functions: "at functionName (filename:lineNumber:columnNumber)"
|
|
// In IE10 function name can have spaces ("Anonymous function") O_o
|
|
var attempt1 = /at .+ \((.+):(\d+):(?:\d+)\)$/.exec(stackLine);
|
|
if (attempt1) {
|
|
return [attempt1[1], Number(attempt1[2])];
|
|
}
|
|
|
|
// Anonymous functions: "at filename:lineNumber:columnNumber"
|
|
var attempt2 = /at ([^ ]+):(\d+):(?:\d+)$/.exec(stackLine);
|
|
if (attempt2) {
|
|
return [attempt2[1], Number(attempt2[2])];
|
|
}
|
|
|
|
// Firefox style: "function@filename:lineNumber or @filename:lineNumber"
|
|
var attempt3 = /.*@(.+):(\d+)$/.exec(stackLine);
|
|
if (attempt3) {
|
|
return [attempt3[1], Number(attempt3[2])];
|
|
}
|
|
}
|
|
|
|
function isInternalFrame(stackLine) {
|
|
var fileNameAndLineNumber = getFileNameAndLineNumber(stackLine);
|
|
|
|
if (!fileNameAndLineNumber) {
|
|
return false;
|
|
}
|
|
|
|
var fileName = fileNameAndLineNumber[0];
|
|
var lineNumber = fileNameAndLineNumber[1];
|
|
|
|
return fileName === qFileName &&
|
|
lineNumber >= qStartingLine &&
|
|
lineNumber <= qEndingLine;
|
|
}
|
|
|
|
// discover own file name and line number range for filtering stack
|
|
// traces
|
|
function captureLine() {
|
|
if (!hasStacks) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
throw new Error();
|
|
} catch (e) {
|
|
var lines = e.stack.split("\n");
|
|
var firstLine = lines[0].indexOf("@") > 0 ? lines[1] : lines[2];
|
|
var fileNameAndLineNumber = getFileNameAndLineNumber(firstLine);
|
|
if (!fileNameAndLineNumber) {
|
|
return;
|
|
}
|
|
|
|
qFileName = fileNameAndLineNumber[0];
|
|
return fileNameAndLineNumber[1];
|
|
}
|
|
}
|
|
|
|
function deprecate(callback, name, alternative) {
|
|
return function () {
|
|
if (typeof console !== "undefined" &&
|
|
typeof console.warn === "function") {
|
|
console.warn(name + " is deprecated, use " + alternative +
|
|
" instead.", new Error("").stack);
|
|
}
|
|
return callback.apply(callback, arguments);
|
|
};
|
|
}
|
|
|
|
// end of shims
|
|
// beginning of real work
|
|
|
|
/**
|
|
* Constructs a promise for an immediate reference, passes promises through, or
|
|
* coerces promises from different systems.
|
|
* @param value immediate reference or promise
|
|
*/
|
|
function Q(value) {
|
|
// If the object is already a Promise, return it directly. This enables
|
|
// the resolve function to both be used to created references from objects,
|
|
// but to tolerably coerce non-promises to promises.
|
|
if (value instanceof Promise) {
|
|
return value;
|
|
}
|
|
|
|
// assimilate thenables
|
|
if (isPromiseAlike(value)) {
|
|
return coerce(value);
|
|
} else {
|
|
return fulfill(value);
|
|
}
|
|
}
|
|
Q.resolve = Q;
|
|
|
|
/**
|
|
* Performs a task in a future turn of the event loop.
|
|
* @param {Function} task
|
|
*/
|
|
Q.nextTick = nextTick;
|
|
|
|
/**
|
|
* Controls whether or not long stack traces will be on
|
|
*/
|
|
Q.longStackSupport = false;
|
|
|
|
// enable long stacks if Q_DEBUG is set
|
|
if (typeof process === "object" && process && process.env && process.env.Q_DEBUG) {
|
|
Q.longStackSupport = true;
|
|
}
|
|
|
|
/**
|
|
* Constructs a {promise, resolve, reject} object.
|
|
*
|
|
* `resolve` is a callback to invoke with a more resolved value for the
|
|
* promise. To fulfill the promise, invoke `resolve` with any value that is
|
|
* not a thenable. To reject the promise, invoke `resolve` with a rejected
|
|
* thenable, or invoke `reject` with the reason directly. To resolve the
|
|
* promise to another thenable, thus putting it in the same state, invoke
|
|
* `resolve` with that other thenable.
|
|
*/
|
|
Q.defer = defer;
|
|
function defer() {
|
|
// if "messages" is an "Array", that indicates that the promise has not yet
|
|
// been resolved. If it is "undefined", it has been resolved. Each
|
|
// element of the messages array is itself an array of complete arguments to
|
|
// forward to the resolved promise. We coerce the resolution value to a
|
|
// promise using the `resolve` function because it handles both fully
|
|
// non-thenable values and other thenables gracefully.
|
|
var messages = [], progressListeners = [], resolvedPromise;
|
|
|
|
var deferred = object_create(defer.prototype);
|
|
var promise = object_create(Promise.prototype);
|
|
|
|
promise.promiseDispatch = function (resolve, op, operands) {
|
|
var args = array_slice(arguments);
|
|
if (messages) {
|
|
messages.push(args);
|
|
if (op === "when" && operands[1]) { // progress operand
|
|
progressListeners.push(operands[1]);
|
|
}
|
|
} else {
|
|
Q.nextTick(function () {
|
|
resolvedPromise.promiseDispatch.apply(resolvedPromise, args);
|
|
});
|
|
}
|
|
};
|
|
|
|
// XXX deprecated
|
|
promise.valueOf = function () {
|
|
if (messages) {
|
|
return promise;
|
|
}
|
|
var nearerValue = nearer(resolvedPromise);
|
|
if (isPromise(nearerValue)) {
|
|
resolvedPromise = nearerValue; // shorten chain
|
|
}
|
|
return nearerValue;
|
|
};
|
|
|
|
promise.inspect = function () {
|
|
if (!resolvedPromise) {
|
|
return { state: "pending" };
|
|
}
|
|
return resolvedPromise.inspect();
|
|
};
|
|
|
|
if (Q.longStackSupport && hasStacks) {
|
|
try {
|
|
throw new Error();
|
|
} catch (e) {
|
|
// NOTE: don't try to use `Error.captureStackTrace` or transfer the
|
|
// accessor around; that causes memory leaks as per GH-111. Just
|
|
// reify the stack trace as a string ASAP.
|
|
//
|
|
// At the same time, cut off the first line; it's always just
|
|
// "[object Promise]\n", as per the `toString`.
|
|
promise.stack = e.stack.substring(e.stack.indexOf("\n") + 1);
|
|
}
|
|
}
|
|
|
|
// NOTE: we do the checks for `resolvedPromise` in each method, instead of
|
|
// consolidating them into `become`, since otherwise we'd create new
|
|
// promises with the lines `become(whatever(value))`. See e.g. GH-252.
|
|
|
|
function become(newPromise) {
|
|
resolvedPromise = newPromise;
|
|
promise.source = newPromise;
|
|
|
|
array_reduce(messages, function (undefined, message) {
|
|
Q.nextTick(function () {
|
|
newPromise.promiseDispatch.apply(newPromise, message);
|
|
});
|
|
}, void 0);
|
|
|
|
messages = void 0;
|
|
progressListeners = void 0;
|
|
}
|
|
|
|
deferred.promise = promise;
|
|
deferred.resolve = function (value) {
|
|
if (resolvedPromise) {
|
|
return;
|
|
}
|
|
|
|
become(Q(value));
|
|
};
|
|
|
|
deferred.fulfill = function (value) {
|
|
if (resolvedPromise) {
|
|
return;
|
|
}
|
|
|
|
become(fulfill(value));
|
|
};
|
|
deferred.reject = function (reason) {
|
|
if (resolvedPromise) {
|
|
return;
|
|
}
|
|
|
|
become(reject(reason));
|
|
};
|
|
deferred.notify = function (progress) {
|
|
if (resolvedPromise) {
|
|
return;
|
|
}
|
|
|
|
array_reduce(progressListeners, function (undefined, progressListener) {
|
|
Q.nextTick(function () {
|
|
progressListener(progress);
|
|
});
|
|
}, void 0);
|
|
};
|
|
|
|
return deferred;
|
|
}
|
|
|
|
/**
|
|
* Creates a Node-style callback that will resolve or reject the deferred
|
|
* promise.
|
|
* @returns a nodeback
|
|
*/
|
|
defer.prototype.makeNodeResolver = function () {
|
|
var self = this;
|
|
return function (error, value) {
|
|
if (error) {
|
|
self.reject(error);
|
|
} else if (arguments.length > 2) {
|
|
self.resolve(array_slice(arguments, 1));
|
|
} else {
|
|
self.resolve(value);
|
|
}
|
|
};
|
|
};
|
|
|
|
/**
|
|
* @param resolver {Function} a function that returns nothing and accepts
|
|
* the resolve, reject, and notify functions for a deferred.
|
|
* @returns a promise that may be resolved with the given resolve and reject
|
|
* functions, or rejected by a thrown exception in resolver
|
|
*/
|
|
Q.Promise = promise; // ES6
|
|
Q.promise = promise;
|
|
function promise(resolver) {
|
|
if (typeof resolver !== "function") {
|
|
throw new TypeError("resolver must be a function.");
|
|
}
|
|
var deferred = defer();
|
|
try {
|
|
resolver(deferred.resolve, deferred.reject, deferred.notify);
|
|
} catch (reason) {
|
|
deferred.reject(reason);
|
|
}
|
|
return deferred.promise;
|
|
}
|
|
|
|
promise.race = race; // ES6
|
|
promise.all = all; // ES6
|
|
promise.reject = reject; // ES6
|
|
promise.resolve = Q; // ES6
|
|
|
|
// XXX experimental. This method is a way to denote that a local value is
|
|
// serializable and should be immediately dispatched to a remote upon request,
|
|
// instead of passing a reference.
|
|
Q.passByCopy = function (object) {
|
|
//freeze(object);
|
|
//passByCopies.set(object, true);
|
|
return object;
|
|
};
|
|
|
|
Promise.prototype.passByCopy = function () {
|
|
//freeze(object);
|
|
//passByCopies.set(object, true);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* If two promises eventually fulfill to the same value, promises that value,
|
|
* but otherwise rejects.
|
|
* @param x {Any*}
|
|
* @param y {Any*}
|
|
* @returns {Any*} a promise for x and y if they are the same, but a rejection
|
|
* otherwise.
|
|
*
|
|
*/
|
|
Q.join = function (x, y) {
|
|
return Q(x).join(y);
|
|
};
|
|
|
|
Promise.prototype.join = function (that) {
|
|
return Q([this, that]).spread(function (x, y) {
|
|
if (x === y) {
|
|
// TODO: "===" should be Object.is or equiv
|
|
return x;
|
|
} else {
|
|
throw new Error("Can't join: not the same: " + x + " " + y);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Returns a promise for the first of an array of promises to become settled.
|
|
* @param answers {Array[Any*]} promises to race
|
|
* @returns {Any*} the first promise to be settled
|
|
*/
|
|
Q.race = race;
|
|
function race(answerPs) {
|
|
return promise(function (resolve, reject) {
|
|
// Switch to this once we can assume at least ES5
|
|
// answerPs.forEach(function (answerP) {
|
|
// Q(answerP).then(resolve, reject);
|
|
// });
|
|
// Use this in the meantime
|
|
for (var i = 0, len = answerPs.length; i < len; i++) {
|
|
Q(answerPs[i]).then(resolve, reject);
|
|
}
|
|
});
|
|
}
|
|
|
|
Promise.prototype.race = function () {
|
|
return this.then(Q.race);
|
|
};
|
|
|
|
/**
|
|
* Constructs a Promise with a promise descriptor object and optional fallback
|
|
* function. The descriptor contains methods like when(rejected), get(name),
|
|
* set(name, value), post(name, args), and delete(name), which all
|
|
* return either a value, a promise for a value, or a rejection. The fallback
|
|
* accepts the operation name, a resolver, and any further arguments that would
|
|
* have been forwarded to the appropriate method above had a method been
|
|
* provided with the proper name. The API makes no guarantees about the nature
|
|
* of the returned object, apart from that it is usable whereever promises are
|
|
* bought and sold.
|
|
*/
|
|
Q.makePromise = Promise;
|
|
function Promise(descriptor, fallback, inspect) {
|
|
if (fallback === void 0) {
|
|
fallback = function (op) {
|
|
return reject(new Error(
|
|
"Promise does not support operation: " + op
|
|
));
|
|
};
|
|
}
|
|
if (inspect === void 0) {
|
|
inspect = function () {
|
|
return {state: "unknown"};
|
|
};
|
|
}
|
|
|
|
var promise = object_create(Promise.prototype);
|
|
|
|
promise.promiseDispatch = function (resolve, op, args) {
|
|
var result;
|
|
try {
|
|
if (descriptor[op]) {
|
|
result = descriptor[op].apply(promise, args);
|
|
} else {
|
|
result = fallback.call(promise, op, args);
|
|
}
|
|
} catch (exception) {
|
|
result = reject(exception);
|
|
}
|
|
if (resolve) {
|
|
resolve(result);
|
|
}
|
|
};
|
|
|
|
promise.inspect = inspect;
|
|
|
|
// XXX deprecated `valueOf` and `exception` support
|
|
if (inspect) {
|
|
var inspected = inspect();
|
|
if (inspected.state === "rejected") {
|
|
promise.exception = inspected.reason;
|
|
}
|
|
|
|
promise.valueOf = function () {
|
|
var inspected = inspect();
|
|
if (inspected.state === "pending" ||
|
|
inspected.state === "rejected") {
|
|
return promise;
|
|
}
|
|
return inspected.value;
|
|
};
|
|
}
|
|
|
|
return promise;
|
|
}
|
|
|
|
Promise.prototype.toString = function () {
|
|
return "[object Promise]";
|
|
};
|
|
|
|
Promise.prototype.then = function (fulfilled, rejected, progressed) {
|
|
var self = this;
|
|
var deferred = defer();
|
|
var done = false; // ensure the untrusted promise makes at most a
|
|
// single call to one of the callbacks
|
|
|
|
function _fulfilled(value) {
|
|
try {
|
|
return typeof fulfilled === "function" ? fulfilled(value) : value;
|
|
} catch (exception) {
|
|
return reject(exception);
|
|
}
|
|
}
|
|
|
|
function _rejected(exception) {
|
|
if (typeof rejected === "function") {
|
|
makeStackTraceLong(exception, self);
|
|
try {
|
|
return rejected(exception);
|
|
} catch (newException) {
|
|
return reject(newException);
|
|
}
|
|
}
|
|
return reject(exception);
|
|
}
|
|
|
|
function _progressed(value) {
|
|
return typeof progressed === "function" ? progressed(value) : value;
|
|
}
|
|
|
|
Q.nextTick(function () {
|
|
self.promiseDispatch(function (value) {
|
|
if (done) {
|
|
return;
|
|
}
|
|
done = true;
|
|
|
|
deferred.resolve(_fulfilled(value));
|
|
}, "when", [function (exception) {
|
|
if (done) {
|
|
return;
|
|
}
|
|
done = true;
|
|
|
|
deferred.resolve(_rejected(exception));
|
|
}]);
|
|
});
|
|
|
|
// Progress propagator need to be attached in the current tick.
|
|
self.promiseDispatch(void 0, "when", [void 0, function (value) {
|
|
var newValue;
|
|
var threw = false;
|
|
try {
|
|
newValue = _progressed(value);
|
|
} catch (e) {
|
|
threw = true;
|
|
if (Q.onerror) {
|
|
Q.onerror(e);
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
if (!threw) {
|
|
deferred.notify(newValue);
|
|
}
|
|
}]);
|
|
|
|
return deferred.promise;
|
|
};
|
|
|
|
Q.tap = function (promise, callback) {
|
|
return Q(promise).tap(callback);
|
|
};
|
|
|
|
/**
|
|
* Works almost like "finally", but not called for rejections.
|
|
* Original resolution value is passed through callback unaffected.
|
|
* Callback may return a promise that will be awaited for.
|
|
* @param {Function} callback
|
|
* @returns {Q.Promise}
|
|
* @example
|
|
* doSomething()
|
|
* .then(...)
|
|
* .tap(console.log)
|
|
* .then(...);
|
|
*/
|
|
Promise.prototype.tap = function (callback) {
|
|
callback = Q(callback);
|
|
|
|
return this.then(function (value) {
|
|
return callback.fcall(value).thenResolve(value);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Registers an observer on a promise.
|
|
*
|
|
* Guarantees:
|
|
*
|
|
* 1. that fulfilled and rejected will be called only once.
|
|
* 2. that either the fulfilled callback or the rejected callback will be
|
|
* called, but not both.
|
|
* 3. that fulfilled and rejected will not be called in this turn.
|
|
*
|
|
* @param value promise or immediate reference to observe
|
|
* @param fulfilled function to be called with the fulfilled value
|
|
* @param rejected function to be called with the rejection exception
|
|
* @param progressed function to be called on any progress notifications
|
|
* @return promise for the return value from the invoked callback
|
|
*/
|
|
Q.when = when;
|
|
function when(value, fulfilled, rejected, progressed) {
|
|
return Q(value).then(fulfilled, rejected, progressed);
|
|
}
|
|
|
|
Promise.prototype.thenResolve = function (value) {
|
|
return this.then(function () { return value; });
|
|
};
|
|
|
|
Q.thenResolve = function (promise, value) {
|
|
return Q(promise).thenResolve(value);
|
|
};
|
|
|
|
Promise.prototype.thenReject = function (reason) {
|
|
return this.then(function () { throw reason; });
|
|
};
|
|
|
|
Q.thenReject = function (promise, reason) {
|
|
return Q(promise).thenReject(reason);
|
|
};
|
|
|
|
/**
|
|
* If an object is not a promise, it is as "near" as possible.
|
|
* If a promise is rejected, it is as "near" as possible too.
|
|
* If it’s a fulfilled promise, the fulfillment value is nearer.
|
|
* If it’s a deferred promise and the deferred has been resolved, the
|
|
* resolution is "nearer".
|
|
* @param object
|
|
* @returns most resolved (nearest) form of the object
|
|
*/
|
|
|
|
// XXX should we re-do this?
|
|
Q.nearer = nearer;
|
|
function nearer(value) {
|
|
if (isPromise(value)) {
|
|
var inspected = value.inspect();
|
|
if (inspected.state === "fulfilled") {
|
|
return inspected.value;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* @returns whether the given object is a promise.
|
|
* Otherwise it is a fulfilled value.
|
|
*/
|
|
Q.isPromise = isPromise;
|
|
function isPromise(object) {
|
|
return object instanceof Promise;
|
|
}
|
|
|
|
Q.isPromiseAlike = isPromiseAlike;
|
|
function isPromiseAlike(object) {
|
|
return isObject(object) && typeof object.then === "function";
|
|
}
|
|
|
|
/**
|
|
* @returns whether the given object is a pending promise, meaning not
|
|
* fulfilled or rejected.
|
|
*/
|
|
Q.isPending = isPending;
|
|
function isPending(object) {
|
|
return isPromise(object) && object.inspect().state === "pending";
|
|
}
|
|
|
|
Promise.prototype.isPending = function () {
|
|
return this.inspect().state === "pending";
|
|
};
|
|
|
|
/**
|
|
* @returns whether the given object is a value or fulfilled
|
|
* promise.
|
|
*/
|
|
Q.isFulfilled = isFulfilled;
|
|
function isFulfilled(object) {
|
|
return !isPromise(object) || object.inspect().state === "fulfilled";
|
|
}
|
|
|
|
Promise.prototype.isFulfilled = function () {
|
|
return this.inspect().state === "fulfilled";
|
|
};
|
|
|
|
/**
|
|
* @returns whether the given object is a rejected promise.
|
|
*/
|
|
Q.isRejected = isRejected;
|
|
function isRejected(object) {
|
|
return isPromise(object) && object.inspect().state === "rejected";
|
|
}
|
|
|
|
Promise.prototype.isRejected = function () {
|
|
return this.inspect().state === "rejected";
|
|
};
|
|
|
|
//// BEGIN UNHANDLED REJECTION TRACKING
|
|
|
|
// This promise library consumes exceptions thrown in handlers so they can be
|
|
// handled by a subsequent promise. The exceptions get added to this array when
|
|
// they are created, and removed when they are handled. Note that in ES6 or
|
|
// shimmed environments, this would naturally be a `Set`.
|
|
var unhandledReasons = [];
|
|
var unhandledRejections = [];
|
|
var reportedUnhandledRejections = [];
|
|
var trackUnhandledRejections = true;
|
|
|
|
function resetUnhandledRejections() {
|
|
unhandledReasons.length = 0;
|
|
unhandledRejections.length = 0;
|
|
|
|
if (!trackUnhandledRejections) {
|
|
trackUnhandledRejections = true;
|
|
}
|
|
}
|
|
|
|
function trackRejection(promise, reason) {
|
|
if (!trackUnhandledRejections) {
|
|
return;
|
|
}
|
|
if (typeof process === "object" && typeof process.emit === "function") {
|
|
Q.nextTick.runAfter(function () {
|
|
if (array_indexOf(unhandledRejections, promise) !== -1) {
|
|
process.emit("unhandledRejection", reason, promise);
|
|
reportedUnhandledRejections.push(promise);
|
|
}
|
|
});
|
|
}
|
|
|
|
unhandledRejections.push(promise);
|
|
if (reason && typeof reason.stack !== "undefined") {
|
|
unhandledReasons.push(reason.stack);
|
|
} else {
|
|
unhandledReasons.push("(no stack) " + reason);
|
|
}
|
|
}
|
|
|
|
function untrackRejection(promise) {
|
|
if (!trackUnhandledRejections) {
|
|
return;
|
|
}
|
|
|
|
var at = array_indexOf(unhandledRejections, promise);
|
|
if (at !== -1) {
|
|
if (typeof process === "object" && typeof process.emit === "function") {
|
|
Q.nextTick.runAfter(function () {
|
|
var atReport = array_indexOf(reportedUnhandledRejections, promise);
|
|
if (atReport !== -1) {
|
|
process.emit("rejectionHandled", unhandledReasons[at], promise);
|
|
reportedUnhandledRejections.splice(atReport, 1);
|
|
}
|
|
});
|
|
}
|
|
unhandledRejections.splice(at, 1);
|
|
unhandledReasons.splice(at, 1);
|
|
}
|
|
}
|
|
|
|
Q.resetUnhandledRejections = resetUnhandledRejections;
|
|
|
|
Q.getUnhandledReasons = function () {
|
|
// Make a copy so that consumers can't interfere with our internal state.
|
|
return unhandledReasons.slice();
|
|
};
|
|
|
|
Q.stopUnhandledRejectionTracking = function () {
|
|
resetUnhandledRejections();
|
|
trackUnhandledRejections = false;
|
|
};
|
|
|
|
resetUnhandledRejections();
|
|
|
|
//// END UNHANDLED REJECTION TRACKING
|
|
|
|
/**
|
|
* Constructs a rejected promise.
|
|
* @param reason value describing the failure
|
|
*/
|
|
Q.reject = reject;
|
|
function reject(reason) {
|
|
var rejection = Promise({
|
|
"when": function (rejected) {
|
|
// note that the error has been handled
|
|
if (rejected) {
|
|
untrackRejection(this);
|
|
}
|
|
return rejected ? rejected(reason) : this;
|
|
}
|
|
}, function fallback() {
|
|
return this;
|
|
}, function inspect() {
|
|
return { state: "rejected", reason: reason };
|
|
});
|
|
|
|
// Note that the reason has not been handled.
|
|
trackRejection(rejection, reason);
|
|
|
|
return rejection;
|
|
}
|
|
|
|
/**
|
|
* Constructs a fulfilled promise for an immediate reference.
|
|
* @param value immediate reference
|
|
*/
|
|
Q.fulfill = fulfill;
|
|
function fulfill(value) {
|
|
return Promise({
|
|
"when": function () {
|
|
return value;
|
|
},
|
|
"get": function (name) {
|
|
return value[name];
|
|
},
|
|
"set": function (name, rhs) {
|
|
value[name] = rhs;
|
|
},
|
|
"delete": function (name) {
|
|
delete value[name];
|
|
},
|
|
"post": function (name, args) {
|
|
// Mark Miller proposes that post with no name should apply a
|
|
// promised function.
|
|
if (name === null || name === void 0) {
|
|
return value.apply(void 0, args);
|
|
} else {
|
|
return value[name].apply(value, args);
|
|
}
|
|
},
|
|
"apply": function (thisp, args) {
|
|
return value.apply(thisp, args);
|
|
},
|
|
"keys": function () {
|
|
return object_keys(value);
|
|
}
|
|
}, void 0, function inspect() {
|
|
return { state: "fulfilled", value: value };
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Converts thenables to Q promises.
|
|
* @param promise thenable promise
|
|
* @returns a Q promise
|
|
*/
|
|
function coerce(promise) {
|
|
var deferred = defer();
|
|
Q.nextTick(function () {
|
|
try {
|
|
promise.then(deferred.resolve, deferred.reject, deferred.notify);
|
|
} catch (exception) {
|
|
deferred.reject(exception);
|
|
}
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
/**
|
|
* Annotates an object such that it will never be
|
|
* transferred away from this process over any promise
|
|
* communication channel.
|
|
* @param object
|
|
* @returns promise a wrapping of that object that
|
|
* additionally responds to the "isDef" message
|
|
* without a rejection.
|
|
*/
|
|
Q.master = master;
|
|
function master(object) {
|
|
return Promise({
|
|
"isDef": function () {}
|
|
}, function fallback(op, args) {
|
|
return dispatch(object, op, args);
|
|
}, function () {
|
|
return Q(object).inspect();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Spreads the values of a promised array of arguments into the
|
|
* fulfillment callback.
|
|
* @param fulfilled callback that receives variadic arguments from the
|
|
* promised array
|
|
* @param rejected callback that receives the exception if the promise
|
|
* is rejected.
|
|
* @returns a promise for the return value or thrown exception of
|
|
* either callback.
|
|
*/
|
|
Q.spread = spread;
|
|
function spread(value, fulfilled, rejected) {
|
|
return Q(value).spread(fulfilled, rejected);
|
|
}
|
|
|
|
Promise.prototype.spread = function (fulfilled, rejected) {
|
|
return this.all().then(function (array) {
|
|
return fulfilled.apply(void 0, array);
|
|
}, rejected);
|
|
};
|
|
|
|
/**
|
|
* The async function is a decorator for generator functions, turning
|
|
* them into asynchronous generators. Although generators are only part
|
|
* of the newest ECMAScript 6 drafts, this code does not cause syntax
|
|
* errors in older engines. This code should continue to work and will
|
|
* in fact improve over time as the language improves.
|
|
*
|
|
* ES6 generators are currently part of V8 version 3.19 with the
|
|
* --harmony-generators runtime flag enabled. SpiderMonkey has had them
|
|
* for longer, but under an older Python-inspired form. This function
|
|
* works on both kinds of generators.
|
|
*
|
|
* Decorates a generator function such that:
|
|
* - it may yield promises
|
|
* - execution will continue when that promise is fulfilled
|
|
* - the value of the yield expression will be the fulfilled value
|
|
* - it returns a promise for the return value (when the generator
|
|
* stops iterating)
|
|
* - the decorated function returns a promise for the return value
|
|
* of the generator or the first rejected promise among those
|
|
* yielded.
|
|
* - if an error is thrown in the generator, it propagates through
|
|
* every following yield until it is caught, or until it escapes
|
|
* the generator function altogether, and is translated into a
|
|
* rejection for the promise returned by the decorated generator.
|
|
*/
|
|
Q.async = async;
|
|
function async(makeGenerator) {
|
|
return function () {
|
|
// when verb is "send", arg is a value
|
|
// when verb is "throw", arg is an exception
|
|
function continuer(verb, arg) {
|
|
var result;
|
|
|
|
// Until V8 3.19 / Chromium 29 is released, SpiderMonkey is the only
|
|
// engine that has a deployed base of browsers that support generators.
|
|
// However, SM's generators use the Python-inspired semantics of
|
|
// outdated ES6 drafts. We would like to support ES6, but we'd also
|
|
// like to make it possible to use generators in deployed browsers, so
|
|
// we also support Python-style generators. At some point we can remove
|
|
// this block.
|
|
|
|
if (typeof StopIteration === "undefined") {
|
|
// ES6 Generators
|
|
try {
|
|
result = generator[verb](arg);
|
|
} catch (exception) {
|
|
return reject(exception);
|
|
}
|
|
if (result.done) {
|
|
return Q(result.value);
|
|
} else {
|
|
return when(result.value, callback, errback);
|
|
}
|
|
} else {
|
|
// SpiderMonkey Generators
|
|
// FIXME: Remove this case when SM does ES6 generators.
|
|
try {
|
|
result = generator[verb](arg);
|
|
} catch (exception) {
|
|
if (isStopIteration(exception)) {
|
|
return Q(exception.value);
|
|
} else {
|
|
return reject(exception);
|
|
}
|
|
}
|
|
return when(result, callback, errback);
|
|
}
|
|
}
|
|
var generator = makeGenerator.apply(this, arguments);
|
|
var callback = continuer.bind(continuer, "next");
|
|
var errback = continuer.bind(continuer, "throw");
|
|
return callback();
|
|
};
|
|
}
|
|
|
|
/**
|
|
* The spawn function is a small wrapper around async that immediately
|
|
* calls the generator and also ends the promise chain, so that any
|
|
* unhandled errors are thrown instead of forwarded to the error
|
|
* handler. This is useful because it's extremely common to run
|
|
* generators at the top-level to work with libraries.
|
|
*/
|
|
Q.spawn = spawn;
|
|
function spawn(makeGenerator) {
|
|
Q.done(Q.async(makeGenerator)());
|
|
}
|
|
|
|
// FIXME: Remove this interface once ES6 generators are in SpiderMonkey.
|
|
/**
|
|
* Throws a ReturnValue exception to stop an asynchronous generator.
|
|
*
|
|
* This interface is a stop-gap measure to support generator return
|
|
* values in older Firefox/SpiderMonkey. In browsers that support ES6
|
|
* generators like Chromium 29, just use "return" in your generator
|
|
* functions.
|
|
*
|
|
* @param value the return value for the surrounding generator
|
|
* @throws ReturnValue exception with the value.
|
|
* @example
|
|
* // ES6 style
|
|
* Q.async(function* () {
|
|
* var foo = yield getFooPromise();
|
|
* var bar = yield getBarPromise();
|
|
* return foo + bar;
|
|
* })
|
|
* // Older SpiderMonkey style
|
|
* Q.async(function () {
|
|
* var foo = yield getFooPromise();
|
|
* var bar = yield getBarPromise();
|
|
* Q.return(foo + bar);
|
|
* })
|
|
*/
|
|
Q["return"] = _return;
|
|
function _return(value) {
|
|
throw new QReturnValue(value);
|
|
}
|
|
|
|
/**
|
|
* The promised function decorator ensures that any promise arguments
|
|
* are settled and passed as values (`this` is also settled and passed
|
|
* as a value). It will also ensure that the result of a function is
|
|
* always a promise.
|
|
*
|
|
* @example
|
|
* var add = Q.promised(function (a, b) {
|
|
* return a + b;
|
|
* });
|
|
* add(Q(a), Q(B));
|
|
*
|
|
* @param {function} callback The function to decorate
|
|
* @returns {function} a function that has been decorated.
|
|
*/
|
|
Q.promised = promised;
|
|
function promised(callback) {
|
|
return function () {
|
|
return spread([this, all(arguments)], function (self, args) {
|
|
return callback.apply(self, args);
|
|
});
|
|
};
|
|
}
|
|
|
|
/**
|
|
* sends a message to a value in a future turn
|
|
* @param object* the recipient
|
|
* @param op the name of the message operation, e.g., "when",
|
|
* @param args further arguments to be forwarded to the operation
|
|
* @returns result {Promise} a promise for the result of the operation
|
|
*/
|
|
Q.dispatch = dispatch;
|
|
function dispatch(object, op, args) {
|
|
return Q(object).dispatch(op, args);
|
|
}
|
|
|
|
Promise.prototype.dispatch = function (op, args) {
|
|
var self = this;
|
|
var deferred = defer();
|
|
Q.nextTick(function () {
|
|
self.promiseDispatch(deferred.resolve, op, args);
|
|
});
|
|
return deferred.promise;
|
|
};
|
|
|
|
/**
|
|
* Gets the value of a property in a future turn.
|
|
* @param object promise or immediate reference for target object
|
|
* @param name name of property to get
|
|
* @return promise for the property value
|
|
*/
|
|
Q.get = function (object, key) {
|
|
return Q(object).dispatch("get", [key]);
|
|
};
|
|
|
|
Promise.prototype.get = function (key) {
|
|
return this.dispatch("get", [key]);
|
|
};
|
|
|
|
/**
|
|
* Sets the value of a property in a future turn.
|
|
* @param object promise or immediate reference for object object
|
|
* @param name name of property to set
|
|
* @param value new value of property
|
|
* @return promise for the return value
|
|
*/
|
|
Q.set = function (object, key, value) {
|
|
return Q(object).dispatch("set", [key, value]);
|
|
};
|
|
|
|
Promise.prototype.set = function (key, value) {
|
|
return this.dispatch("set", [key, value]);
|
|
};
|
|
|
|
/**
|
|
* Deletes a property in a future turn.
|
|
* @param object promise or immediate reference for target object
|
|
* @param name name of property to delete
|
|
* @return promise for the return value
|
|
*/
|
|
Q.del = // XXX legacy
|
|
Q["delete"] = function (object, key) {
|
|
return Q(object).dispatch("delete", [key]);
|
|
};
|
|
|
|
Promise.prototype.del = // XXX legacy
|
|
Promise.prototype["delete"] = function (key) {
|
|
return this.dispatch("delete", [key]);
|
|
};
|
|
|
|
/**
|
|
* Invokes a method in a future turn.
|
|
* @param object promise or immediate reference for target object
|
|
* @param name name of method to invoke
|
|
* @param value a value to post, typically an array of
|
|
* invocation arguments for promises that
|
|
* are ultimately backed with `resolve` values,
|
|
* as opposed to those backed with URLs
|
|
* wherein the posted value can be any
|
|
* JSON serializable object.
|
|
* @return promise for the return value
|
|
*/
|
|
// bound locally because it is used by other methods
|
|
Q.mapply = // XXX As proposed by "Redsandro"
|
|
Q.post = function (object, name, args) {
|
|
return Q(object).dispatch("post", [name, args]);
|
|
};
|
|
|
|
Promise.prototype.mapply = // XXX As proposed by "Redsandro"
|
|
Promise.prototype.post = function (name, args) {
|
|
return this.dispatch("post", [name, args]);
|
|
};
|
|
|
|
/**
|
|
* Invokes a method in a future turn.
|
|
* @param object promise or immediate reference for target object
|
|
* @param name name of method to invoke
|
|
* @param ...args array of invocation arguments
|
|
* @return promise for the return value
|
|
*/
|
|
Q.send = // XXX Mark Miller's proposed parlance
|
|
Q.mcall = // XXX As proposed by "Redsandro"
|
|
Q.invoke = function (object, name /*...args*/) {
|
|
return Q(object).dispatch("post", [name, array_slice(arguments, 2)]);
|
|
};
|
|
|
|
Promise.prototype.send = // XXX Mark Miller's proposed parlance
|
|
Promise.prototype.mcall = // XXX As proposed by "Redsandro"
|
|
Promise.prototype.invoke = function (name /*...args*/) {
|
|
return this.dispatch("post", [name, array_slice(arguments, 1)]);
|
|
};
|
|
|
|
/**
|
|
* Applies the promised function in a future turn.
|
|
* @param object promise or immediate reference for target function
|
|
* @param args array of application arguments
|
|
*/
|
|
Q.fapply = function (object, args) {
|
|
return Q(object).dispatch("apply", [void 0, args]);
|
|
};
|
|
|
|
Promise.prototype.fapply = function (args) {
|
|
return this.dispatch("apply", [void 0, args]);
|
|
};
|
|
|
|
/**
|
|
* Calls the promised function in a future turn.
|
|
* @param object promise or immediate reference for target function
|
|
* @param ...args array of application arguments
|
|
*/
|
|
Q["try"] =
|
|
Q.fcall = function (object /* ...args*/) {
|
|
return Q(object).dispatch("apply", [void 0, array_slice(arguments, 1)]);
|
|
};
|
|
|
|
Promise.prototype.fcall = function (/*...args*/) {
|
|
return this.dispatch("apply", [void 0, array_slice(arguments)]);
|
|
};
|
|
|
|
/**
|
|
* Binds the promised function, transforming return values into a fulfilled
|
|
* promise and thrown errors into a rejected one.
|
|
* @param object promise or immediate reference for target function
|
|
* @param ...args array of application arguments
|
|
*/
|
|
Q.fbind = function (object /*...args*/) {
|
|
var promise = Q(object);
|
|
var args = array_slice(arguments, 1);
|
|
return function fbound() {
|
|
return promise.dispatch("apply", [
|
|
this,
|
|
args.concat(array_slice(arguments))
|
|
]);
|
|
};
|
|
};
|
|
Promise.prototype.fbind = function (/*...args*/) {
|
|
var promise = this;
|
|
var args = array_slice(arguments);
|
|
return function fbound() {
|
|
return promise.dispatch("apply", [
|
|
this,
|
|
args.concat(array_slice(arguments))
|
|
]);
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Requests the names of the owned properties of a promised
|
|
* object in a future turn.
|
|
* @param object promise or immediate reference for target object
|
|
* @return promise for the keys of the eventually settled object
|
|
*/
|
|
Q.keys = function (object) {
|
|
return Q(object).dispatch("keys", []);
|
|
};
|
|
|
|
Promise.prototype.keys = function () {
|
|
return this.dispatch("keys", []);
|
|
};
|
|
|
|
/**
|
|
* Turns an array of promises into a promise for an array. If any of
|
|
* the promises gets rejected, the whole array is rejected immediately.
|
|
* @param {Array*} an array (or promise for an array) of values (or
|
|
* promises for values)
|
|
* @returns a promise for an array of the corresponding values
|
|
*/
|
|
// By Mark Miller
|
|
// http://wiki.ecmascript.org/doku.php?id=strawman:concurrency&rev=1308776521#allfulfilled
|
|
Q.all = all;
|
|
function all(promises) {
|
|
return when(promises, function (promises) {
|
|
var pendingCount = 0;
|
|
var deferred = defer();
|
|
array_reduce(promises, function (undefined, promise, index) {
|
|
var snapshot;
|
|
if (
|
|
isPromise(promise) &&
|
|
(snapshot = promise.inspect()).state === "fulfilled"
|
|
) {
|
|
promises[index] = snapshot.value;
|
|
} else {
|
|
++pendingCount;
|
|
when(
|
|
promise,
|
|
function (value) {
|
|
promises[index] = value;
|
|
if (--pendingCount === 0) {
|
|
deferred.resolve(promises);
|
|
}
|
|
},
|
|
deferred.reject,
|
|
function (progress) {
|
|
deferred.notify({ index: index, value: progress });
|
|
}
|
|
);
|
|
}
|
|
}, void 0);
|
|
if (pendingCount === 0) {
|
|
deferred.resolve(promises);
|
|
}
|
|
return deferred.promise;
|
|
});
|
|
}
|
|
|
|
Promise.prototype.all = function () {
|
|
return all(this);
|
|
};
|
|
|
|
/**
|
|
* Returns the first resolved promise of an array. Prior rejected promises are
|
|
* ignored. Rejects only if all promises are rejected.
|
|
* @param {Array*} an array containing values or promises for values
|
|
* @returns a promise fulfilled with the value of the first resolved promise,
|
|
* or a rejected promise if all promises are rejected.
|
|
*/
|
|
Q.any = any;
|
|
|
|
function any(promises) {
|
|
if (promises.length === 0) {
|
|
return Q.resolve();
|
|
}
|
|
|
|
var deferred = Q.defer();
|
|
var pendingCount = 0;
|
|
array_reduce(promises, function (prev, current, index) {
|
|
var promise = promises[index];
|
|
|
|
pendingCount++;
|
|
|
|
when(promise, onFulfilled, onRejected, onProgress);
|
|
function onFulfilled(result) {
|
|
deferred.resolve(result);
|
|
}
|
|
function onRejected() {
|
|
pendingCount--;
|
|
if (pendingCount === 0) {
|
|
deferred.reject(new Error(
|
|
"Can't get fulfillment value from any promise, all " +
|
|
"promises were rejected."
|
|
));
|
|
}
|
|
}
|
|
function onProgress(progress) {
|
|
deferred.notify({
|
|
index: index,
|
|
value: progress
|
|
});
|
|
}
|
|
}, undefined);
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
Promise.prototype.any = function () {
|
|
return any(this);
|
|
};
|
|
|
|
/**
|
|
* Waits for all promises to be settled, either fulfilled or
|
|
* rejected. This is distinct from `all` since that would stop
|
|
* waiting at the first rejection. The promise returned by
|
|
* `allResolved` will never be rejected.
|
|
* @param promises a promise for an array (or an array) of promises
|
|
* (or values)
|
|
* @return a promise for an array of promises
|
|
*/
|
|
Q.allResolved = deprecate(allResolved, "allResolved", "allSettled");
|
|
function allResolved(promises) {
|
|
return when(promises, function (promises) {
|
|
promises = array_map(promises, Q);
|
|
return when(all(array_map(promises, function (promise) {
|
|
return when(promise, noop, noop);
|
|
})), function () {
|
|
return promises;
|
|
});
|
|
});
|
|
}
|
|
|
|
Promise.prototype.allResolved = function () {
|
|
return allResolved(this);
|
|
};
|
|
|
|
/**
|
|
* @see Promise#allSettled
|
|
*/
|
|
Q.allSettled = allSettled;
|
|
function allSettled(promises) {
|
|
return Q(promises).allSettled();
|
|
}
|
|
|
|
/**
|
|
* Turns an array of promises into a promise for an array of their states (as
|
|
* returned by `inspect`) when they have all settled.
|
|
* @param {Array[Any*]} values an array (or promise for an array) of values (or
|
|
* promises for values)
|
|
* @returns {Array[State]} an array of states for the respective values.
|
|
*/
|
|
Promise.prototype.allSettled = function () {
|
|
return this.then(function (promises) {
|
|
return all(array_map(promises, function (promise) {
|
|
promise = Q(promise);
|
|
function regardless() {
|
|
return promise.inspect();
|
|
}
|
|
return promise.then(regardless, regardless);
|
|
}));
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Captures the failure of a promise, giving an oportunity to recover
|
|
* with a callback. If the given promise is fulfilled, the returned
|
|
* promise is fulfilled.
|
|
* @param {Any*} promise for something
|
|
* @param {Function} callback to fulfill the returned promise if the
|
|
* given promise is rejected
|
|
* @returns a promise for the return value of the callback
|
|
*/
|
|
Q.fail = // XXX legacy
|
|
Q["catch"] = function (object, rejected) {
|
|
return Q(object).then(void 0, rejected);
|
|
};
|
|
|
|
Promise.prototype.fail = // XXX legacy
|
|
Promise.prototype["catch"] = function (rejected) {
|
|
return this.then(void 0, rejected);
|
|
};
|
|
|
|
/**
|
|
* Attaches a listener that can respond to progress notifications from a
|
|
* promise's originating deferred. This listener receives the exact arguments
|
|
* passed to ``deferred.notify``.
|
|
* @param {Any*} promise for something
|
|
* @param {Function} callback to receive any progress notifications
|
|
* @returns the given promise, unchanged
|
|
*/
|
|
Q.progress = progress;
|
|
function progress(object, progressed) {
|
|
return Q(object).then(void 0, void 0, progressed);
|
|
}
|
|
|
|
Promise.prototype.progress = function (progressed) {
|
|
return this.then(void 0, void 0, progressed);
|
|
};
|
|
|
|
/**
|
|
* Provides an opportunity to observe the settling of a promise,
|
|
* regardless of whether the promise is fulfilled or rejected. Forwards
|
|
* the resolution to the returned promise when the callback is done.
|
|
* The callback can return a promise to defer completion.
|
|
* @param {Any*} promise
|
|
* @param {Function} callback to observe the resolution of the given
|
|
* promise, takes no arguments.
|
|
* @returns a promise for the resolution of the given promise when
|
|
* ``fin`` is done.
|
|
*/
|
|
Q.fin = // XXX legacy
|
|
Q["finally"] = function (object, callback) {
|
|
return Q(object)["finally"](callback);
|
|
};
|
|
|
|
Promise.prototype.fin = // XXX legacy
|
|
Promise.prototype["finally"] = function (callback) {
|
|
callback = Q(callback);
|
|
return this.then(function (value) {
|
|
return callback.fcall().then(function () {
|
|
return value;
|
|
});
|
|
}, function (reason) {
|
|
// TODO attempt to recycle the rejection with "this".
|
|
return callback.fcall().then(function () {
|
|
throw reason;
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Terminates a chain of promises, forcing rejections to be
|
|
* thrown as exceptions.
|
|
* @param {Any*} promise at the end of a chain of promises
|
|
* @returns nothing
|
|
*/
|
|
Q.done = function (object, fulfilled, rejected, progress) {
|
|
return Q(object).done(fulfilled, rejected, progress);
|
|
};
|
|
|
|
Promise.prototype.done = function (fulfilled, rejected, progress) {
|
|
var onUnhandledError = function (error) {
|
|
// forward to a future turn so that ``when``
|
|
// does not catch it and turn it into a rejection.
|
|
Q.nextTick(function () {
|
|
makeStackTraceLong(error, promise);
|
|
if (Q.onerror) {
|
|
Q.onerror(error);
|
|
} else {
|
|
throw error;
|
|
}
|
|
});
|
|
};
|
|
|
|
// Avoid unnecessary `nextTick`ing via an unnecessary `when`.
|
|
var promise = fulfilled || rejected || progress ?
|
|
this.then(fulfilled, rejected, progress) :
|
|
this;
|
|
|
|
if (typeof process === "object" && process && process.domain) {
|
|
onUnhandledError = process.domain.bind(onUnhandledError);
|
|
}
|
|
|
|
promise.then(void 0, onUnhandledError);
|
|
};
|
|
|
|
/**
|
|
* Causes a promise to be rejected if it does not get fulfilled before
|
|
* some milliseconds time out.
|
|
* @param {Any*} promise
|
|
* @param {Number} milliseconds timeout
|
|
* @param {Any*} custom error message or Error object (optional)
|
|
* @returns a promise for the resolution of the given promise if it is
|
|
* fulfilled before the timeout, otherwise rejected.
|
|
*/
|
|
Q.timeout = function (object, ms, error) {
|
|
return Q(object).timeout(ms, error);
|
|
};
|
|
|
|
Promise.prototype.timeout = function (ms, error) {
|
|
var deferred = defer();
|
|
var timeoutId = setTimeout(function () {
|
|
if (!error || "string" === typeof error) {
|
|
error = new Error(error || "Timed out after " + ms + " ms");
|
|
error.code = "ETIMEDOUT";
|
|
}
|
|
deferred.reject(error);
|
|
}, ms);
|
|
|
|
this.then(function (value) {
|
|
clearTimeout(timeoutId);
|
|
deferred.resolve(value);
|
|
}, function (exception) {
|
|
clearTimeout(timeoutId);
|
|
deferred.reject(exception);
|
|
}, deferred.notify);
|
|
|
|
return deferred.promise;
|
|
};
|
|
|
|
/**
|
|
* Returns a promise for the given value (or promised value), some
|
|
* milliseconds after it resolved. Passes rejections immediately.
|
|
* @param {Any*} promise
|
|
* @param {Number} milliseconds
|
|
* @returns a promise for the resolution of the given promise after milliseconds
|
|
* time has elapsed since the resolution of the given promise.
|
|
* If the given promise rejects, that is passed immediately.
|
|
*/
|
|
Q.delay = function (object, timeout) {
|
|
if (timeout === void 0) {
|
|
timeout = object;
|
|
object = void 0;
|
|
}
|
|
return Q(object).delay(timeout);
|
|
};
|
|
|
|
Promise.prototype.delay = function (timeout) {
|
|
return this.then(function (value) {
|
|
var deferred = defer();
|
|
setTimeout(function () {
|
|
deferred.resolve(value);
|
|
}, timeout);
|
|
return deferred.promise;
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Passes a continuation to a Node function, which is called with the given
|
|
* arguments provided as an array, and returns a promise.
|
|
*
|
|
* Q.nfapply(FS.readFile, [__filename])
|
|
* .then(function (content) {
|
|
* })
|
|
*
|
|
*/
|
|
Q.nfapply = function (callback, args) {
|
|
return Q(callback).nfapply(args);
|
|
};
|
|
|
|
Promise.prototype.nfapply = function (args) {
|
|
var deferred = defer();
|
|
var nodeArgs = array_slice(args);
|
|
nodeArgs.push(deferred.makeNodeResolver());
|
|
this.fapply(nodeArgs).fail(deferred.reject);
|
|
return deferred.promise;
|
|
};
|
|
|
|
/**
|
|
* Passes a continuation to a Node function, which is called with the given
|
|
* arguments provided individually, and returns a promise.
|
|
* @example
|
|
* Q.nfcall(FS.readFile, __filename)
|
|
* .then(function (content) {
|
|
* })
|
|
*
|
|
*/
|
|
Q.nfcall = function (callback /*...args*/) {
|
|
var args = array_slice(arguments, 1);
|
|
return Q(callback).nfapply(args);
|
|
};
|
|
|
|
Promise.prototype.nfcall = function (/*...args*/) {
|
|
var nodeArgs = array_slice(arguments);
|
|
var deferred = defer();
|
|
nodeArgs.push(deferred.makeNodeResolver());
|
|
this.fapply(nodeArgs).fail(deferred.reject);
|
|
return deferred.promise;
|
|
};
|
|
|
|
/**
|
|
* Wraps a NodeJS continuation passing function and returns an equivalent
|
|
* version that returns a promise.
|
|
* @example
|
|
* Q.nfbind(FS.readFile, __filename)("utf-8")
|
|
* .then(console.log)
|
|
* .done()
|
|
*/
|
|
Q.nfbind =
|
|
Q.denodeify = function (callback /*...args*/) {
|
|
var baseArgs = array_slice(arguments, 1);
|
|
return function () {
|
|
var nodeArgs = baseArgs.concat(array_slice(arguments));
|
|
var deferred = defer();
|
|
nodeArgs.push(deferred.makeNodeResolver());
|
|
Q(callback).fapply(nodeArgs).fail(deferred.reject);
|
|
return deferred.promise;
|
|
};
|
|
};
|
|
|
|
Promise.prototype.nfbind =
|
|
Promise.prototype.denodeify = function (/*...args*/) {
|
|
var args = array_slice(arguments);
|
|
args.unshift(this);
|
|
return Q.denodeify.apply(void 0, args);
|
|
};
|
|
|
|
Q.nbind = function (callback, thisp /*...args*/) {
|
|
var baseArgs = array_slice(arguments, 2);
|
|
return function () {
|
|
var nodeArgs = baseArgs.concat(array_slice(arguments));
|
|
var deferred = defer();
|
|
nodeArgs.push(deferred.makeNodeResolver());
|
|
function bound() {
|
|
return callback.apply(thisp, arguments);
|
|
}
|
|
Q(bound).fapply(nodeArgs).fail(deferred.reject);
|
|
return deferred.promise;
|
|
};
|
|
};
|
|
|
|
Promise.prototype.nbind = function (/*thisp, ...args*/) {
|
|
var args = array_slice(arguments, 0);
|
|
args.unshift(this);
|
|
return Q.nbind.apply(void 0, args);
|
|
};
|
|
|
|
/**
|
|
* Calls a method of a Node-style object that accepts a Node-style
|
|
* callback with a given array of arguments, plus a provided callback.
|
|
* @param object an object that has the named method
|
|
* @param {String} name name of the method of object
|
|
* @param {Array} args arguments to pass to the method; the callback
|
|
* will be provided by Q and appended to these arguments.
|
|
* @returns a promise for the value or error
|
|
*/
|
|
Q.nmapply = // XXX As proposed by "Redsandro"
|
|
Q.npost = function (object, name, args) {
|
|
return Q(object).npost(name, args);
|
|
};
|
|
|
|
Promise.prototype.nmapply = // XXX As proposed by "Redsandro"
|
|
Promise.prototype.npost = function (name, args) {
|
|
var nodeArgs = array_slice(args || []);
|
|
var deferred = defer();
|
|
nodeArgs.push(deferred.makeNodeResolver());
|
|
this.dispatch("post", [name, nodeArgs]).fail(deferred.reject);
|
|
return deferred.promise;
|
|
};
|
|
|
|
/**
|
|
* Calls a method of a Node-style object that accepts a Node-style
|
|
* callback, forwarding the given variadic arguments, plus a provided
|
|
* callback argument.
|
|
* @param object an object that has the named method
|
|
* @param {String} name name of the method of object
|
|
* @param ...args arguments to pass to the method; the callback will
|
|
* be provided by Q and appended to these arguments.
|
|
* @returns a promise for the value or error
|
|
*/
|
|
Q.nsend = // XXX Based on Mark Miller's proposed "send"
|
|
Q.nmcall = // XXX Based on "Redsandro's" proposal
|
|
Q.ninvoke = function (object, name /*...args*/) {
|
|
var nodeArgs = array_slice(arguments, 2);
|
|
var deferred = defer();
|
|
nodeArgs.push(deferred.makeNodeResolver());
|
|
Q(object).dispatch("post", [name, nodeArgs]).fail(deferred.reject);
|
|
return deferred.promise;
|
|
};
|
|
|
|
Promise.prototype.nsend = // XXX Based on Mark Miller's proposed "send"
|
|
Promise.prototype.nmcall = // XXX Based on "Redsandro's" proposal
|
|
Promise.prototype.ninvoke = function (name /*...args*/) {
|
|
var nodeArgs = array_slice(arguments, 1);
|
|
var deferred = defer();
|
|
nodeArgs.push(deferred.makeNodeResolver());
|
|
this.dispatch("post", [name, nodeArgs]).fail(deferred.reject);
|
|
return deferred.promise;
|
|
};
|
|
|
|
/**
|
|
* If a function would like to support both Node continuation-passing-style and
|
|
* promise-returning-style, it can end its internal promise chain with
|
|
* `nodeify(nodeback)`, forwarding the optional nodeback argument. If the user
|
|
* elects to use a nodeback, the result will be sent there. If they do not
|
|
* pass a nodeback, they will receive the result promise.
|
|
* @param object a result (or a promise for a result)
|
|
* @param {Function} nodeback a Node.js-style callback
|
|
* @returns either the promise or nothing
|
|
*/
|
|
Q.nodeify = nodeify;
|
|
function nodeify(object, nodeback) {
|
|
return Q(object).nodeify(nodeback);
|
|
}
|
|
|
|
Promise.prototype.nodeify = function (nodeback) {
|
|
if (nodeback) {
|
|
this.then(function (value) {
|
|
Q.nextTick(function () {
|
|
nodeback(null, value);
|
|
});
|
|
}, function (error) {
|
|
Q.nextTick(function () {
|
|
nodeback(error);
|
|
});
|
|
});
|
|
} else {
|
|
return this;
|
|
}
|
|
};
|
|
|
|
// All code before this point will be filtered from stack traces.
|
|
var qEndingLine = captureLine();
|
|
|
|
return Q;
|
|
|
|
});
|
|
|
|
}).call(this,require("/Users/bumpmann/Desktop/projects/oauth-js-github/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"))
|
|
},{"/Users/bumpmann/Desktop/projects/oauth-js-github/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":15}]},{},[8])
|