Last active
March 30, 2020 05:47
-
-
Save rcook/360f788a00f66194269d1efb42de3f80 to your computer and use it in GitHub Desktop.
Demonstration of hello.js page display login mode
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<script src="/jquery.min.js"></script> | |
<script src="/hello.js"></script> | |
<script src="/hello-ynab.js"></script> | |
<script> | |
$(() => { | |
const NETWORK = "ynab"; | |
const YNAB_CLIENT_ID = "REDACTED"; | |
const Auth = { | |
init: () => { hello.init({ ynab: YNAB_CLIENT_ID }); return Promise.resolve(null); }, | |
isSignedIn: () => hello(NETWORK).getAuthResponse() != null, | |
signIn: () => hello(NETWORK).login({ display: "page", force: true }), | |
signOut: () => hello(NETWORK).logout() | |
}; | |
Auth | |
.init() | |
.then(() => { | |
if (Auth.isSignedIn()) { | |
$("#sign-out").on("click", e => Auth.signOut().then(() => { window.location = "/"; })).show(); | |
} | |
else { | |
Auth.signIn(); | |
} | |
}); | |
}); | |
</script> | |
</head> | |
<body> | |
<button id="sign-out" style="display: none">Sign out</button> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(hello => { | |
function User(id) { | |
this.id = id; | |
} | |
hello.init({ | |
ynab: { | |
oauth: { | |
version: 2, | |
auth: "https://app.youneedabudget.com/oauth/authorize", | |
response_type: "token" | |
}, | |
base: "https://api.youneedabudget.com/v1/", | |
get: { me: "user" }, | |
wrap: { me: obj => new User(obj.data.user.id) } | |
} | |
}); | |
})(hello); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @hello.js | |
* | |
* HelloJS is a client side Javascript SDK for making OAuth2 logins and subsequent REST calls. | |
* | |
* @author Andrew Dodson | |
* @website https://adodson.com/hello.js/ | |
* | |
* @copyright Andrew Dodson, 2012 - 2015 | |
* @license MIT: You are free to use and modify this code for any use, on the condition that this copyright notice remains. | |
*/ | |
var hello = function(name) { | |
return hello.use(name); | |
}; | |
hello.utils = { | |
// Extend the first object with the properties and methods of the second | |
extend: function(r /*, a[, b[, ...]] */) { | |
// Get the arguments as an array but ommit the initial item | |
Array.prototype.slice.call(arguments, 1).forEach(function(a) { | |
if (Array.isArray(r) && Array.isArray(a)) { | |
Array.prototype.push.apply(r, a); | |
} | |
else if (r && (r instanceof Object || typeof r === 'object') && a && (a instanceof Object || typeof a === 'object') && r !== a) { | |
for (var x in a) { | |
r[x] = hello.utils.extend(r[x], a[x]); | |
} | |
} | |
else { | |
if (Array.isArray(a)) { | |
// Clone it | |
a = a.slice(0); | |
} | |
r = a; | |
} | |
}); | |
return r; | |
} | |
}; | |
// Core library | |
hello.utils.extend(hello, { | |
settings: { | |
// OAuth2 authentication defaults | |
redirect_uri: window.location.href.split('#')[0], | |
response_type: 'token', | |
display: 'popup', | |
state: '', | |
// OAuth1 shim | |
// The path to the OAuth1 server for signing user requests | |
// Want to recreate your own? Checkout https://github.com/MrSwitch/node-oauth-shim | |
oauth_proxy: 'https://auth-server.herokuapp.com/proxy', | |
// API timeout in milliseconds | |
timeout: 20000, | |
// Popup Options | |
popup: { | |
resizable: 1, | |
scrollbars: 1, | |
width: 500, | |
height: 550 | |
}, | |
// Default scope | |
// Many services require atleast a profile scope, | |
// HelloJS automatially includes the value of provider.scope_map.basic | |
// If that's not required it can be removed via hello.settings.scope.length = 0; | |
scope: ['basic'], | |
// Scope Maps | |
// This is the default module scope, these are the defaults which each service is mapped too. | |
// By including them here it prevents the scope from being applied accidentally | |
scope_map: { | |
basic: '' | |
}, | |
// Default service / network | |
default_service: null, | |
// Force authentication | |
// When hello.login is fired. | |
// (null): ignore current session expiry and continue with login | |
// (true): ignore current session expiry and continue with login, ask for user to reauthenticate | |
// (false): if the current session looks good for the request scopes return the current session. | |
force: null, | |
// Page URL | |
// When 'display=page' this property defines where the users page should end up after redirect_uri | |
// Ths could be problematic if the redirect_uri is indeed the final place, | |
// Typically this circumvents the problem of the redirect_url being a dumb relay page. | |
page_uri: window.location.href | |
}, | |
// Service configuration objects | |
services: {}, | |
// Use | |
// Define a new instance of the HelloJS library with a default service | |
use: function(service) { | |
// Create self, which inherits from its parent | |
var self = Object.create(this); | |
// Inherit the prototype from its parent | |
self.settings = Object.create(this.settings); | |
// Define the default service | |
if (service) { | |
self.settings.default_service = service; | |
} | |
// Create an instance of Events | |
self.utils.Event.call(self); | |
return self; | |
}, | |
// Initialize | |
// Define the client_ids for the endpoint services | |
// @param object o, contains a key value pair, service => clientId | |
// @param object opts, contains a key value pair of options used for defining the authentication defaults | |
// @param number timeout, timeout in seconds | |
init: function(services, options) { | |
var utils = this.utils; | |
if (!services) { | |
return this.services; | |
} | |
// Define provider credentials | |
// Reformat the ID field | |
for (var x in services) {if (services.hasOwnProperty(x)) { | |
if (typeof (services[x]) !== 'object') { | |
services[x] = {id: services[x]}; | |
} | |
}} | |
// Merge services if there already exists some | |
utils.extend(this.services, services); | |
// Update the default settings with this one. | |
if (options) { | |
utils.extend(this.settings, options); | |
// Do this immediatly incase the browser changes the current path. | |
if ('redirect_uri' in options) { | |
this.settings.redirect_uri = utils.url(options.redirect_uri).href; | |
} | |
} | |
return this; | |
}, | |
// Login | |
// Using the endpoint | |
// @param network stringify name to connect to | |
// @param options object (optional) {display mode, is either none|popup(default)|page, scope: email,birthday,publish, .. } | |
// @param callback function (optional) fired on signin | |
login: function() { | |
// Create an object which inherits its parent as the prototype and constructs a new event chain. | |
var _this = this; | |
var utils = _this.utils; | |
var error = utils.error; | |
var promise = utils.Promise(); | |
// Get parameters | |
var p = utils.args({network: 's', options: 'o', callback: 'f'}, arguments); | |
// Local vars | |
var url; | |
// Get all the custom options and store to be appended to the querystring | |
var qs = utils.diffKey(p.options, _this.settings); | |
// Merge/override options with app defaults | |
var opts = p.options = utils.merge(_this.settings, p.options || {}); | |
// Merge/override options with app defaults | |
opts.popup = utils.merge(_this.settings.popup, p.options.popup || {}); | |
// Network | |
p.network = p.network || _this.settings.default_service; | |
// Bind callback to both reject and fulfill states | |
promise.proxy.then(p.callback, p.callback); | |
// Trigger an event on the global listener | |
function emit(s, value) { | |
hello.emit(s, value); | |
} | |
promise.proxy.then(emit.bind(this, 'auth.login auth'), emit.bind(this, 'auth.failed auth')); | |
// Is our service valid? | |
if (typeof (p.network) !== 'string' || !(p.network in _this.services)) { | |
// Trigger the default login. | |
// Ahh we dont have one. | |
return promise.reject(error('invalid_network', 'The provided network was not recognized')); | |
} | |
var provider = _this.services[p.network]; | |
// Create a global listener to capture events triggered out of scope | |
var callbackId = utils.globalEvent(function(str) { | |
// The responseHandler returns a string, lets save this locally | |
var obj; | |
if (str) { | |
obj = JSON.parse(str); | |
} | |
else { | |
obj = error('cancelled', 'The authentication was not completed'); | |
} | |
// Handle these response using the local | |
// Trigger on the parent | |
if (!obj.error) { | |
// Save on the parent window the new credentials | |
// This fixes an IE10 bug i think... atleast it does for me. | |
utils.store(obj.network, obj); | |
// Fulfill a successful login | |
promise.fulfill({ | |
network: obj.network, | |
authResponse: obj | |
}); | |
} | |
else { | |
// Reject a successful login | |
promise.reject(obj); | |
} | |
}); | |
var redirectUri = utils.url(opts.redirect_uri).href; | |
// May be a space-delimited list of multiple, complementary types | |
var responseType = provider.oauth.response_type || opts.response_type; | |
// Fallback to token if the module hasn't defined a grant url | |
if (/\bcode\b/.test(responseType) && !provider.oauth.grant) { | |
responseType = responseType.replace(/\bcode\b/, 'token'); | |
} | |
// Query string parameters, we may pass our own arguments to form the querystring | |
p.qs = utils.merge(qs, { | |
client_id: encodeURIComponent(provider.id), | |
response_type: encodeURIComponent(responseType), | |
redirect_uri: encodeURIComponent(redirectUri), | |
state: { | |
client_id: provider.id, | |
network: p.network, | |
display: opts.display, | |
callback: callbackId, | |
state: opts.state, | |
redirect_uri: redirectUri | |
} | |
}); | |
// Get current session for merging scopes, and for quick auth response | |
var session = utils.store(p.network); | |
// Scopes (authentication permisions) | |
// Ensure this is a string - IE has a problem moving Arrays between windows | |
// Append the setup scope | |
var SCOPE_SPLIT = /[,\s]+/; | |
// Include default scope settings (cloned). | |
var scope = _this.settings.scope ? [_this.settings.scope.toString()] : []; | |
// Extend the providers scope list with the default | |
var scopeMap = utils.merge(_this.settings.scope_map, provider.scope || {}); | |
// Add user defined scopes... | |
if (opts.scope) { | |
scope.push(opts.scope.toString()); | |
} | |
// Append scopes from a previous session. | |
// This helps keep app credentials constant, | |
// Avoiding having to keep tabs on what scopes are authorized | |
if (session && 'scope' in session && session.scope instanceof String) { | |
scope.push(session.scope); | |
} | |
// Join and Split again | |
scope = scope.join(',').split(SCOPE_SPLIT); | |
// Format remove duplicates and empty values | |
scope = utils.unique(scope).filter(filterEmpty); | |
// Save the the scopes to the state with the names that they were requested with. | |
p.qs.state.scope = scope.join(','); | |
// Map scopes to the providers naming convention | |
scope = scope.map(function(item) { | |
// Does this have a mapping? | |
return (item in scopeMap) ? scopeMap[item] : item; | |
}); | |
// Stringify and Arrayify so that double mapped scopes are given the chance to be formatted | |
scope = scope.join(',').split(SCOPE_SPLIT); | |
// Again... | |
// Format remove duplicates and empty values | |
scope = utils.unique(scope).filter(filterEmpty); | |
// Join with the expected scope delimiter into a string | |
p.qs.scope = scope.join(provider.scope_delim || ','); | |
// Is the user already signed in with the appropriate scopes, valid access_token? | |
if (opts.force === false) { | |
if (session && 'access_token' in session && session.access_token && 'expires' in session && session.expires > ((new Date()).getTime() / 1e3)) { | |
// What is different about the scopes in the session vs the scopes in the new login? | |
var diff = utils.diff((session.scope || '').split(SCOPE_SPLIT), (p.qs.state.scope || '').split(SCOPE_SPLIT)); | |
if (diff.length === 0) { | |
// OK trigger the callback | |
promise.fulfill({ | |
unchanged: true, | |
network: p.network, | |
authResponse: session | |
}); | |
// Nothing has changed | |
return promise; | |
} | |
} | |
} | |
// Page URL | |
if (opts.display === 'page' && opts.page_uri) { | |
// Add a page location, place to endup after session has authenticated | |
p.qs.state.page_uri = utils.url(opts.page_uri).href; | |
} | |
// Bespoke | |
// Override login querystrings from auth_options | |
if ('login' in provider && typeof (provider.login) === 'function') { | |
// Format the paramaters according to the providers formatting function | |
provider.login(p); | |
} | |
// Add OAuth to state | |
// Where the service is going to take advantage of the oauth_proxy | |
if (!/\btoken\b/.test(responseType) || | |
parseInt(provider.oauth.version, 10) < 2 || | |
(opts.display === 'none' && provider.oauth.grant && session && session.refresh_token)) { | |
// Add the oauth endpoints | |
p.qs.state.oauth = provider.oauth; | |
// Add the proxy url | |
p.qs.state.oauth_proxy = opts.oauth_proxy; | |
} | |
// Convert state to a string | |
p.qs.state = encodeURIComponent(JSON.stringify(p.qs.state)); | |
// URL | |
if (parseInt(provider.oauth.version, 10) === 1) { | |
// Turn the request to the OAuth Proxy for 3-legged auth | |
url = utils.qs(opts.oauth_proxy, p.qs, encodeFunction); | |
} | |
// Refresh token | |
else if (opts.display === 'none' && provider.oauth.grant && session && session.refresh_token) { | |
// Add the refresh_token to the request | |
p.qs.refresh_token = session.refresh_token; | |
// Define the request path | |
url = utils.qs(opts.oauth_proxy, p.qs, encodeFunction); | |
} | |
else { | |
url = utils.qs(provider.oauth.auth, p.qs, encodeFunction); | |
} | |
// Broadcast this event as an auth:init | |
emit('auth.init', p); | |
// Execute | |
// Trigger how we want self displayed | |
if (opts.display === 'none') { | |
// Sign-in in the background, iframe | |
utils.iframe(url, redirectUri); | |
} | |
// Triggering popup? | |
else if (opts.display === 'popup') { | |
var popup = utils.popup(url, redirectUri, opts.popup); | |
var timer = setInterval(function() { | |
if (!popup || popup.closed) { | |
clearInterval(timer); | |
if (!promise.state) { | |
var response = error('cancelled', 'Login has been cancelled'); | |
if (!popup) { | |
response = error('blocked', 'Popup was blocked'); | |
} | |
response.network = p.network; | |
promise.reject(response); | |
} | |
} | |
}, 100); | |
} | |
else { | |
window.location = url; | |
} | |
return promise.proxy; | |
function encodeFunction(s) {return s;} | |
function filterEmpty(s) {return !!s;} | |
}, | |
// Remove any data associated with a given service | |
// @param string name of the service | |
// @param function callback | |
logout: function() { | |
var _this = this; | |
var utils = _this.utils; | |
var error = utils.error; | |
// Create a new promise | |
var promise = utils.Promise(); | |
var p = utils.args({name:'s', options: 'o', callback: 'f'}, arguments); | |
p.options = p.options || {}; | |
// Add callback to events | |
promise.proxy.then(p.callback, p.callback); | |
// Trigger an event on the global listener | |
function emit(s, value) { | |
hello.emit(s, value); | |
} | |
promise.proxy.then(emit.bind(this, 'auth.logout auth'), emit.bind(this, 'error')); | |
// Network | |
p.name = p.name || this.settings.default_service; | |
p.authResponse = utils.store(p.name); | |
if (p.name && !(p.name in _this.services)) { | |
promise.reject(error('invalid_network', 'The network was unrecognized')); | |
} | |
else if (p.name && p.authResponse) { | |
// Define the callback | |
var callback = function(opts) { | |
// Remove from the store | |
utils.store(p.name, null); | |
// Emit events by default | |
promise.fulfill(hello.utils.merge({network:p.name}, opts || {})); | |
}; | |
// Run an async operation to remove the users session | |
var _opts = {}; | |
if (p.options.force) { | |
var logout = _this.services[p.name].logout; | |
if (logout) { | |
// Convert logout to URL string, | |
// If no string is returned, then this function will handle the logout async style | |
if (typeof (logout) === 'function') { | |
logout = logout(callback, p); | |
} | |
// If logout is a string then assume URL and open in iframe. | |
if (typeof (logout) === 'string') { | |
utils.iframe(logout); | |
_opts.force = null; | |
_opts.message = 'Logout success on providers site was indeterminate'; | |
} | |
else if (logout === undefined) { | |
// The callback function will handle the response. | |
return promise.proxy; | |
} | |
} | |
} | |
// Remove local credentials | |
callback(_opts); | |
} | |
else { | |
promise.reject(error('invalid_session', 'There was no session to remove')); | |
} | |
return promise.proxy; | |
}, | |
// Returns all the sessions that are subscribed too | |
// @param string optional, name of the service to get information about. | |
getAuthResponse: function(service) { | |
// If the service doesn't exist | |
service = service || this.settings.default_service; | |
if (!service || !(service in this.services)) { | |
return null; | |
} | |
return this.utils.store(service) || null; | |
}, | |
// Events: placeholder for the events | |
events: {} | |
}); | |
// Core utilities | |
hello.utils.extend(hello.utils, { | |
// Error | |
error: function(code, message) { | |
return { | |
error: { | |
code: code, | |
message: message | |
} | |
}; | |
}, | |
// Append the querystring to a url | |
// @param string url | |
// @param object parameters | |
qs: function(url, params, formatFunction) { | |
if (params) { | |
// Set default formatting function | |
formatFunction = formatFunction || encodeURIComponent; | |
// Override the items in the URL which already exist | |
for (var x in params) { | |
var str = '([\\?\\&])' + x + '=[^\\&]*'; | |
var reg = new RegExp(str); | |
if (url.match(reg)) { | |
url = url.replace(reg, '$1' + x + '=' + formatFunction(params[x])); | |
delete params[x]; | |
} | |
} | |
} | |
if (!this.isEmpty(params)) { | |
return url + (url.indexOf('?') > -1 ? '&' : '?') + this.param(params, formatFunction); | |
} | |
return url; | |
}, | |
// Param | |
// Explode/encode the parameters of an URL string/object | |
// @param string s, string to decode | |
param: function(s, formatFunction) { | |
var b; | |
var a = {}; | |
var m; | |
if (typeof (s) === 'string') { | |
formatFunction = formatFunction || decodeURIComponent; | |
m = s.replace(/^[\#\?]/, '').match(/([^=\/\&]+)=([^\&]+)/g); | |
if (m) { | |
for (var i = 0; i < m.length; i++) { | |
b = m[i].match(/([^=]+)=(.*)/); | |
a[b[1]] = formatFunction(b[2]); | |
} | |
} | |
return a; | |
} | |
else { | |
formatFunction = formatFunction || encodeURIComponent; | |
var o = s; | |
a = []; | |
for (var x in o) {if (o.hasOwnProperty(x)) { | |
if (o.hasOwnProperty(x)) { | |
a.push([x, o[x] === '?' ? '?' : formatFunction(o[x])].join('=')); | |
} | |
}} | |
return a.join('&'); | |
} | |
}, | |
// Local storage facade | |
store: (function() { | |
var a = ['localStorage', 'sessionStorage']; | |
var i = -1; | |
var prefix = 'test'; | |
// Set LocalStorage | |
var localStorage; | |
while (a[++i]) { | |
try { | |
// In Chrome with cookies blocked, calling localStorage throws an error | |
localStorage = window[a[i]]; | |
localStorage.setItem(prefix + i, i); | |
localStorage.removeItem(prefix + i); | |
break; | |
} | |
catch (e) { | |
localStorage = null; | |
} | |
} | |
if (!localStorage) { | |
var cache = null; | |
localStorage = { | |
getItem: function(prop) { | |
prop = prop + '='; | |
var m = document.cookie.split(';'); | |
for (var i = 0; i < m.length; i++) { | |
var _m = m[i].replace(/(^\s+|\s+$)/, ''); | |
if (_m && _m.indexOf(prop) === 0) { | |
return _m.substr(prop.length); | |
} | |
} | |
return cache; | |
}, | |
setItem: function(prop, value) { | |
cache = value; | |
document.cookie = prop + '=' + value; | |
} | |
}; | |
// Fill the cache up | |
cache = localStorage.getItem('hello'); | |
} | |
function get() { | |
var json = {}; | |
try { | |
json = JSON.parse(localStorage.getItem('hello')) || {}; | |
} | |
catch (e) {} | |
return json; | |
} | |
function set(json) { | |
localStorage.setItem('hello', JSON.stringify(json)); | |
} | |
// Check if the browser support local storage | |
return function(name, value, days) { | |
// Local storage | |
var json = get(); | |
if (name && value === undefined) { | |
return json[name] || null; | |
} | |
else if (name && value === null) { | |
try { | |
delete json[name]; | |
} | |
catch (e) { | |
json[name] = null; | |
} | |
} | |
else if (name) { | |
json[name] = value; | |
} | |
else { | |
return json; | |
} | |
set(json); | |
return json || null; | |
}; | |
})(), | |
// Create and Append new DOM elements | |
// @param node string | |
// @param attr object literal | |
// @param dom/string | |
append: function(node, attr, target) { | |
var n = typeof (node) === 'string' ? document.createElement(node) : node; | |
if (typeof (attr) === 'object') { | |
if ('tagName' in attr) { | |
target = attr; | |
} | |
else { | |
for (var x in attr) {if (attr.hasOwnProperty(x)) { | |
if (typeof (attr[x]) === 'object') { | |
for (var y in attr[x]) {if (attr[x].hasOwnProperty(y)) { | |
n[x][y] = attr[x][y]; | |
}} | |
} | |
else if (x === 'html') { | |
n.innerHTML = attr[x]; | |
} | |
// IE doesn't like us setting methods with setAttribute | |
else if (!/^on/.test(x)) { | |
n.setAttribute(x, attr[x]); | |
} | |
else { | |
n[x] = attr[x]; | |
} | |
}} | |
} | |
} | |
if (target === 'body') { | |
(function self() { | |
if (document.body) { | |
document.body.appendChild(n); | |
} | |
else { | |
setTimeout(self, 16); | |
} | |
})(); | |
} | |
else if (typeof (target) === 'object') { | |
target.appendChild(n); | |
} | |
else if (typeof (target) === 'string') { | |
document.getElementsByTagName(target)[0].appendChild(n); | |
} | |
return n; | |
}, | |
// An easy way to create a hidden iframe | |
// @param string src | |
iframe: function(src) { | |
this.append('iframe', {src: src, style: {position:'absolute', left: '-1000px', bottom: 0, height: '1px', width: '1px'}}, 'body'); | |
}, | |
// Recursive merge two objects into one, second parameter overides the first | |
// @param a array | |
merge: function(/* Args: a, b, c, .. n */) { | |
var args = Array.prototype.slice.call(arguments); | |
args.unshift({}); | |
return this.extend.apply(null, args); | |
}, | |
// Makes it easier to assign parameters, where some are optional | |
// @param o object | |
// @param a arguments | |
args: function(o, args) { | |
var p = {}; | |
var i = 0; | |
var t = null; | |
var x = null; | |
// 'x' is the first key in the list of object parameters | |
for (x in o) {if (o.hasOwnProperty(x)) { | |
break; | |
}} | |
// Passing in hash object of arguments? | |
// Where the first argument can't be an object | |
if ((args.length === 1) && (typeof (args[0]) === 'object') && o[x] != 'o!') { | |
// Could this object still belong to a property? | |
// Check the object keys if they match any of the property keys | |
for (x in args[0]) {if (o.hasOwnProperty(x)) { | |
// Does this key exist in the property list? | |
if (x in o) { | |
// Yes this key does exist so its most likely this function has been invoked with an object parameter | |
// Return first argument as the hash of all arguments | |
return args[0]; | |
} | |
}} | |
} | |
// Else loop through and account for the missing ones. | |
for (x in o) {if (o.hasOwnProperty(x)) { | |
t = typeof (args[i]); | |
if ((typeof (o[x]) === 'function' && o[x].test(args[i])) || (typeof (o[x]) === 'string' && ( | |
(o[x].indexOf('s') > -1 && t === 'string') || | |
(o[x].indexOf('o') > -1 && t === 'object') || | |
(o[x].indexOf('i') > -1 && t === 'number') || | |
(o[x].indexOf('a') > -1 && t === 'object') || | |
(o[x].indexOf('f') > -1 && t === 'function') | |
)) | |
) { | |
p[x] = args[i++]; | |
} | |
else if (typeof (o[x]) === 'string' && o[x].indexOf('!') > -1) { | |
return false; | |
} | |
}} | |
return p; | |
}, | |
// Returns a URL instance | |
url: function(path) { | |
// If the path is empty | |
if (!path) { | |
return window.location; | |
} | |
// Chrome and FireFox support new URL() to extract URL objects | |
else if (window.URL && URL instanceof Function && URL.length !== 0) { | |
return new URL(path, window.location); | |
} | |
// Ugly shim, it works! | |
else { | |
var a = document.createElement('a'); | |
a.href = path; | |
return a.cloneNode(false); | |
} | |
}, | |
diff: function(a, b) { | |
return b.filter(function(item) { | |
return a.indexOf(item) === -1; | |
}); | |
}, | |
// Get the different hash of properties unique to `a`, and not in `b` | |
diffKey: function(a, b) { | |
if (a || !b) { | |
var r = {}; | |
for (var x in a) { | |
// Does the property not exist? | |
if (!(x in b)) { | |
r[x] = a[x]; | |
} | |
} | |
return r; | |
} | |
return a; | |
}, | |
// Unique | |
// Remove duplicate and null values from an array | |
// @param a array | |
unique: function(a) { | |
if (!Array.isArray(a)) { return []; } | |
return a.filter(function(item, index) { | |
// Is this the first location of item | |
return a.indexOf(item) === index; | |
}); | |
}, | |
isEmpty: function(obj) { | |
// Scalar | |
if (!obj) | |
return true; | |
// Array | |
if (Array.isArray(obj)) { | |
return !obj.length; | |
} | |
else if (typeof (obj) === 'object') { | |
// Object | |
for (var key in obj) { | |
if (obj.hasOwnProperty(key)) { | |
return false; | |
} | |
} | |
} | |
return true; | |
}, | |
//jscs:disable | |
/*! | |
** Thenable -- Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable | |
** Copyright (c) 2013-2014 Ralf S. Engelschall <http://engelschall.com> | |
** Licensed under The MIT License <http://opensource.org/licenses/MIT> | |
** Source-Code distributed on <http://github.com/rse/thenable> | |
*/ | |
Promise: (function(){ | |
/* promise states [Promises/A+ 2.1] */ | |
var STATE_PENDING = 0; /* [Promises/A+ 2.1.1] */ | |
var STATE_FULFILLED = 1; /* [Promises/A+ 2.1.2] */ | |
var STATE_REJECTED = 2; /* [Promises/A+ 2.1.3] */ | |
/* promise object constructor */ | |
var api = function (executor) { | |
/* optionally support non-constructor/plain-function call */ | |
if (!(this instanceof api)) | |
return new api(executor); | |
/* initialize object */ | |
this.id = "Thenable/1.0.6"; | |
this.state = STATE_PENDING; /* initial state */ | |
this.fulfillValue = undefined; /* initial value */ /* [Promises/A+ 1.3, 2.1.2.2] */ | |
this.rejectReason = undefined; /* initial reason */ /* [Promises/A+ 1.5, 2.1.3.2] */ | |
this.onFulfilled = []; /* initial handlers */ | |
this.onRejected = []; /* initial handlers */ | |
/* provide optional information-hiding proxy */ | |
this.proxy = { | |
then: this.then.bind(this) | |
}; | |
/* support optional executor function */ | |
if (typeof executor === "function") | |
executor.call(this, this.fulfill.bind(this), this.reject.bind(this)); | |
}; | |
/* promise API methods */ | |
api.prototype = { | |
/* promise resolving methods */ | |
fulfill: function (value) { return deliver(this, STATE_FULFILLED, "fulfillValue", value); }, | |
reject: function (value) { return deliver(this, STATE_REJECTED, "rejectReason", value); }, | |
/* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */ | |
then: function (onFulfilled, onRejected) { | |
var curr = this; | |
var next = new api(); /* [Promises/A+ 2.2.7] */ | |
curr.onFulfilled.push( | |
resolver(onFulfilled, next, "fulfill")); /* [Promises/A+ 2.2.2/2.2.6] */ | |
curr.onRejected.push( | |
resolver(onRejected, next, "reject" )); /* [Promises/A+ 2.2.3/2.2.6] */ | |
execute(curr); | |
return next.proxy; /* [Promises/A+ 2.2.7, 3.3] */ | |
} | |
}; | |
/* deliver an action */ | |
var deliver = function (curr, state, name, value) { | |
if (curr.state === STATE_PENDING) { | |
curr.state = state; /* [Promises/A+ 2.1.2.1, 2.1.3.1] */ | |
curr[name] = value; /* [Promises/A+ 2.1.2.2, 2.1.3.2] */ | |
execute(curr); | |
} | |
return curr; | |
}; | |
/* execute all handlers */ | |
var execute = function (curr) { | |
if (curr.state === STATE_FULFILLED) | |
execute_handlers(curr, "onFulfilled", curr.fulfillValue); | |
else if (curr.state === STATE_REJECTED) | |
execute_handlers(curr, "onRejected", curr.rejectReason); | |
}; | |
/* execute particular set of handlers */ | |
var execute_handlers = function (curr, name, value) { | |
/* global process: true */ | |
/* global setImmediate: true */ | |
/* global setTimeout: true */ | |
/* short-circuit processing */ | |
if (curr[name].length === 0) | |
return; | |
/* iterate over all handlers, exactly once */ | |
var handlers = curr[name]; | |
curr[name] = []; /* [Promises/A+ 2.2.2.3, 2.2.3.3] */ | |
var func = function () { | |
for (var i = 0; i < handlers.length; i++) | |
handlers[i](value); /* [Promises/A+ 2.2.5] */ | |
}; | |
/* execute procedure asynchronously */ /* [Promises/A+ 2.2.4, 3.1] */ | |
if (typeof process === "object" && typeof process.nextTick === "function") | |
process.nextTick(func); | |
else if (typeof setImmediate === "function") | |
setImmediate(func); | |
else | |
setTimeout(func, 0); | |
}; | |
/* generate a resolver function */ | |
var resolver = function (cb, next, method) { | |
return function (value) { | |
if (typeof cb !== "function") /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */ | |
next[method].call(next, value); /* [Promises/A+ 2.2.7.3, 2.2.7.4] */ | |
else { | |
var result; | |
try { result = cb(value); } /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */ | |
catch (e) { | |
next.reject(e); /* [Promises/A+ 2.2.7.2] */ | |
return; | |
} | |
resolve(next, result); /* [Promises/A+ 2.2.7.1] */ | |
} | |
}; | |
}; | |
/* "Promise Resolution Procedure" */ /* [Promises/A+ 2.3] */ | |
var resolve = function (promise, x) { | |
/* sanity check arguments */ /* [Promises/A+ 2.3.1] */ | |
if (promise === x || promise.proxy === x) { | |
promise.reject(new TypeError("cannot resolve promise with itself")); | |
return; | |
} | |
/* surgically check for a "then" method | |
(mainly to just call the "getter" of "then" only once) */ | |
var then; | |
if ((typeof x === "object" && x !== null) || typeof x === "function") { | |
try { then = x.then; } /* [Promises/A+ 2.3.3.1, 3.5] */ | |
catch (e) { | |
promise.reject(e); /* [Promises/A+ 2.3.3.2] */ | |
return; | |
} | |
} | |
/* handle own Thenables [Promises/A+ 2.3.2] | |
and similar "thenables" [Promises/A+ 2.3.3] */ | |
if (typeof then === "function") { | |
var resolved = false; | |
try { | |
/* call retrieved "then" method */ /* [Promises/A+ 2.3.3.3] */ | |
then.call(x, | |
/* resolvePromise */ /* [Promises/A+ 2.3.3.3.1] */ | |
function (y) { | |
if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */ | |
if (y === x) /* [Promises/A+ 3.6] */ | |
promise.reject(new TypeError("circular thenable chain")); | |
else | |
resolve(promise, y); | |
}, | |
/* rejectPromise */ /* [Promises/A+ 2.3.3.3.2] */ | |
function (r) { | |
if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */ | |
promise.reject(r); | |
} | |
); | |
} | |
catch (e) { | |
if (!resolved) /* [Promises/A+ 2.3.3.3.3] */ | |
promise.reject(e); /* [Promises/A+ 2.3.3.3.4] */ | |
} | |
return; | |
} | |
/* handle other values */ | |
promise.fulfill(x); /* [Promises/A+ 2.3.4, 2.3.3.4] */ | |
}; | |
/* export API */ | |
return api; | |
})(), | |
//jscs:enable | |
// Event | |
// A contructor superclass for adding event menthods, on, off, emit. | |
Event: function() { | |
var separator = /[\s\,]+/; | |
// If this doesn't support getPrototype then we can't get prototype.events of the parent | |
// So lets get the current instance events, and add those to a parent property | |
this.parent = { | |
events: this.events, | |
findEvents: this.findEvents, | |
parent: this.parent, | |
utils: this.utils | |
}; | |
this.events = {}; | |
// On, subscribe to events | |
// @param evt string | |
// @param callback function | |
this.on = function(evt, callback) { | |
if (callback && typeof (callback) === 'function') { | |
var a = evt.split(separator); | |
for (var i = 0; i < a.length; i++) { | |
// Has this event already been fired on this instance? | |
this.events[a[i]] = [callback].concat(this.events[a[i]] || []); | |
} | |
} | |
return this; | |
}; | |
// Off, unsubscribe to events | |
// @param evt string | |
// @param callback function | |
this.off = function(evt, callback) { | |
this.findEvents(evt, function(name, index) { | |
if (!callback || this.events[name][index] === callback) { | |
this.events[name][index] = null; | |
} | |
}); | |
return this; | |
}; | |
// Emit | |
// Triggers any subscribed events | |
this.emit = function(evt /*, data, ... */) { | |
// Get arguments as an Array, knock off the first one | |
var args = Array.prototype.slice.call(arguments, 1); | |
args.push(evt); | |
// Handler | |
var handler = function(name, index) { | |
// Replace the last property with the event name | |
args[args.length - 1] = (name === '*' ? evt : name); | |
// Trigger | |
this.events[name][index].apply(this, args); | |
}; | |
// Find the callbacks which match the condition and call | |
var _this = this; | |
while (_this && _this.findEvents) { | |
// Find events which match | |
_this.findEvents(evt + ',*', handler); | |
_this = _this.parent; | |
} | |
return this; | |
}; | |
// | |
// Easy functions | |
this.emitAfter = function() { | |
var _this = this; | |
var args = arguments; | |
setTimeout(function() { | |
_this.emit.apply(_this, args); | |
}, 0); | |
return this; | |
}; | |
this.findEvents = function(evt, callback) { | |
var a = evt.split(separator); | |
for (var name in this.events) {if (this.events.hasOwnProperty(name)) { | |
if (a.indexOf(name) > -1) { | |
for (var i = 0; i < this.events[name].length; i++) { | |
// Does the event handler exist? | |
if (this.events[name][i]) { | |
// Emit on the local instance of this | |
callback.call(this, name, i); | |
} | |
} | |
} | |
}} | |
}; | |
return this; | |
}, | |
// Global Events | |
// Attach the callback to the window object | |
// Return its unique reference | |
globalEvent: function(callback, guid) { | |
// If the guid has not been supplied then create a new one. | |
guid = guid || '_hellojs_' + parseInt(Math.random() * 1e12, 10).toString(36); | |
// Define the callback function | |
window[guid] = function() { | |
// Trigger the callback | |
try { | |
if (callback.apply(this, arguments)) { | |
delete window[guid]; | |
} | |
} | |
catch (e) { | |
console.error(e); | |
} | |
}; | |
return guid; | |
}, | |
// Trigger a clientside popup | |
// This has been augmented to support PhoneGap | |
popup: function(url, redirectUri, options) { | |
var documentElement = document.documentElement; | |
// Multi Screen Popup Positioning (http://stackoverflow.com/a/16861050) | |
// Credit: http://www.xtf.dk/2011/08/center-new-popup-window-even-on.html | |
// Fixes dual-screen position Most browsers Firefox | |
if (options.height && options.top === undefined) { | |
var dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top; | |
var height = screen.height || window.innerHeight || documentElement.clientHeight; | |
options.top = parseInt((height - options.height) / 2, 10) + dualScreenTop; | |
} | |
if (options.width && options.left === undefined) { | |
var dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left; | |
var width = screen.width || window.innerWidth || documentElement.clientWidth; | |
options.left = parseInt((width - options.width) / 2, 10) + dualScreenLeft; | |
} | |
// Convert options into an array | |
var optionsArray = []; | |
Object.keys(options).forEach(function(name) { | |
var value = options[name]; | |
optionsArray.push(name + (value !== null ? '=' + value : '')); | |
}); | |
// Call the open() function with the initial path | |
// | |
// OAuth redirect, fixes URI fragments from being lost in Safari | |
// (URI Fragments within 302 Location URI are lost over HTTPS) | |
// Loading the redirect.html before triggering the OAuth Flow seems to fix it. | |
// | |
// Firefox decodes URL fragments when calling location.hash. | |
// - This is bad if the value contains break points which are escaped | |
// - Hence the url must be encoded twice as it contains breakpoints. | |
if (navigator.userAgent.indexOf('Safari') !== -1 && navigator.userAgent.indexOf('Chrome') === -1) { | |
url = redirectUri + '#oauth_redirect=' + encodeURIComponent(encodeURIComponent(url)); | |
} | |
var popup = window.open( | |
url, | |
'_blank', | |
optionsArray.join(',') | |
); | |
if (popup && popup.focus) { | |
popup.focus(); | |
} | |
return popup; | |
}, | |
// OAuth and API response handler | |
responseHandler: function(window, parent) { | |
var _this = this; | |
var p; | |
var location = window.location; | |
// Is this an auth relay message which needs to call the proxy? | |
p = _this.param(location.search); | |
// OAuth2 or OAuth1 server response? | |
if (p && p.state && (p.code || p.oauth_token)) { | |
var state = JSON.parse(p.state); | |
// Add this path as the redirect_uri | |
p.redirect_uri = state.redirect_uri || location.href.replace(/[\?\#].*$/, ''); | |
// Redirect to the host | |
var path = _this.qs(state.oauth_proxy, p); | |
location.assign(path); | |
return; | |
} | |
// Save session, from redirected authentication | |
// #access_token has come in? | |
// | |
// FACEBOOK is returning auth errors within as a query_string... thats a stickler for consistency. | |
// SoundCloud is the state in the querystring and the token in the hashtag, so we'll mix the two together | |
p = _this.merge(_this.param(location.search || ''), _this.param(location.hash || '')); | |
// If p.state | |
if (p && 'state' in p) { | |
// Remove any addition information | |
// E.g. p.state = 'facebook.page'; | |
try { | |
var a = JSON.parse(p.state); | |
_this.extend(p, a); | |
} | |
catch (e) { | |
var stateDecoded = decodeURIComponent(p.state); | |
try { | |
var b = JSON.parse(stateDecoded); | |
_this.extend(p, b); | |
} | |
catch (e) { | |
console.error('Could not decode state parameter'); | |
} | |
} | |
// Access_token? | |
if (('access_token' in p && p.access_token) && p.network) { | |
if (!p.expires_in || parseInt(p.expires_in, 10) === 0) { | |
// If p.expires_in is unset, set to 0 | |
p.expires_in = 0; | |
} | |
p.expires_in = parseInt(p.expires_in, 10); | |
p.expires = ((new Date()).getTime() / 1e3) + (p.expires_in || (60 * 60 * 24 * 365)); | |
// Lets use the "state" to assign it to one of our networks | |
authCallback(p, window, parent); | |
} | |
// Error=? | |
// &error_description=? | |
// &state=? | |
else if (('error' in p && p.error) && p.network) { | |
p.error = { | |
code: p.error, | |
message: p.error_message || p.error_description | |
}; | |
// Let the state handler handle it | |
authCallback(p, window, parent); | |
} | |
// API call, or a cancelled login | |
// Result is serialized JSON string | |
else if (p.callback && p.callback in parent) { | |
// Trigger a function in the parent | |
var res = 'result' in p && p.result ? JSON.parse(p.result) : false; | |
// Trigger the callback on the parent | |
callback(parent, p.callback)(res); | |
closeWindow(); | |
} | |
// If this page is still open | |
if (p.page_uri) { | |
location.assign(p.page_uri); | |
} | |
} | |
// OAuth redirect, fixes URI fragments from being lost in Safari | |
// (URI Fragments within 302 Location URI are lost over HTTPS) | |
// Loading the redirect.html before triggering the OAuth Flow seems to fix it. | |
else if ('oauth_redirect' in p) { | |
location.assign(decodeURIComponent(p.oauth_redirect)); | |
return; | |
} | |
// Trigger a callback to authenticate | |
function authCallback(obj, window, parent) { | |
var cb = obj.callback; | |
var network = obj.network; | |
// Trigger the callback on the parent | |
_this.store(network, obj); | |
// If this is a page request it has no parent or opener window to handle callbacks | |
if (('display' in obj) && obj.display === 'page') { | |
return; | |
} | |
// Remove from session object | |
if (parent && cb && cb in parent) { | |
try { | |
delete obj.callback; | |
} | |
catch (e) {} | |
// Update store | |
_this.store(network, obj); | |
// Call the globalEvent function on the parent | |
// It's safer to pass back a string to the parent, | |
// Rather than an object/array (better for IE8) | |
var str = JSON.stringify(obj); | |
try { | |
callback(parent, cb)(str); | |
} | |
catch (e) { | |
// Error thrown whilst executing parent callback | |
} | |
} | |
closeWindow(); | |
} | |
function callback(parent, callbackID) { | |
if (callbackID.indexOf('_hellojs_') !== 0) { | |
return function() { | |
throw 'Could not execute callback ' + callbackID; | |
}; | |
} | |
return parent[callbackID]; | |
} | |
function closeWindow() { | |
if (window.frameElement) { | |
// Inside an iframe, remove from parent | |
parent.document.body.removeChild(window.frameElement); | |
} | |
else { | |
// Close this current window | |
try { | |
window.close(); | |
} | |
catch (e) {} | |
// IOS bug wont let us close a popup if still loading | |
if (window.addEventListener) { | |
window.addEventListener('load', function() { | |
window.close(); | |
}); | |
} | |
} | |
} | |
} | |
}); | |
// Events | |
// Extend the hello object with its own event instance | |
hello.utils.Event.call(hello); | |
/////////////////////////////////// | |
// Monitoring session state | |
// Check for session changes | |
/////////////////////////////////// | |
(function(hello) { | |
// Monitor for a change in state and fire | |
var oldSessions = {}; | |
// Hash of expired tokens | |
var expired = {}; | |
// Listen to other triggers to Auth events, use these to update this | |
hello.on('auth.login, auth.logout', function(auth) { | |
if (auth && typeof (auth) === 'object' && auth.network) { | |
oldSessions[auth.network] = hello.utils.store(auth.network) || {}; | |
} | |
}); | |
(function self() { | |
var CURRENT_TIME = ((new Date()).getTime() / 1e3); | |
var emit = function(eventName) { | |
hello.emit('auth.' + eventName, { | |
network: name, | |
authResponse: session | |
}); | |
}; | |
// Loop through the services | |
for (var name in hello.services) {if (hello.services.hasOwnProperty(name)) { | |
if (!hello.services[name].id) { | |
// We haven't attached an ID so dont listen. | |
continue; | |
} | |
// Get session | |
var session = hello.utils.store(name) || {}; | |
var provider = hello.services[name]; | |
var oldSess = oldSessions[name] || {}; | |
// Listen for globalEvents that did not get triggered from the child | |
if (session && 'callback' in session) { | |
// To do remove from session object... | |
var cb = session.callback; | |
try { | |
delete session.callback; | |
} | |
catch (e) {} | |
// Update store | |
// Removing the callback | |
hello.utils.store(name, session); | |
// Emit global events | |
try { | |
window[cb](session); | |
} | |
catch (e) {} | |
} | |
// Refresh token | |
if (session && ('expires' in session) && session.expires < CURRENT_TIME) { | |
// If auto refresh is possible | |
// Either the browser supports | |
var refresh = provider.refresh || session.refresh_token; | |
// Has the refresh been run recently? | |
if (refresh && (!(name in expired) || expired[name] < CURRENT_TIME)) { | |
// Try to resignin | |
hello.emit('notice', name + ' has expired trying to resignin'); | |
hello.login(name, {display: 'none', force: false}); | |
// Update expired, every 10 minutes | |
expired[name] = CURRENT_TIME + 600; | |
} | |
// Does this provider not support refresh | |
else if (!refresh && !(name in expired)) { | |
// Label the event | |
emit('expired'); | |
expired[name] = true; | |
} | |
// If session has expired then we dont want to store its value until it can be established that its been updated | |
continue; | |
} | |
// Has session changed? | |
else if (oldSess.access_token === session.access_token && | |
oldSess.expires === session.expires) { | |
continue; | |
} | |
// Access_token has been removed | |
else if (!session.access_token && oldSess.access_token) { | |
emit('logout'); | |
} | |
// Access_token has been created | |
else if (session.access_token && !oldSess.access_token) { | |
emit('login'); | |
} | |
// Access_token has been updated | |
else if (session.expires !== oldSess.expires) { | |
emit('update'); | |
} | |
// Updated stored session | |
oldSessions[name] = session; | |
// Remove the expired flags | |
if (name in expired) { | |
delete expired[name]; | |
} | |
}} | |
// Check error events | |
setTimeout(self, 1000); | |
})(); | |
})(hello); | |
// EOF CORE lib | |
////////////////////////////////// | |
///////////////////////////////////////// | |
// API | |
// @param path string | |
// @param query object (optional) | |
// @param method string (optional) | |
// @param data object (optional) | |
// @param timeout integer (optional) | |
// @param callback function (optional) | |
hello.api = function() { | |
// Shorthand | |
var _this = this; | |
var utils = _this.utils; | |
var error = utils.error; | |
// Construct a new Promise object | |
var promise = utils.Promise(); | |
// Arguments | |
var p = utils.args({path: 's!', query: 'o', method: 's', data: 'o', timeout: 'i', callback: 'f'}, arguments); | |
// Method | |
p.method = (p.method || 'get').toLowerCase(); | |
// Headers | |
p.headers = p.headers || {}; | |
// Query | |
p.query = p.query || {}; | |
// If get, put all parameters into query | |
if (p.method === 'get' || p.method === 'delete') { | |
utils.extend(p.query, p.data); | |
p.data = {}; | |
} | |
var data = p.data = p.data || {}; | |
// Completed event callback | |
promise.then(p.callback, p.callback); | |
// Remove the network from path, e.g. facebook:/me/friends | |
// Results in { network : facebook, path : me/friends } | |
if (!p.path) { | |
return promise.reject(error('invalid_path', 'Missing the path parameter from the request')); | |
} | |
p.path = p.path.replace(/^\/+/, ''); | |
var a = (p.path.split(/[\/\:]/, 2) || [])[0].toLowerCase(); | |
if (a in _this.services) { | |
p.network = a; | |
var reg = new RegExp('^' + a + ':?\/?'); | |
p.path = p.path.replace(reg, ''); | |
} | |
// Network & Provider | |
// Define the network that this request is made for | |
p.network = _this.settings.default_service = p.network || _this.settings.default_service; | |
var o = _this.services[p.network]; | |
// INVALID | |
// Is there no service by the given network name? | |
if (!o) { | |
return promise.reject(error('invalid_network', 'Could not match the service requested: ' + p.network)); | |
} | |
// PATH | |
// As long as the path isn't flagged as unavaiable, e.g. path == false | |
if (!(!(p.method in o) || !(p.path in o[p.method]) || o[p.method][p.path] !== false)) { | |
return promise.reject(error('invalid_path', 'The provided path is not available on the selected network')); | |
} | |
// PROXY | |
// OAuth1 calls always need a proxy | |
if (!p.oauth_proxy) { | |
p.oauth_proxy = _this.settings.oauth_proxy; | |
} | |
if (!('proxy' in p)) { | |
p.proxy = p.oauth_proxy && o.oauth && parseInt(o.oauth.version, 10) === 1; | |
} | |
// TIMEOUT | |
// Adopt timeout from global settings by default | |
if (!('timeout' in p)) { | |
p.timeout = _this.settings.timeout; | |
} | |
// Format response | |
// Whether to run the raw response through post processing. | |
if (!('formatResponse' in p)) { | |
p.formatResponse = true; | |
} | |
// Get the current session | |
// Append the access_token to the query | |
p.authResponse = _this.getAuthResponse(p.network); | |
if (p.authResponse && p.authResponse.access_token) { | |
p.query.access_token = p.authResponse.access_token; | |
} | |
var url = p.path; | |
var m; | |
// Store the query as options | |
// This is used to populate the request object before the data is augmented by the prewrap handlers. | |
p.options = utils.clone(p.query); | |
// Clone the data object | |
// Prevent this script overwriting the data of the incoming object. | |
// Ensure that everytime we run an iteration the callbacks haven't removed some data | |
p.data = utils.clone(data); | |
// URL Mapping | |
// Is there a map for the given URL? | |
var actions = o[{'delete': 'del'}[p.method] || p.method] || {}; | |
// Extrapolate the QueryString | |
// Provide a clean path | |
// Move the querystring into the data | |
if (p.method === 'get') { | |
var query = url.split(/[\?#]/)[1]; | |
if (query) { | |
utils.extend(p.query, utils.param(query)); | |
// Remove the query part from the URL | |
url = url.replace(/\?.*?(#|$)/, '$1'); | |
} | |
} | |
// Is the hash fragment defined | |
if ((m = url.match(/#(.+)/, ''))) { | |
url = url.split('#')[0]; | |
p.path = m[1]; | |
} | |
else if (url in actions) { | |
p.path = url; | |
url = actions[url]; | |
} | |
else if ('default' in actions) { | |
url = actions['default']; | |
} | |
// Redirect Handler | |
// This defines for the Form+Iframe+Hash hack where to return the results too. | |
p.redirect_uri = _this.settings.redirect_uri; | |
// Define FormatHandler | |
// The request can be procesed in a multitude of ways | |
// Here's the options - depending on the browser and endpoint | |
p.xhr = o.xhr; | |
p.jsonp = o.jsonp; | |
p.form = o.form; | |
// Make request | |
if (typeof (url) === 'function') { | |
// Does self have its own callback? | |
url(p, getPath); | |
} | |
else { | |
// Else the URL is a string | |
getPath(url); | |
} | |
return promise.proxy; | |
// If url needs a base | |
// Wrap everything in | |
function getPath(url) { | |
// Format the string if it needs it | |
url = url.replace(/\@\{([a-z\_\-]+)(\|.*?)?\}/gi, function(m, key, defaults) { | |
var val = defaults ? defaults.replace(/^\|/, '') : ''; | |
if (key in p.query) { | |
val = p.query[key]; | |
delete p.query[key]; | |
} | |
else if (p.data && key in p.data) { | |
val = p.data[key]; | |
delete p.data[key]; | |
} | |
else if (!defaults) { | |
promise.reject(error('missing_attribute', 'The attribute ' + key + ' is missing from the request')); | |
} | |
return val; | |
}); | |
// Add base | |
if (!url.match(/^https?:\/\//)) { | |
url = o.base + url; | |
} | |
// Define the request URL | |
p.url = url; | |
// Make the HTTP request with the curated request object | |
// CALLBACK HANDLER | |
// @ response object | |
// @ statusCode integer if available | |
utils.request(p, function(r, headers) { | |
// Is this a raw response? | |
if (!p.formatResponse) { | |
// Bad request? error statusCode or otherwise contains an error response vis JSONP? | |
if (typeof headers === 'object' ? (headers.statusCode >= 400) : (typeof r === 'object' && 'error' in r)) { | |
promise.reject(r); | |
} | |
else { | |
promise.fulfill(r); | |
} | |
return; | |
} | |
// Should this be an object | |
if (r === true) { | |
r = {success:true}; | |
} | |
else if (!r) { | |
r = {}; | |
} | |
// The delete callback needs a better response | |
if (p.method === 'delete') { | |
r = (!r || utils.isEmpty(r)) ? {success:true} : r; | |
} | |
// FORMAT RESPONSE? | |
// Does self request have a corresponding formatter | |
if (o.wrap && ((p.path in o.wrap) || ('default' in o.wrap))) { | |
var wrap = (p.path in o.wrap ? p.path : 'default'); | |
var time = (new Date()).getTime(); | |
// FORMAT RESPONSE | |
var b = o.wrap[wrap](r, headers, p); | |
// Has the response been utterly overwritten? | |
// Typically self augments the existing object.. but for those rare occassions | |
if (b) { | |
r = b; | |
} | |
} | |
// Is there a next_page defined in the response? | |
if (r && 'paging' in r && r.paging.next) { | |
// Add the relative path if it is missing from the paging/next path | |
if (r.paging.next[0] === '?') { | |
r.paging.next = p.path + r.paging.next; | |
} | |
// The relative path has been defined, lets markup the handler in the HashFragment | |
else { | |
r.paging.next += '#' + p.path; | |
} | |
} | |
// Dispatch to listeners | |
// Emit events which pertain to the formatted response | |
if (!r || 'error' in r) { | |
promise.reject(r); | |
} | |
else { | |
promise.fulfill(r); | |
} | |
}); | |
} | |
}; | |
// API utilities | |
hello.utils.extend(hello.utils, { | |
// Make an HTTP request | |
request: function(p, callback) { | |
var _this = this; | |
var error = _this.error; | |
// This has to go through a POST request | |
if (!_this.isEmpty(p.data) && !('FileList' in window) && _this.hasBinary(p.data)) { | |
// Disable XHR and JSONP | |
p.xhr = false; | |
p.jsonp = false; | |
} | |
// Check if the browser and service support CORS | |
var cors = this.request_cors(function() { | |
// If it does then run this... | |
return ((p.xhr === undefined) || (p.xhr && (typeof (p.xhr) !== 'function' || p.xhr(p, p.query)))); | |
}); | |
if (cors) { | |
formatUrl(p, function(url) { | |
var x = _this.xhr(p.method, url, p.headers, p.data, callback); | |
x.onprogress = p.onprogress || null; | |
// Windows Phone does not support xhr.upload, see #74 | |
// Feature detect | |
if (x.upload && p.onuploadprogress) { | |
x.upload.onprogress = p.onuploadprogress; | |
} | |
}); | |
return; | |
} | |
// Clone the query object | |
// Each request modifies the query object and needs to be tared after each one. | |
var _query = p.query; | |
p.query = _this.clone(p.query); | |
// Assign a new callbackID | |
p.callbackID = _this.globalEvent(); | |
// JSONP | |
if (p.jsonp !== false) { | |
// Clone the query object | |
p.query.callback = p.callbackID; | |
// If the JSONP is a function then run it | |
if (typeof (p.jsonp) === 'function') { | |
p.jsonp(p, p.query); | |
} | |
// Lets use JSONP if the method is 'get' | |
if (p.method === 'get') { | |
formatUrl(p, function(url) { | |
_this.jsonp(url, callback, p.callbackID, p.timeout); | |
}); | |
return; | |
} | |
else { | |
// It's not compatible reset query | |
p.query = _query; | |
} | |
} | |
// Otherwise we're on to the old school, iframe hacks and JSONP | |
if (p.form !== false) { | |
// Add some additional query parameters to the URL | |
// We're pretty stuffed if the endpoint doesn't like these | |
p.query.redirect_uri = p.redirect_uri; | |
p.query.state = JSON.stringify({callback:p.callbackID}); | |
var opts; | |
if (typeof (p.form) === 'function') { | |
// Format the request | |
opts = p.form(p, p.query); | |
} | |
if (p.method === 'post' && opts !== false) { | |
formatUrl(p, function(url) { | |
_this.post(url, p.data, opts, callback, p.callbackID, p.timeout); | |
}); | |
return; | |
} | |
} | |
// None of the methods were successful throw an error | |
callback(error('invalid_request', 'There was no mechanism for handling this request')); | |
return; | |
// Format URL | |
// Constructs the request URL, optionally wraps the URL through a call to a proxy server | |
// Returns the formatted URL | |
function formatUrl(p, callback) { | |
// Are we signing the request? | |
var sign; | |
// OAuth1 | |
// Remove the token from the query before signing | |
if (p.authResponse && p.authResponse.oauth && parseInt(p.authResponse.oauth.version, 10) === 1) { | |
// OAUTH SIGNING PROXY | |
sign = p.query.access_token; | |
// Remove the access_token | |
delete p.query.access_token; | |
// Enfore use of Proxy | |
p.proxy = true; | |
} | |
// POST body to querystring | |
if (p.data && (p.method === 'get' || p.method === 'delete')) { | |
// Attach the p.data to the querystring. | |
_this.extend(p.query, p.data); | |
p.data = null; | |
} | |
// Construct the path | |
var path = _this.qs(p.url, p.query); | |
// Proxy the request through a server | |
// Used for signing OAuth1 | |
// And circumventing services without Access-Control Headers | |
if (p.proxy) { | |
// Use the proxy as a path | |
path = _this.qs(p.oauth_proxy, { | |
path: path, | |
access_token: sign || '', | |
// This will prompt the request to be signed as though it is OAuth1 | |
then: p.proxy_response_type || (p.method.toLowerCase() === 'get' ? 'redirect' : 'proxy'), | |
method: p.method.toLowerCase(), | |
suppress_response_codes: true | |
}); | |
} | |
callback(path); | |
} | |
}, | |
// Test whether the browser supports the CORS response | |
request_cors: function(callback) { | |
return 'withCredentials' in new XMLHttpRequest() && callback(); | |
}, | |
// Return the type of DOM object | |
domInstance: function(type, data) { | |
var test = 'HTML' + (type || '').replace( | |
/^[a-z]/, | |
function(m) { | |
return m.toUpperCase(); | |
} | |
) + 'Element'; | |
if (!data) { | |
return false; | |
} | |
if (window[test]) { | |
return data instanceof window[test]; | |
} | |
else if (window.Element) { | |
return data instanceof window.Element && (!type || (data.tagName && data.tagName.toLowerCase() === type)); | |
} | |
else { | |
return (!(data instanceof Object || data instanceof Array || data instanceof String || data instanceof Number) && data.tagName && data.tagName.toLowerCase() === type); | |
} | |
}, | |
// Create a clone of an object | |
clone: function(obj) { | |
// Does not clone DOM elements, nor Binary data, e.g. Blobs, Filelists | |
if (obj === null || typeof (obj) !== 'object' || obj instanceof Date || 'nodeName' in obj || this.isBinary(obj) || (typeof FormData === 'function' && obj instanceof FormData)) { | |
return obj; | |
} | |
if (Array.isArray(obj)) { | |
// Clone each item in the array | |
return obj.map(this.clone.bind(this)); | |
} | |
// But does clone everything else. | |
var clone = {}; | |
for (var x in obj) { | |
clone[x] = this.clone(obj[x]); | |
} | |
return clone; | |
}, | |
// XHR: uses CORS to make requests | |
xhr: function(method, url, headers, data, callback) { | |
var r = new XMLHttpRequest(); | |
var error = this.error; | |
// Binary? | |
var binary = false; | |
if (method === 'blob') { | |
binary = method; | |
method = 'GET'; | |
} | |
method = method.toUpperCase(); | |
// Xhr.responseType 'json' is not supported in any of the vendors yet. | |
r.onload = function(e) { | |
var json = r.response; | |
try { | |
json = JSON.parse(r.responseText); | |
} | |
catch (_e) { | |
if (r.status === 401) { | |
json = error('access_denied', r.statusText); | |
} | |
} | |
var headers = headersToJSON(r.getAllResponseHeaders()); | |
headers.statusCode = r.status; | |
callback(json || (method === 'GET' ? error('empty_response', 'Could not get resource') : {}), headers); | |
}; | |
r.onerror = function(e) { | |
var json = r.responseText; | |
try { | |
json = JSON.parse(r.responseText); | |
} | |
catch (_e) {} | |
callback(json || error('access_denied', 'Could not get resource')); | |
}; | |
var x; | |
// Should we add the query to the URL? | |
if (method === 'GET' || method === 'DELETE') { | |
data = null; | |
} | |
else if (data && typeof (data) !== 'string' && !(data instanceof FormData) && !(data instanceof File) && !(data instanceof Blob)) { | |
// Loop through and add formData | |
var f = new FormData(); | |
for (x in data) if (data.hasOwnProperty(x)) { | |
if (data[x] instanceof HTMLInputElement) { | |
if ('files' in data[x] && data[x].files.length > 0) { | |
f.append(x, data[x].files[0]); | |
} | |
} | |
else if (data[x] instanceof Blob) { | |
f.append(x, data[x], data.name); | |
} | |
else { | |
f.append(x, data[x]); | |
} | |
} | |
data = f; | |
} | |
// Open the path, async | |
r.open(method, url, true); | |
if (binary) { | |
if ('responseType' in r) { | |
r.responseType = binary; | |
} | |
else { | |
r.overrideMimeType('text/plain; charset=x-user-defined'); | |
} | |
} | |
// Set any bespoke headers | |
if (headers) { | |
for (x in headers) { | |
r.setRequestHeader(x, headers[x]); | |
} | |
} | |
r.send(data); | |
return r; | |
// Headers are returned as a string | |
function headersToJSON(s) { | |
var r = {}; | |
var reg = /([a-z\-]+):\s?(.*);?/gi; | |
var m; | |
while ((m = reg.exec(s))) { | |
r[m[1]] = m[2]; | |
} | |
return r; | |
} | |
}, | |
// JSONP | |
// Injects a script tag into the DOM to be executed and appends a callback function to the window object | |
// @param string/function pathFunc either a string of the URL or a callback function pathFunc(querystringhash, continueFunc); | |
// @param function callback a function to call on completion; | |
jsonp: function(url, callback, callbackID, timeout) { | |
var _this = this; | |
var error = _this.error; | |
// Change the name of the callback | |
var bool = 0; | |
var head = document.getElementsByTagName('head')[0]; | |
var operaFix; | |
var result = error('server_error', 'server_error'); | |
var cb = function() { | |
if (!(bool++)) { | |
window.setTimeout(function() { | |
callback(result); | |
head.removeChild(script); | |
}, 0); | |
} | |
}; | |
// Add callback to the window object | |
callbackID = _this.globalEvent(function(json) { | |
result = json; | |
return true; | |
// Mark callback as done | |
}, callbackID); | |
// The URL is a function for some cases and as such | |
// Determine its value with a callback containing the new parameters of this function. | |
url = url.replace(new RegExp('=\\?(&|$)'), '=' + callbackID + '$1'); | |
// Build script tag | |
var script = _this.append('script', { | |
id: callbackID, | |
name: callbackID, | |
src: url, | |
async: true, | |
onload: cb, | |
onerror: cb, | |
onreadystatechange: function() { | |
if (/loaded|complete/i.test(this.readyState)) { | |
cb(); | |
} | |
} | |
}); | |
// Opera fix error | |
// Problem: If an error occurs with script loading Opera fails to trigger the script.onerror handler we specified | |
// | |
// Fix: | |
// By setting the request to synchronous we can trigger the error handler when all else fails. | |
// This action will be ignored if we've already called the callback handler "cb" with a successful onload event | |
if (window.navigator.userAgent.toLowerCase().indexOf('opera') > -1) { | |
operaFix = _this.append('script', { | |
text: 'document.getElementById(\'' + callbackID + '\').onerror();' | |
}); | |
script.async = false; | |
} | |
// Add timeout | |
if (timeout) { | |
window.setTimeout(function() { | |
result = error('timeout', 'timeout'); | |
cb(); | |
}, timeout); | |
} | |
// TODO: add fix for IE, | |
// However: unable recreate the bug of firing off the onreadystatechange before the script content has been executed and the value of "result" has been defined. | |
// Inject script tag into the head element | |
head.appendChild(script); | |
// Append Opera Fix to run after our script | |
if (operaFix) { | |
head.appendChild(operaFix); | |
} | |
}, | |
// Post | |
// Send information to a remote location using the post mechanism | |
// @param string uri path | |
// @param object data, key value data to send | |
// @param function callback, function to execute in response | |
post: function(url, data, options, callback, callbackID, timeout) { | |
var _this = this; | |
var error = _this.error; | |
var doc = document; | |
// This hack needs a form | |
var form = null; | |
var reenableAfterSubmit = []; | |
var newform; | |
var i = 0; | |
var x = null; | |
var bool = 0; | |
var cb = function(r) { | |
if (!(bool++)) { | |
callback(r); | |
} | |
}; | |
// What is the name of the callback to contain | |
// We'll also use this to name the iframe | |
_this.globalEvent(cb, callbackID); | |
// Build the iframe window | |
var win; | |
try { | |
// IE7 hack, only lets us define the name here, not later. | |
win = doc.createElement('<iframe name="' + callbackID + '">'); | |
} | |
catch (e) { | |
win = doc.createElement('iframe'); | |
} | |
win.name = callbackID; | |
win.id = callbackID; | |
win.style.display = 'none'; | |
// Override callback mechanism. Triggger a response onload/onerror | |
if (options && options.callbackonload) { | |
// Onload is being fired twice | |
win.onload = function() { | |
cb({ | |
response: 'posted', | |
message: 'Content was posted' | |
}); | |
}; | |
} | |
if (timeout) { | |
setTimeout(function() { | |
cb(error('timeout', 'The post operation timed out')); | |
}, timeout); | |
} | |
doc.body.appendChild(win); | |
// If we are just posting a single item | |
if (_this.domInstance('form', data)) { | |
// Get the parent form | |
form = data.form; | |
// Loop through and disable all of its siblings | |
for (i = 0; i < form.elements.length; i++) { | |
if (form.elements[i] !== data) { | |
form.elements[i].setAttribute('disabled', true); | |
} | |
} | |
// Move the focus to the form | |
data = form; | |
} | |
// Posting a form | |
if (_this.domInstance('form', data)) { | |
// This is a form element | |
form = data; | |
// Does this form need to be a multipart form? | |
for (i = 0; i < form.elements.length; i++) { | |
if (!form.elements[i].disabled && form.elements[i].type === 'file') { | |
form.encoding = form.enctype = 'multipart/form-data'; | |
form.elements[i].setAttribute('name', 'file'); | |
} | |
} | |
} | |
else { | |
// Its not a form element, | |
// Therefore it must be a JSON object of Key=>Value or Key=>Element | |
// If anyone of those values are a input type=file we shall shall insert its siblings into the form for which it belongs. | |
for (x in data) if (data.hasOwnProperty(x)) { | |
// Is this an input Element? | |
if (_this.domInstance('input', data[x]) && data[x].type === 'file') { | |
form = data[x].form; | |
form.encoding = form.enctype = 'multipart/form-data'; | |
} | |
} | |
// Do If there is no defined form element, lets create one. | |
if (!form) { | |
// Build form | |
form = doc.createElement('form'); | |
doc.body.appendChild(form); | |
newform = form; | |
} | |
var input; | |
// Add elements to the form if they dont exist | |
for (x in data) if (data.hasOwnProperty(x)) { | |
// Is this an element? | |
var el = (_this.domInstance('input', data[x]) || _this.domInstance('textArea', data[x]) || _this.domInstance('select', data[x])); | |
// Is this not an input element, or one that exists outside the form. | |
if (!el || data[x].form !== form) { | |
// Does an element have the same name? | |
var inputs = form.elements[x]; | |
if (input) { | |
// Remove it. | |
if (!(inputs instanceof NodeList)) { | |
inputs = [inputs]; | |
} | |
for (i = 0; i < inputs.length; i++) { | |
inputs[i].parentNode.removeChild(inputs[i]); | |
} | |
} | |
// Create an input element | |
input = doc.createElement('input'); | |
input.setAttribute('type', 'hidden'); | |
input.setAttribute('name', x); | |
// Does it have a value attribute? | |
if (el) { | |
input.value = data[x].value; | |
} | |
else if (_this.domInstance(null, data[x])) { | |
input.value = data[x].innerHTML || data[x].innerText; | |
} | |
else { | |
input.value = data[x]; | |
} | |
form.appendChild(input); | |
} | |
// It is an element, which exists within the form, but the name is wrong | |
else if (el && data[x].name !== x) { | |
data[x].setAttribute('name', x); | |
data[x].name = x; | |
} | |
} | |
// Disable elements from within the form if they weren't specified | |
for (i = 0; i < form.elements.length; i++) { | |
input = form.elements[i]; | |
// Does the same name and value exist in the parent | |
if (!(input.name in data) && input.getAttribute('disabled') !== true) { | |
// Disable | |
input.setAttribute('disabled', true); | |
// Add re-enable to callback | |
reenableAfterSubmit.push(input); | |
} | |
} | |
} | |
// Set the target of the form | |
form.setAttribute('method', 'POST'); | |
form.setAttribute('target', callbackID); | |
form.target = callbackID; | |
// Update the form URL | |
form.setAttribute('action', url); | |
// Submit the form | |
// Some reason this needs to be offset from the current window execution | |
setTimeout(function() { | |
form.submit(); | |
setTimeout(function() { | |
try { | |
// Remove the iframe from the page. | |
//win.parentNode.removeChild(win); | |
// Remove the form | |
if (newform) { | |
newform.parentNode.removeChild(newform); | |
} | |
} | |
catch (e) { | |
try { | |
console.error('HelloJS: could not remove iframe'); | |
} | |
catch (ee) {} | |
} | |
// Reenable the disabled form | |
for (var i = 0; i < reenableAfterSubmit.length; i++) { | |
if (reenableAfterSubmit[i]) { | |
reenableAfterSubmit[i].setAttribute('disabled', false); | |
reenableAfterSubmit[i].disabled = false; | |
} | |
} | |
}, 0); | |
}, 100); | |
}, | |
// Some of the providers require that only multipart is used with non-binary forms. | |
// This function checks whether the form contains binary data | |
hasBinary: function(data) { | |
for (var x in data) if (data.hasOwnProperty(x)) { | |
if (this.isBinary(data[x])) { | |
return true; | |
} | |
} | |
return false; | |
}, | |
// Determines if a variable Either Is or like a FormInput has the value of a Blob | |
isBinary: function(data) { | |
return data instanceof Object && ( | |
(this.domInstance('input', data) && data.type === 'file') || | |
('FileList' in window && data instanceof window.FileList) || | |
('File' in window && data instanceof window.File) || | |
('Blob' in window && data instanceof window.Blob)); | |
}, | |
// Convert Data-URI to Blob string | |
toBlob: function(dataURI) { | |
var reg = /^data\:([^;,]+(\;charset=[^;,]+)?)(\;base64)?,/i; | |
var m = dataURI.match(reg); | |
if (!m) { | |
return dataURI; | |
} | |
var binary = atob(dataURI.replace(reg, '')); | |
var array = []; | |
for (var i = 0; i < binary.length; i++) { | |
array.push(binary.charCodeAt(i)); | |
} | |
return new Blob([new Uint8Array(array)], {type: m[1]}); | |
} | |
}); | |
// EXTRA: Convert FormElement to JSON for POSTing | |
// Wrappers to add additional functionality to existing functions | |
(function(hello) { | |
// Copy original function | |
var api = hello.api; | |
var utils = hello.utils; | |
utils.extend(utils, { | |
// DataToJSON | |
// This takes a FormElement|NodeList|InputElement|MixedObjects and convers the data object to JSON. | |
dataToJSON: function(p) { | |
var _this = this; | |
var w = window; | |
var data = p.data; | |
// Is data a form object | |
if (_this.domInstance('form', data)) { | |
data = _this.nodeListToJSON(data.elements); | |
} | |
else if ('NodeList' in w && data instanceof NodeList) { | |
data = _this.nodeListToJSON(data); | |
} | |
else if (_this.domInstance('input', data)) { | |
data = _this.nodeListToJSON([data]); | |
} | |
// Is data a blob, File, FileList? | |
if (('File' in w && data instanceof w.File) || | |
('Blob' in w && data instanceof w.Blob) || | |
('FileList' in w && data instanceof w.FileList)) { | |
data = {file: data}; | |
} | |
// Loop through data if it's not form data it must now be a JSON object | |
if (!('FormData' in w && data instanceof w.FormData)) { | |
for (var x in data) if (data.hasOwnProperty(x)) { | |
if ('FileList' in w && data[x] instanceof w.FileList) { | |
if (data[x].length === 1) { | |
data[x] = data[x][0]; | |
} | |
} | |
else if (_this.domInstance('input', data[x]) && data[x].type === 'file') { | |
continue; | |
} | |
else if (_this.domInstance('input', data[x]) || | |
_this.domInstance('select', data[x]) || | |
_this.domInstance('textArea', data[x])) { | |
data[x] = data[x].value; | |
} | |
else if (_this.domInstance(null, data[x])) { | |
data[x] = data[x].innerHTML || data[x].innerText; | |
} | |
} | |
} | |
p.data = data; | |
return data; | |
}, | |
// NodeListToJSON | |
// Given a list of elements extrapolate their values and return as a json object | |
nodeListToJSON: function(nodelist) { | |
var json = {}; | |
// Create a data string | |
for (var i = 0; i < nodelist.length; i++) { | |
var input = nodelist[i]; | |
// If the name of the input is empty or diabled, dont add it. | |
if (input.disabled || !input.name) { | |
continue; | |
} | |
// Is this a file, does the browser not support 'files' and 'FormData'? | |
if (input.type === 'file') { | |
json[input.name] = input; | |
} | |
else { | |
json[input.name] = input.value || input.innerHTML; | |
} | |
} | |
return json; | |
} | |
}); | |
// Replace it | |
hello.api = function() { | |
// Get arguments | |
var p = utils.args({path: 's!', method: 's', data:'o', timeout: 'i', callback: 'f'}, arguments); | |
// Change for into a data object | |
if (p.data) { | |
utils.dataToJSON(p); | |
} | |
return api.call(this, p); | |
}; | |
})(hello); | |
///////////////////////////////////// | |
// | |
// Save any access token that is in the current page URL | |
// Handle any response solicited through iframe hash tag following an API request | |
// | |
///////////////////////////////////// | |
hello.utils.responseHandler(window, window.opener || window.parent); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
</head> | |
<body> | |
<a href="/dashboard.html">Go to dashboard</a> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ | |
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:s,sort:n.sort,splice:n.splice},w.extend=w.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||g(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)n=a[t],a!==(r=e[t])&&(l&&r&&(w.isPlainObject(r)||(i=Array.isArray(r)))?(i?(i=!1,o=n&&Array.isArray(n)?n:[]):o=n&&w.isPlainObject(n)?n:{},a[t]=w.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},w.extend({expando:"jQuery"+("3.3.1"+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==c.call(e))&&(!(t=i(e))||"function"==typeof(n=f.call(t,"constructor")&&t.constructor)&&p.call(n)===d)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e){m(e)},each:function(e,t){var n,r=0;if(C(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},trim:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(C(Object(e))?w.merge(n,"string"==typeof e?[e]:e):s.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:u.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r,i=[],o=0,a=e.length,s=!n;o<a;o++)(r=!t(e[o],o))!==s&&i.push(e[o]);return i},map:function(e,t,n){var r,i,o=0,s=[];if(C(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&s.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&s.push(i);return a.apply([],s)},guid:1,support:h}),"function"==typeof Symbol&&(w.fn[Symbol.iterator]=n[Symbol.iterator]),w.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function C(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!g(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},P="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",I="\\["+M+"*("+R+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+R+"))|)"+M+"*\\]",W=":("+R+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+I+")*)|.*)\\)|)",$=new RegExp(M+"+","g"),B=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),F=new RegExp("^"+M+"*,"+M+"*"),_=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="<a id='"+b+"'></a><select id='"+b+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:he(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:he(function(e,t,n){for(var r=n<0?n+t:n;--r>=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=r.pseudos.eq;for(t in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})r.pseudos[t]=fe(t);for(t in{submit:!0,reset:!0})r.pseudos[t]=pe(t);function ye(){}ye.prototype=r.filters=r.pseudos,r.setFilters=new ye,a=oe.tokenize=function(e,t){var n,i,o,a,s,u,l,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=r.preFilter;while(s){n&&!(i=F.exec(s))||(i&&(s=s.slice(i[0].length)||s),u.push(o=[])),n=!1,(i=_.exec(s))&&(n=i.shift(),o.push({value:n,type:i[0].replace(B," ")}),s=s.slice(n.length));for(a in r.filter)!(i=V[a].exec(s))||l[a]&&!(i=l[a](i))||(n=i.shift(),o.push({value:n,type:a,matches:i}),s=s.slice(n.length));if(!n)break}return t?s.length:s?oe.error(e):k(e,u).slice(0)};function ve(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function me(e,t,n){var r=t.dir,i=t.next,o=i||r,a=n&&"parentNode"===o,s=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||a)return e(t,n,i);return!1}:function(t,n,u){var l,c,f,p=[T,s];if(u){while(t=t[r])if((1===t.nodeType||a)&&e(t,n,u))return!0}else while(t=t[r])if(1===t.nodeType||a)if(f=t[b]||(t[b]={}),c=f[t.uniqueID]||(f[t.uniqueID]={}),i&&i===t.nodeName.toLowerCase())t=t[r]||t;else{if((l=c[o])&&l[0]===T&&l[1]===s)return p[2]=l[2];if(c[o]=p,p[2]=e(t,n,u))return!0}return!1}}function xe(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r<i;r++)oe(e,t[r],n);return n}function we(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Te(e,t,n,r,i,o){return r&&!r[b]&&(r=Te(r)),i&&!i[b]&&(i=Te(i,o)),se(function(o,a,s,u){var l,c,f,p=[],d=[],h=a.length,g=o||be(t||"*",s.nodeType?[s]:s,[]),y=!e||!o&&t?g:we(g,p,e,s,u),v=n?i||(o?e:h||r)?[]:a:y;if(n&&n(y,v,s,u),r){l=we(v,d),r(l,[],s,u),c=l.length;while(c--)(f=l[c])&&(v[d[c]]=!(y[d[c]]=f))}if(o){if(i||e){if(i){l=[],c=v.length;while(c--)(f=v[c])&&l.push(y[c]=f);i(null,v=[],l,u)}c=v.length;while(c--)(f=v[c])&&(l=i?O(o,f):p[c])>-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u<o;u++)if(n=r.relative[e[u].type])p=[me(xe(p),n)];else{if((n=r.filter[e[u].type].apply(null,e[u].matches))[b]){for(i=++u;i<o;i++)if(r.relative[e[i].type])break;return Te(u>1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u<i&&Ce(e.slice(u,i)),i<o&&Ce(e=e.slice(i)),i<o&&ve(e))}p.push(n)}return xe(p)}function Ee(e,t){var n=t.length>0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t<r;t++)if(w.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)w.find(e,i[t],n);return r>1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(w.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&w(e);if(!D.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?a.index(n)>-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s<o.length)!1===o[s].apply(n[0],n[1])&&e.stopOnFalse&&(s=o.length,n=!1)}e.memory||(n=!1),t=!1,i&&(o=n?[]:"")},l={add:function(){return o&&(n&&!t&&(s=o.length-1,a.push(n)),function t(n){w.each(n,function(n,r){g(r)?e.unique&&l.has(r)||o.push(r):r&&r.length&&"string"!==x(r)&&t(r)})}(arguments),n&&!t&&u()),this},remove:function(){return w.each(arguments,function(e,t){var n;while((n=w.inArray(t,o,n))>-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t<o)){if((e=r.apply(s,u))===n.promise())throw new TypeError("Thenable self-resolution");l=e&&("object"==typeof e||"function"==typeof e)&&e.then,g(l)?i?l.call(e,a(o,n,I,i),a(o,n,W,i)):(o++,l.call(e,a(o,n,I,i),a(o,n,W,i),a(o,n,I,n.notifyWith))):(r!==I&&(s=void 0,u=[e]),(i||n.resolveWith)(s,u))}},c=i?l:function(){try{l()}catch(e){w.Deferred.exceptionHook&&w.Deferred.exceptionHook(e,c.stackTrace),t+1>=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},X=/^-ms-/,U=/-([a-z])/g;function V(e,t){return t.toUpperCase()}function G(e){return e.replace(X,"ms-").replace(U,V)}var Y=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function Q(){this.expando=w.expando+Q.uid++}Q.uid=1,Q.prototype={cache:function(e){var t=e[this.expando];return t||(t={},Y(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[G(t)]=n;else for(r in t)i[G(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][G(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(G):(t=G(t))in r?[t]:t.match(M)||[]).length;while(n--)delete r[t[n]]}(void 0===t||w.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!w.isEmptyObject(t)}};var J=new Q,K=new Q,Z=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,ee=/[A-Z]/g;function te(e){return"true"===e||"false"!==e&&("null"===e?null:e===+e+""?+e:Z.test(e)?JSON.parse(e):e)}function ne(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(ee,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n=te(n)}catch(e){}K.set(e,t,n)}else n=void 0;return n}w.extend({hasData:function(e){return K.hasData(e)||J.hasData(e)},data:function(e,t,n){return K.access(e,t,n)},removeData:function(e,t){K.remove(e,t)},_data:function(e,t,n){return J.access(e,t,n)},_removeData:function(e,t){J.remove(e,t)}}),w.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=K.get(o),1===o.nodeType&&!J.get(o,"hasDataAttrs"))){n=a.length;while(n--)a[n]&&0===(r=a[n].name).indexOf("data-")&&(r=G(r.slice(5)),ne(o,r,i[r]));J.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof e?this.each(function(){K.set(this,e)}):z(this,function(t){var n;if(o&&void 0===t){if(void 0!==(n=K.get(o,e)))return n;if(void 0!==(n=ne(o,e)))return n}else this.each(function(){K.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length<n?w.queue(this[0],e):void 0===t?this:this.each(function(){var n=w.queue(this,e,t);w._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&w.dequeue(this,e)})},dequeue:function(e){return this.each(function(){w.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=w.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=J.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var re=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,ie=new RegExp("^(?:([+-])=|)("+re+")([a-z%]*)$","i"),oe=["Top","Right","Bottom","Left"],ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&w.contains(e.ownerDocument,e)&&"none"===w.css(e,"display")},se=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i};function ue(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return w.css(e,t,"")},u=s(),l=n&&n[3]||(w.cssNumber[t]?"":"px"),c=(w.cssNumber[t]||"px"!==l&&+u)&&ie.exec(w.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)w.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,w.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var le={};function ce(e){var t,n=e.ownerDocument,r=e.nodeName,i=le[r];return i||(t=n.body.appendChild(n.createElement(r)),i=w.css(t,"display"),t.parentNode.removeChild(t),"none"===i&&(i="block"),le[r]=i,i)}function fe(e,t){for(var n,r,i=[],o=0,a=e.length;o<a;o++)(r=e[o]).style&&(n=r.style.display,t?("none"===n&&(i[o]=J.get(r,"display")||null,i[o]||(r.style.display="")),""===r.style.display&&ae(r)&&(i[o]=ce(r))):"none"!==n&&(i[o]="none",J.set(r,"display",n)));for(o=0;o<a;o++)null!=i[o]&&(e[o].style.display=i[o]);return e}w.fn.extend({show:function(){return fe(this,!0)},hide:function(){return fe(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?w(this).show():w(this).hide()})}});var pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n<r;n++)J.set(e[n],"globalEval",!t||J.get(t[n],"globalEval"))}var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===x(o))w.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+w.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;w.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&w.inArray(o,r)>-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="<textarea>x</textarea>",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n<arguments.length;n++)u[n]=arguments[n];if(t.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,t)){s=w.event.handlers.call(this,t,l),n=0;while((o=s[n++])&&!t.isPropagationStopped()){t.currentTarget=o.elem,r=0;while((a=o.handlers[r++])&&!t.isImmediatePropagationStopped())t.rnamespace&&!t.rnamespace.test(a.namespace)||(t.handleObj=a,t.data=a.data,void 0!==(i=((w.event.special[a.origType]||{}).handle||a.handler).apply(o.elem,u))&&!1===(t.result=i)&&(t.preventDefault(),t.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,t),t.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&e.button>=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?w(i,this).index(l)>-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(e,t){Object.defineProperty(w.Event.prototype,e,{enumerable:!0,configurable:!0,get:g(t)?function(){if(this.originalEvent)return t(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[e]},set:function(t){Object.defineProperty(this,e,{enumerable:!0,configurable:!0,writable:!0,value:t})}})},fix:function(e){return e[w.expando]?e:new w.Event(e)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==Se()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===Se()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&N(this,"input"))return this.click(),!1},_default:function(e){return N(e.target,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},w.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},w.Event=function(e,t){if(!(this instanceof w.Event))return new w.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ee:ke,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&w.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[w.expando]=!0},w.Event.prototype={constructor:w.Event,isDefaultPrevented:ke,isPropagationStopped:ke,isImmediatePropagationStopped:ke,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ee,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ee,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ee,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},w.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&we.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&Te.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},w.event.addProp),w.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,t){w.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return i&&(i===r||w.contains(r,i))||(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),w.fn.extend({on:function(e,t,n,r){return De(this,e,t,n,r)},one:function(e,t,n,r){return De(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,w(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=ke),this.each(function(){w.event.remove(this,e,n,t)})}});var Ne=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/<script|<style|<link/i,je=/checked\s*(?:[^=]|=\s*.checked.)/i,qe=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n<r;n++)w.event.add(t,i,l[i][n])}K.hasData(e)&&(s=K.access(e),u=w.extend({},s),K.set(t,u))}}function Me(e,t){var n=t.nodeName.toLowerCase();"input"===n&&pe.test(e.type)?t.checked=e.checked:"input"!==n&&"textarea"!==n||(t.defaultValue=e.defaultValue)}function Re(e,t,n,r){t=a.apply([],t);var i,o,s,u,l,c,f=0,p=e.length,d=p-1,y=t[0],v=g(y);if(v||p>1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f<p;f++)l=i,f!==d&&(l=w.clone(l,!0,!0),u&&w.merge(s,ye(l,"script"))),n.call(e[f],l,f);if(u)for(c=s[s.length-1].ownerDocument,w.map(s,Oe),f=0;f<u;f++)l=s[f],he.test(l.type||"")&&!J.access(l,"globalEval")&&w.contains(c,l)&&(l.src&&"module"!==(l.type||"").toLowerCase()?w._evalUrl&&w._evalUrl(l.src):m(l.textContent.replace(qe,""),c,l))}return e}function Ie(e,t,n){for(var r,i=t?w.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||w.cleanData(ye(r)),r.parentNode&&(n&&w.contains(r.ownerDocument,r)&&ve(ye(r,"script")),r.parentNode.removeChild(r));return e}w.extend({htmlPrefilter:function(e){return e.replace(Ne,"<$1></$2>")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r<i;r++)Me(o[r],a[r]);if(t)if(n)for(o=o||ye(e),a=a||ye(s),r=0,i=o.length;r<i;r++)Pe(o[r],a[r]);else Pe(e,s);return(a=ye(s,"script")).length>0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(w.cleanData(ye(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=[];return Re(this,arguments,function(t){var n=this.parentNode;w.inArray(this,e)<0&&(w.cleanData(ye(this)),n&&n.replaceChild(t,this))},e)}}),w.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){w.fn[e]=function(e){for(var n,r=[],i=w(e),o=i.length-1,a=0;a<=o;a++)n=a===o?this:this.clone(!0),w(i[a])[t](n),s.apply(r,n.get());return this.pushStack(r)}});var We=new RegExp("^("+re+")(?!px)[a-z%]+$","i"),$e=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},Be=new RegExp(oe.join("|"),"i");!function(){function t(){if(c){l.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",c.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",be.appendChild(l).appendChild(c);var t=e.getComputedStyle(c);i="1%"!==t.top,u=12===n(t.marginLeft),c.style.right="60%",s=36===n(t.right),o=36===n(t.width),c.style.position="absolute",a=36===c.offsetWidth||"absolute",be.removeChild(l),c=null}}function n(e){return Math.round(parseFloat(e))}var i,o,a,s,u,l=r.createElement("div"),c=r.createElement("div");c.style&&(c.style.backgroundClip="content-box",c.cloneNode(!0).style.backgroundClip="",h.clearCloneStyle="content-box"===c.style.backgroundClip,w.extend(h,{boxSizingReliable:function(){return t(),o},pixelBoxStyles:function(){return t(),s},pixelPosition:function(){return t(),i},reliableMarginLeft:function(){return t(),u},scrollboxSize:function(){return t(),a}}))}();function Fe(e,t,n){var r,i,o,a,s=e.style;return(n=n||$e(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||w.contains(e.ownerDocument,e)||(a=w.style(e,t)),!h.pixelBoxStyles()&&We.test(a)&&Be.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function _e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}var ze=/^(none|table(?!-c[ea]).+)/,Xe=/^--/,Ue={position:"absolute",visibility:"hidden",display:"block"},Ve={letterSpacing:"0",fontWeight:"400"},Ge=["Webkit","Moz","ms"],Ye=r.createElement("div").style;function Qe(e){if(e in Ye)return e;var t=e[0].toUpperCase()+e.slice(1),n=Ge.length;while(n--)if((e=Ge[n]+t)in Ye)return e}function Je(e){var t=w.cssProps[e];return t||(t=w.cssProps[e]=Qe(e)||e),t}function Ke(e,t,n){var r=ie.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ze(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=w.css(e,n+oe[a],!0,i)),r?("content"===n&&(u-=w.css(e,"padding"+oe[a],!0,i)),"margin"!==n&&(u-=w.css(e,"border"+oe[a]+"Width",!0,i))):(u+=w.css(e,"padding"+oe[a],!0,i),"padding"!==n?u+=w.css(e,"border"+oe[a]+"Width",!0,i):s+=w.css(e,"border"+oe[a]+"Width",!0,i));return!r&&o>=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a<i;a++)o[t[a]]=w.css(e,t[a],!1,r);return o}return void 0!==n?w.style(e,t,n):w.css(e,t)},e,t,arguments.length>1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ct(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),y=J.get(e,"fxshow");n.queue||(null==(a=w._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,w.queue(e,"fx").length||a.empty.fire()})}));for(r in t)if(i=t[r],it.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!y||void 0===y[r])continue;g=!0}d[r]=y&&y[r]||w.style(e,r)}if((u=!w.isEmptyObject(t))||!w.isEmptyObject(d)){f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=y&&y.display)&&(l=J.get(e,"display")),"none"===(c=w.css(e,"display"))&&(l?c=l:(fe([e],!0),l=e.style.display||l,c=w.css(e,"display"),fe([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===w.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1;for(r in d)u||(y?"hidden"in y&&(g=y.hidden):y=J.access(e,"fxshow",{display:l}),o&&(y.hidden=!g),g&&fe([e],!0),p.done(function(){g||fe([e]),J.remove(e,"fxshow");for(r in d)w.style(e,r,d[r])})),u=lt(g?y[r]:0,r,p),r in y||(y[r]=u.start,g&&(u.end=u.start,u.start=0))}}function ft(e,t){var n,r,i,o,a;for(n in e)if(r=G(n),i=t[r],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=w.cssHooks[r])&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function pt(e,t,n){var r,i,o=0,a=pt.prefilters.length,s=w.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;for(var t=nt||st(),n=Math.max(0,l.startTime+l.duration-t),r=1-(n/l.duration||0),o=0,a=l.tweens.length;o<a;o++)l.tweens[o].run(r);return s.notifyWith(e,[l,r,n]),r<1&&a?n:(a||s.notifyWith(e,[l,1,0]),s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:w.extend({},t),opts:w.extend(!0,{specialEasing:{},easing:w.easing._default},n),originalProperties:t,originalOptions:n,startTime:nt||st(),duration:n.duration,tweens:[],createTween:function(t,n){var r=w.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;n<r;n++)l.tweens[n].run(1);return t?(s.notifyWith(e,[l,1,0]),s.resolveWith(e,[l,t])):s.rejectWith(e,[l,t]),this}}),c=l.props;for(ft(c,l.opts.specialEasing);o<a;o++)if(r=pt.prefilters[o].call(l,e,c,l.opts))return g(r.stop)&&(w._queueHooks(l.elem,l.opts.queue).stop=r.stop.bind(r)),r;return w.map(c,lt,l),g(l.opts.start)&&l.opts.start.call(e,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),w.fx.timer(w.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l}w.Animation=w.extend(pt,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return ue(n.elem,e,ie.exec(t),n),n}]},tweener:function(e,t){g(e)?(t=e,e=["*"]):e=e.match(M);for(var n,r=0,i=e.length;r<i;r++)n=e[r],pt.tweeners[n]=pt.tweeners[n]||[],pt.tweeners[n].unshift(t)},prefilters:[ct],prefilter:function(e,t){t?pt.prefilters.unshift(e):pt.prefilters.push(e)}}),w.speed=function(e,t,n){var r=e&&"object"==typeof e?w.extend({},e):{complete:n||!n&&t||g(e)&&e,duration:e,easing:n&&t||t&&!g(t)&&t};return w.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in w.fx.speeds?r.duration=w.fx.speeds[r.duration]:r.duration=w.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){g(r.old)&&r.old.call(this),r.queue&&w.dequeue(this,r.queue)},r},w.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=w.isEmptyObject(e),o=w.speed(t,n,r),a=function(){var t=pt(this,w.extend({},e),o);(i||J.get(this,"finish"))&&t.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=void 0),t&&!1!==e&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=w.timers,a=J.get(this);if(i)a[i]&&a[i].stop&&r(a[i]);else for(i in a)a[i]&&a[i].stop&&ot.test(i)&&r(a[i]);for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));!t&&n||w.dequeue(this,e)})},finish:function(e){return!1!==e&&(e=e||"fx"),this.each(function(){var t,n=J.get(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=w.timers,a=r?r.length:0;for(n.finish=!0,w.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;t<a;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),w.each(["toggle","show","hide"],function(e,t){var n=w.fn[t];w.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ut(t,!0),e,r,i)}}),w.each({slideDown:ut("show"),slideUp:ut("hide"),slideToggle:ut("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){w.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),w.timers=[],w.fx.tick=function(){var e,t=0,n=w.timers;for(nt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||w.fx.stop(),nt=void 0},w.fx.timer=function(e){w.timers.push(e),w.fx.start()},w.fx.interval=13,w.fx.start=function(){rt||(rt=!0,at())},w.fx.stop=function(){rt=null},w.fx.speeds={slow:600,fast:200,_default:400},w.fn.delay=function(t,n){return t=w.fx?w.fx.speeds[t]||t:t,n=n||"fx",this.queue(n,function(n,r){var i=e.setTimeout(n,t);r.stop=function(){e.clearTimeout(i)}})},function(){var e=r.createElement("input"),t=r.createElement("select").appendChild(r.createElement("option"));e.type="checkbox",h.checkOn=""!==e.value,h.optSelected=t.selected,(e=r.createElement("input")).value="t",e.type="radio",h.radioValue="t"===e.value}();var dt,ht=w.expr.attrHandle;w.fn.extend({attr:function(e,t){return z(this,w.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!N(n.parentNode,"optgroup"))){if(t=w(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=w.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=w.inArray(w.valHooks.option.get(r),o)>-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w("<script>").prop({charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&o("error"===e.type?404:200,e.type)}),r.head.appendChild(t[0])},abort:function(){n&&n()}}}});var Yt=[],Qt=/(=)\?(?=&|$)|\?\?/;w.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Yt.pop()||w.expando+"_"+Et++;return this[e]=!0,e}}),w.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=!1!==t.jsonp&&(Qt.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&Qt.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=g(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(Qt,"$1"+i):!1!==t.jsonp&&(t.url+=(kt.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||w.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?w(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,Yt.push(i)),a&&g(o)&&o(a[0]),a=o=void 0}),"script"}),h.createHTMLDocument=function(){var e=r.implementation.createHTMLDocument("").body;return e.innerHTML="<form></form><form></form>",2===e.childNodes.length}(),w.parseHTML=function(e,t,n){if("string"!=typeof e)return[];"boolean"==typeof t&&(n=t,t=!1);var i,o,a;return t||(h.createHTMLDocument?((i=(t=r.implementation.createHTMLDocument("")).createElement("base")).href=r.location.href,t.head.appendChild(i)):t=r),o=A.exec(e),a=!n&&[],o?[t.createElement(o[1])]:(o=xe([e],t,a),a&&a.length&&w(a).remove(),w.merge([],o.childNodes))},w.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=vt(e.slice(s)),e=e.slice(0,s)),g(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&w.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?w("<div>").append(w.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},w.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){w.fn[t]=function(e){return this.on(t,e)}}),w.expr.pseudos.animated=function(e){return w.grep(w.timers,function(t){return e===t.elem}).length},w.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=w.css(e,"position"),f=w(e),p={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=w.css(e,"top"),u=w.css(e,"left"),(l=("absolute"===c||"fixed"===c)&&(o+u).indexOf("auto")>-1)?(a=(r=f.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),g(t)&&(t=t.call(e,n,w.extend({},s))),null!=t.top&&(p.top=t.top-s.top+a),null!=t.left&&(p.left=t.left-s.left+i),"using"in t?t.using.call(e,p):f.css(p)}},w.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){w.offset.setOffset(this,e,t)});var t,n,r=this[0];if(r)return r.getClientRects().length?(t=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:t.top+n.pageYOffset,left:t.left+n.pageXOffset}):{top:0,left:0}},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===w.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===w.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=w(e).offset()).top+=w.css(e,"borderTopWidth",!0),i.left+=w.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-w.css(r,"marginTop",!0),left:t.left-i.left-w.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===w.css(e,"position"))e=e.offsetParent;return e||be})}}),w.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n="pageYOffset"===t;w.fn[e]=function(r){return z(this,function(e,r,i){var o;if(y(e)?o=e:9===e.nodeType&&(o=e.defaultView),void 0===i)return o?o[t]:e[r];o?o.scrollTo(n?o.pageXOffset:i,n?i:o.pageYOffset):e[r]=i},e,r,arguments.length)}}),w.each(["top","left"],function(e,t){w.cssHooks[t]=_e(h.pixelPosition,function(e,n){if(n)return n=Fe(e,t),We.test(n)?w(e).position()[t]+"px":n})}),w.each({Height:"height",Width:"width"},function(e,t){w.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){w.fn[r]=function(i,o){var a=arguments.length&&(n||"boolean"!=typeof i),s=n||(!0===i||!0===o?"margin":"border");return z(this,function(t,n,i){var o;return y(t)?0===r.indexOf("outer")?t["inner"+e]:t.document.documentElement["client"+e]:9===t.nodeType?(o=t.documentElement,Math.max(t.body["scroll"+e],o["scroll"+e],t.body["offset"+e],o["offset"+e],o["client"+e])):void 0===i?w.css(t,n,s):w.style(t,n,i,s)},t,a?i:void 0,a)}})}),w.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,t){w.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),w.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),w.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),w.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),g(e))return r=o.call(arguments,2),i=function(){return e.apply(t||this,r.concat(o.call(arguments)))},i.guid=e.guid=e.guid||w.guid++,i},w.holdReady=function(e){e?w.readyWait++:w.ready(!0)},w.isArray=Array.isArray,w.parseJSON=JSON.parse,w.nodeName=N,w.isFunction=g,w.isWindow=y,w.camelCase=G,w.type=x,w.now=Date.now,w.isNumeric=function(e){var t=w.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},"function"==typeof define&&define.amd&&define("jquery",[],function(){return w});var Jt=e.jQuery,Kt=e.$;return w.noConflict=function(t){return e.$===w&&(e.$=Kt),t&&e.jQuery===w&&(e.jQuery=Jt),w},t||(e.jQuery=e.$=w),w}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The MIT License (MIT) | |
Copyright (c) 2018 Richard Cook | |
Permission is hereby granted, free of charge, to any person obtaining a copy of | |
this software and associated documentation files (the "Software"), to deal in | |
the Software without restriction, including without limitation the rights to | |
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | |
the Software, and to permit persons to whom the Software is furnished to do so, | |
subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | |
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
server { | |
listen 80 ssl; | |
server_name mytestingurl.org; | |
ssl_certificate /path/to/crt; | |
ssl_certificate_key /path/to/key; | |
ssl_session_cache builtin:1000 shared:SSL:10m; | |
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; | |
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4; | |
ssl_prefer_server_ciphers on; | |
location / { | |
proxy_pass http://localhost:3000; | |
proxy_intercept_errors on; | |
proxy_set_header X-Real-IP $remote_addr; | |
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
proxy_set_header Host $http_host; | |
proxy_set_header X-NginX-Proxy true; | |
proxy_set_header X-Forwarded-Proto $scheme; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment