").append(jQuery.parseHTML(responseText)).find(selector) :
-
- // Otherwise use the full result
- responseText);
-
- }).complete(callback && function (jqXHR, status) {
- self.each(callback, response || [jqXHR.responseText, status, jqXHR]);
- });
- }
-
- return this;
- };
-
- // Attach a bunch of functions for handling common AJAX events
- jQuery.each(["ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend"], function (i, type) {
- jQuery.fn[type] = function (fn) {
- return this.on(type, fn);
- };
- });
-
- jQuery.extend({
-
- // Counter for holding the number of active queries
- active: 0,
-
- // Last-Modified header cache for next request
- lastModified: {},
- etag: {},
-
- ajaxSettings: {
- url: ajaxLocation,
- type: "GET",
- isLocal: rlocalProtocol.test(ajaxLocParts[1]),
- global: true,
- processData: true,
- async: true,
- contentType: "application/x-www-form-urlencoded; charset=UTF-8",
- /*
- timeout: 0,
- data: null,
- dataType: null,
- username: null,
- password: null,
- cache: null,
- throws: false,
- traditional: false,
- headers: {},
- */
-
- accepts: {
- "*": allTypes,
- text: "text/plain",
- html: "text/html",
- xml: "application/xml, text/xml",
- json: "application/json, text/javascript"
- },
-
- contents: {
- xml: /xml/,
- html: /html/,
- json: /json/
- },
-
- responseFields: {
- xml: "responseXML",
- text: "responseText",
- json: "responseJSON"
- },
-
- // Data converters
- // Keys separate source (or catchall "*") and destination types with a single space
- converters: {
-
- // Convert anything to text
- "* text": String,
-
- // Text to html (true = no transformation)
- "text html": true,
-
- // Evaluate text as a json expression
- "text json": jQuery.parseJSON,
-
- // Parse text as xml
- "text xml": jQuery.parseXML
- },
-
- // For options that shouldn't be deep extended:
- // you can add your own custom options here if
- // and when you create one that shouldn't be
- // deep extended (see ajaxExtend)
- flatOptions: {
- url: true,
- context: true
- }
- },
-
- // Creates a full fledged settings object into target
- // with both ajaxSettings and settings fields.
- // If target is omitted, writes into ajaxSettings.
- ajaxSetup: function (target, settings) {
- return settings ?
-
- // Building a settings object
- ajaxExtend(ajaxExtend(target, jQuery.ajaxSettings), settings) :
-
- // Extending ajaxSettings
- ajaxExtend(jQuery.ajaxSettings, target);
- },
-
- ajaxPrefilter: addToPrefiltersOrTransports(prefilters),
- ajaxTransport: addToPrefiltersOrTransports(transports),
-
- // Main method
- ajax: function (url, options) {
-
- // If url is an object, simulate pre-1.5 signature
- if (typeof url === "object") {
- options = url;
- url = undefined;
- }
-
- // Force options to be an object
- options = options || {};
-
- var // Cross-domain detection vars
- parts,
- // Loop variable
- i,
- // URL without anti-cache param
- cacheURL,
- // Response headers as string
- responseHeadersString,
- // timeout handle
- timeoutTimer,
-
- // To know if global events are to be dispatched
- fireGlobals,
-
- transport,
- // Response headers
- responseHeaders,
- // Create the final options object
- s = jQuery.ajaxSetup({}, options),
- // Callbacks context
- callbackContext = s.context || s,
- // Context for global events is callbackContext if it is a DOM node or jQuery collection
- globalEventContext = s.context && (callbackContext.nodeType || callbackContext.jquery) ?
- jQuery(callbackContext) :
- jQuery.event,
- // Deferreds
- deferred = jQuery.Deferred(),
- completeDeferred = jQuery.Callbacks("once memory"),
- // Status-dependent callbacks
- statusCode = s.statusCode || {},
- // Headers (they are sent all at once)
- requestHeaders = {},
- requestHeadersNames = {},
- // The jqXHR state
- state = 0,
- // Default abort message
- strAbort = "canceled",
- // Fake xhr
- jqXHR = {
- readyState: 0,
-
- // Builds headers hashtable if needed
- getResponseHeader: function (key) {
- var match;
- if (state === 2) {
- if (!responseHeaders) {
- responseHeaders = {};
- while ((match = rheaders.exec(responseHeadersString))) {
- responseHeaders[match[1].toLowerCase()] = match[2];
- }
- }
- match = responseHeaders[key.toLowerCase()];
- }
- return match == null ? null : match;
- },
-
- // Raw string
- getAllResponseHeaders: function () {
- return state === 2 ? responseHeadersString : null;
- },
-
- // Caches the header
- setRequestHeader: function (name, value) {
- var lname = name.toLowerCase();
- if (!state) {
- name = requestHeadersNames[lname] = requestHeadersNames[lname] || name;
- requestHeaders[name] = value;
- }
- return this;
- },
-
- // Overrides response content-type header
- overrideMimeType: function (type) {
- if (!state) {
- s.mimeType = type;
- }
- return this;
- },
-
- // Status-dependent callbacks
- statusCode: function (map) {
- var code;
- if (map) {
- if (state < 2) {
- for (code in map) {
- // Lazy-add the new callback in a way that preserves old ones
- statusCode[code] = [statusCode[code], map[code]];
- }
- } else {
- // Execute the appropriate callbacks
- jqXHR.always(map[jqXHR.status]);
- }
- }
- return this;
- },
-
- // Cancel the request
- abort: function (statusText) {
- var finalText = statusText || strAbort;
- if (transport) {
- transport.abort(finalText);
- }
- done(0, finalText);
- return this;
- }
- };
-
- // Attach deferreds
- deferred.promise(jqXHR).complete = completeDeferred.add;
- jqXHR.success = jqXHR.done;
- jqXHR.error = jqXHR.fail;
-
- // Remove hash character (#7531: and string promotion)
- // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
- // Handle falsy url in the settings object (#10093: consistency with old signature)
- // We also use the url parameter if available
- s.url = ((url || s.url || ajaxLocation) + "").replace(rhash, "").replace(rprotocol, ajaxLocParts[1] + "//");
-
- // Alias method option to type as per ticket #12004
- s.type = options.method || options.type || s.method || s.type;
-
- // Extract dataTypes list
- s.dataTypes = jQuery.trim(s.dataType || "*").toLowerCase().match(core_rnotwhite) || [""];
-
- // A cross-domain request is in order when we have a protocol:host:port mismatch
- if (s.crossDomain == null) {
- parts = rurl.exec(s.url.toLowerCase());
- s.crossDomain = !! (parts &&
- (parts[1] !== ajaxLocParts[1] || parts[2] !== ajaxLocParts[2] ||
- (parts[3] || (parts[1] === "http:" ? "80" : "443")) !==
- (ajaxLocParts[3] || (ajaxLocParts[1] === "http:" ? "80" : "443")))
- );
- }
-
- // Convert data if not already a string
- if (s.data && s.processData && typeof s.data !== "string") {
- s.data = jQuery.param(s.data, s.traditional);
- }
-
- // Apply prefilters
- inspectPrefiltersOrTransports(prefilters, s, options, jqXHR);
-
- // If request was aborted inside a prefilter, stop there
- if (state === 2) {
- return jqXHR;
- }
-
- // We can fire global events as of now if asked to
- fireGlobals = s.global;
-
- // Watch for a new set of requests
- if (fireGlobals && jQuery.active++ === 0) {
- jQuery.event.trigger("ajaxStart");
- }
-
- // Uppercase the type
- s.type = s.type.toUpperCase();
-
- // Determine if request has content
- s.hasContent = !rnoContent.test(s.type);
-
- // Save the URL in case we're toying with the If-Modified-Since
- // and/or If-None-Match header later on
- cacheURL = s.url;
-
- // More options handling for requests with no content
- if (!s.hasContent) {
-
- // If data is available, append data to url
- if (s.data) {
- cacheURL = (s.url += (ajax_rquery.test(cacheURL) ? "&" : "?") + s.data);
- // #9682: remove data so that it's not used in an eventual retry
- delete s.data;
- }
-
- // Add anti-cache in url if needed
- if (s.cache === false) {
- s.url = rts.test(cacheURL) ?
-
- // If there is already a '_' parameter, set its value
- cacheURL.replace(rts, "$1_=" + ajax_nonce++) :
-
- // Otherwise add one to the end
- cacheURL + (ajax_rquery.test(cacheURL) ? "&" : "?") + "_=" + ajax_nonce++;
- }
- }
-
- // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
- if (s.ifModified) {
- if (jQuery.lastModified[cacheURL]) {
- jqXHR.setRequestHeader("If-Modified-Since", jQuery.lastModified[cacheURL]);
- }
- if (jQuery.etag[cacheURL]) {
- jqXHR.setRequestHeader("If-None-Match", jQuery.etag[cacheURL]);
- }
- }
-
- // Set the correct header, if data is being sent
- if (s.data && s.hasContent && s.contentType !== false || options.contentType) {
- jqXHR.setRequestHeader("Content-Type", s.contentType);
- }
-
- // Set the Accepts header for the server, depending on the dataType
- jqXHR.setRequestHeader(
- "Accept",
- s.dataTypes[0] && s.accepts[s.dataTypes[0]] ?
- s.accepts[s.dataTypes[0]] + (s.dataTypes[0] !== "*" ? ", " + allTypes + "; q=0.01" : "") :
- s.accepts["*"]
- );
-
- // Check for headers option
- for (i in s.headers) {
- jqXHR.setRequestHeader(i, s.headers[i]);
- }
-
- // Allow custom headers/mimetypes and early abort
- if (s.beforeSend && (s.beforeSend.call(callbackContext, jqXHR, s) === false || state === 2)) {
- // Abort if not done already and return
- return jqXHR.abort();
- }
-
- // aborting is no longer a cancellation
- strAbort = "abort";
-
- // Install callbacks on deferreds
- for (i in {
- success: 1,
- error: 1,
- complete: 1
- }) {
- jqXHR[i](s[i]);
- }
-
- // Get transport
- transport = inspectPrefiltersOrTransports(transports, s, options, jqXHR);
-
- // If no transport, we auto-abort
- if (!transport) {
- done(-1, "No Transport");
- } else {
- jqXHR.readyState = 1;
-
- // Send global event
- if (fireGlobals) {
- globalEventContext.trigger("ajaxSend", [jqXHR, s]);
- }
- // Timeout
- if (s.async && s.timeout > 0) {
- timeoutTimer = setTimeout(function () {
- jqXHR.abort("timeout");
- }, s.timeout);
- }
-
- try {
- state = 1;
- transport.send(requestHeaders, done);
- } catch (e) {
- // Propagate exception as error if not done
- if (state < 2) {
- done(-1, e);
- // Simply rethrow otherwise
- } else {
- throw e;
- }
- }
- }
-
- // Callback for when everything is done
-
- function done(status, nativeStatusText, responses, headers) {
- var isSuccess, success, error, response, modified,
- statusText = nativeStatusText;
-
- // Called once
- if (state === 2) {
- return;
- }
-
- // State is "done" now
- state = 2;
-
- // Clear timeout if it exists
- if (timeoutTimer) {
- clearTimeout(timeoutTimer);
- }
-
- // Dereference transport for early garbage collection
- // (no matter how long the jqXHR object will be used)
- transport = undefined;
-
- // Cache response headers
- responseHeadersString = headers || "";
-
- // Set readyState
- jqXHR.readyState = status > 0 ? 4 : 0;
-
- // Determine if successful
- isSuccess = status >= 200 && status < 300 || status === 304;
-
- // Get response data
- if (responses) {
- response = ajaxHandleResponses(s, jqXHR, responses);
- }
-
- // Convert no matter what (that way responseXXX fields are always set)
- response = ajaxConvert(s, response, jqXHR, isSuccess);
-
- // If successful, handle type chaining
- if (isSuccess) {
-
- // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
- if (s.ifModified) {
- modified = jqXHR.getResponseHeader("Last-Modified");
- if (modified) {
- jQuery.lastModified[cacheURL] = modified;
- }
- modified = jqXHR.getResponseHeader("etag");
- if (modified) {
- jQuery.etag[cacheURL] = modified;
- }
- }
-
- // if no content
- if (status === 204 || s.type === "HEAD") {
- statusText = "nocontent";
-
- // if not modified
- } else if (status === 304) {
- statusText = "notmodified";
-
- // If we have data, let's convert it
- } else {
- statusText = response.state;
- success = response.data;
- error = response.error;
- isSuccess = !error;
- }
- } else {
- // We extract error from statusText
- // then normalize statusText and status for non-aborts
- error = statusText;
- if (status || !statusText) {
- statusText = "error";
- if (status < 0) {
- status = 0;
- }
- }
- }
-
- // Set data for the fake xhr object
- jqXHR.status = status;
- jqXHR.statusText = (nativeStatusText || statusText) + "";
-
- // Success/Error
- if (isSuccess) {
- deferred.resolveWith(callbackContext, [success, statusText, jqXHR]);
- } else {
- deferred.rejectWith(callbackContext, [jqXHR, statusText, error]);
- }
-
- // Status-dependent callbacks
- jqXHR.statusCode(statusCode);
- statusCode = undefined;
-
- if (fireGlobals) {
- globalEventContext.trigger(isSuccess ? "ajaxSuccess" : "ajaxError", [jqXHR, s, isSuccess ? success : error]);
- }
-
- // Complete
- completeDeferred.fireWith(callbackContext, [jqXHR, statusText]);
-
- if (fireGlobals) {
- globalEventContext.trigger("ajaxComplete", [jqXHR, s]);
- // Handle the global AJAX counter
- if (!(--jQuery.active)) {
- jQuery.event.trigger("ajaxStop");
- }
- }
- }
-
- return jqXHR;
- },
-
- getJSON: function (url, data, callback) {
- return jQuery.get(url, data, callback, "json");
- },
-
- getScript: function (url, callback) {
- return jQuery.get(url, undefined, callback, "script");
- }
- });
-
- jQuery.each(["get", "post"], function (i, method) {
- jQuery[method] = function (url, data, callback, type) {
- // shift arguments if data argument was omitted
- if (jQuery.isFunction(data)) {
- type = type || callback;
- callback = data;
- data = undefined;
- }
-
- return jQuery.ajax({
- url: url,
- type: method,
- dataType: type,
- data: data,
- success: callback
- });
- };
- });
-
- /* Handles responses to an ajax request:
- * - finds the right dataType (mediates between content-type and expected dataType)
- * - returns the corresponding response
- */
-
- function ajaxHandleResponses(s, jqXHR, responses) {
- var firstDataType, ct, finalDataType, type,
- contents = s.contents,
- dataTypes = s.dataTypes;
-
- // Remove auto dataType and get content-type in the process
- while (dataTypes[0] === "*") {
- dataTypes.shift();
- if (ct === undefined) {
- ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
- }
- }
-
- // Check if we're dealing with a known content-type
- if (ct) {
- for (type in contents) {
- if (contents[type] && contents[type].test(ct)) {
- dataTypes.unshift(type);
- break;
- }
- }
- }
-
- // Check to see if we have a response for the expected dataType
- if (dataTypes[0] in responses) {
- finalDataType = dataTypes[0];
- } else {
- // Try convertible dataTypes
- for (type in responses) {
- if (!dataTypes[0] || s.converters[type + " " + dataTypes[0]]) {
- finalDataType = type;
- break;
- }
- if (!firstDataType) {
- firstDataType = type;
- }
- }
- // Or just use first one
- finalDataType = finalDataType || firstDataType;
- }
-
- // If we found a dataType
- // We add the dataType to the list if needed
- // and return the corresponding response
- if (finalDataType) {
- if (finalDataType !== dataTypes[0]) {
- dataTypes.unshift(finalDataType);
- }
- return responses[finalDataType];
- }
- }
-
- /* Chain conversions given the request and the original response
- * Also sets the responseXXX fields on the jqXHR instance
- */
-
- function ajaxConvert(s, response, jqXHR, isSuccess) {
- var conv2, current, conv, tmp, prev,
- converters = {},
- // Work with a copy of dataTypes in case we need to modify it for conversion
- dataTypes = s.dataTypes.slice();
-
- // Create converters map with lowercased keys
- if (dataTypes[1]) {
- for (conv in s.converters) {
- converters[conv.toLowerCase()] = s.converters[conv];
- }
- }
-
- current = dataTypes.shift();
-
- // Convert to each sequential dataType
- while (current) {
-
- if (s.responseFields[current]) {
- jqXHR[s.responseFields[current]] = response;
- }
-
- // Apply the dataFilter if provided
- if (!prev && isSuccess && s.dataFilter) {
- response = s.dataFilter(response, s.dataType);
- }
-
- prev = current;
- current = dataTypes.shift();
-
- if (current) {
-
- // There's only work to do if current dataType is non-auto
- if (current === "*") {
-
- current = prev;
-
- // Convert response if prev dataType is non-auto and differs from current
- } else if (prev !== "*" && prev !== current) {
-
- // Seek a direct converter
- conv = converters[prev + " " + current] || converters["* " + current];
-
- // If none found, seek a pair
- if (!conv) {
- for (conv2 in converters) {
-
- // If conv2 outputs current
- tmp = conv2.split(" ");
- if (tmp[1] === current) {
-
- // If prev can be converted to accepted input
- conv = converters[prev + " " + tmp[0]] ||
- converters["* " + tmp[0]];
- if (conv) {
- // Condense equivalence converters
- if (conv === true) {
- conv = converters[conv2];
-
- // Otherwise, insert the intermediate dataType
- } else if (converters[conv2] !== true) {
- current = tmp[0];
- dataTypes.unshift(tmp[1]);
- }
- break;
- }
- }
- }
- }
-
- // Apply converter (if not an equivalence)
- if (conv !== true) {
-
- // Unless errors are allowed to bubble, catch and return them
- if (conv && s["throws"]) {
- response = conv(response);
- } else {
- try {
- response = conv(response);
- } catch (e) {
- return {
- state: "parsererror",
- error: conv ? e : "No conversion from " + prev + " to " + current
- };
- }
- }
- }
- }
- }
- }
-
- return {
- state: "success",
- data: response
- };
- }
- // Install script dataType
- jQuery.ajaxSetup({
- accepts: {
- script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
- },
- contents: {
- script: /(?:java|ecma)script/
- },
- converters: {
- "text script": function (text) {
- jQuery.globalEval(text);
- return text;
- }
- }
- });
-
- // Handle cache's special case and global
- jQuery.ajaxPrefilter("script", function (s) {
- if (s.cache === undefined) {
- s.cache = false;
- }
- if (s.crossDomain) {
- s.type = "GET";
- s.global = false;
- }
- });
-
- // Bind script tag hack transport
- jQuery.ajaxTransport("script", function (s) {
-
- // This transport only deals with cross domain requests
- if (s.crossDomain) {
-
- var script,
- head = document.head || jQuery("head")[0] || document.documentElement;
-
- return {
-
- send: function (_, callback) {
-
- script = document.createElement("script");
-
- script.async = true;
-
- if (s.scriptCharset) {
- script.charset = s.scriptCharset;
- }
-
- script.src = s.url;
-
- // Attach handlers for all browsers
- script.onload = script.onreadystatechange = function (_, isAbort) {
-
- if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) {
-
- // Handle memory leak in IE
- script.onload = script.onreadystatechange = null;
-
- // Remove the script
- if (script.parentNode) {
- script.parentNode.removeChild(script);
- }
-
- // Dereference the script
- script = null;
-
- // Callback if not abort
- if (!isAbort) {
- callback(200, "success");
- }
- }
- };
-
- // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
- // Use native DOM manipulation to avoid our domManip AJAX trickery
- head.insertBefore(script, head.firstChild);
- },
-
- abort: function () {
- if (script) {
- script.onload(undefined, true);
- }
- }
- };
- }
- });
- var oldCallbacks = [],
- rjsonp = /(=)\?(?=&|$)|\?\?/;
-
- // Default jsonp settings
- jQuery.ajaxSetup({
- jsonp: "callback",
- jsonpCallback: function () {
- var callback = oldCallbacks.pop() || (jQuery.expando + "_" + (ajax_nonce++));
- this[callback] = true;
- return callback;
- }
- });
-
- // Detect, normalize options and install callbacks for jsonp requests
- jQuery.ajaxPrefilter("json jsonp", function (s, originalSettings, jqXHR) {
-
- var callbackName, overwritten, responseContainer,
- jsonProp = s.jsonp !== false && (rjsonp.test(s.url) ?
- "url" :
- typeof s.data === "string" && !(s.contentType || "").indexOf("application/x-www-form-urlencoded") && rjsonp.test(s.data) && "data"
- );
-
- // Handle iff the expected data type is "jsonp" or we have a parameter to set
- if (jsonProp || s.dataTypes[0] === "jsonp") {
-
- // Get callback name, remembering preexisting value associated with it
- callbackName = s.jsonpCallback = jQuery.isFunction(s.jsonpCallback) ?
- s.jsonpCallback() :
- s.jsonpCallback;
-
- // Insert callback into url or form data
- if (jsonProp) {
- s[jsonProp] = s[jsonProp].replace(rjsonp, "$1" + callbackName);
- } else if (s.jsonp !== false) {
- s.url += (ajax_rquery.test(s.url) ? "&" : "?") + s.jsonp + "=" + callbackName;
- }
-
- // Use data converter to retrieve json after script execution
- s.converters["script json"] = function () {
- if (!responseContainer) {
- jQuery.error(callbackName + " was not called");
- }
- return responseContainer[0];
- };
-
- // force json dataType
- s.dataTypes[0] = "json";
-
- // Install callback
- overwritten = window[callbackName];
- window[callbackName] = function () {
- responseContainer = arguments;
- };
-
- // Clean-up function (fires after converters)
- jqXHR.always(function () {
- // Restore preexisting value
- window[callbackName] = overwritten;
-
- // Save back as free
- if (s[callbackName]) {
- // make sure that re-using the options doesn't screw things around
- s.jsonpCallback = originalSettings.jsonpCallback;
-
- // save the callback name for future use
- oldCallbacks.push(callbackName);
- }
-
- // Call if it was a function and we have a response
- if (responseContainer && jQuery.isFunction(overwritten)) {
- overwritten(responseContainer[0]);
- }
-
- responseContainer = overwritten = undefined;
- });
-
- // Delegate to script
- return "script";
- }
- });
- var xhrCallbacks, xhrSupported,
- xhrId = 0,
- // #5280: Internet Explorer will keep connections alive if we don't abort on unload
- xhrOnUnloadAbort = window.ActiveXObject && function () {
- // Abort all pending requests
- var key;
- for (key in xhrCallbacks) {
- xhrCallbacks[key](undefined, true);
- }
- };
-
- // Functions to create xhrs
-
- function createStandardXHR() {
- try {
- return new window.XMLHttpRequest();
- } catch (e) {}
- }
-
- function createActiveXHR() {
- try {
- return new window.ActiveXObject("Microsoft.XMLHTTP");
- } catch (e) {}
- }
-
- // Create the request object
- // (This is still attached to ajaxSettings for backward compatibility)
- jQuery.ajaxSettings.xhr = window.ActiveXObject ?
- /* Microsoft failed to properly
- * implement the XMLHttpRequest in IE7 (can't request local files),
- * so we use the ActiveXObject when it is available
- * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
- * we need a fallback.
- */
-
- function () {
- return !this.isLocal && createStandardXHR() || createActiveXHR();
- } :
- // For all other browsers, use the standard XMLHttpRequest object
- createStandardXHR;
-
- // Determine support properties
- xhrSupported = jQuery.ajaxSettings.xhr();
- jQuery.support.cors = !! xhrSupported && ("withCredentials" in xhrSupported);
- xhrSupported = jQuery.support.ajax = !! xhrSupported;
-
- // Create transport if the browser can provide an xhr
- if (xhrSupported) {
-
- jQuery.ajaxTransport(function (s) {
- // Cross domain only allowed if supported through XMLHttpRequest
- if (!s.crossDomain || jQuery.support.cors) {
-
- var callback;
-
- return {
- send: function (headers, complete) {
-
- // Get a new xhr
- var handle, i,
- xhr = s.xhr();
-
- // Open the socket
- // Passing null username, generates a login popup on Opera (#2865)
- if (s.username) {
- xhr.open(s.type, s.url, s.async, s.username, s.password);
- } else {
- xhr.open(s.type, s.url, s.async);
- }
-
- // Apply custom fields if provided
- if (s.xhrFields) {
- for (i in s.xhrFields) {
- xhr[i] = s.xhrFields[i];
- }
- }
-
- // Override mime type if needed
- if (s.mimeType && xhr.overrideMimeType) {
- xhr.overrideMimeType(s.mimeType);
- }
-
- // X-Requested-With header
- // For cross-domain requests, seeing as conditions for a preflight are
- // akin to a jigsaw puzzle, we simply never set it to be sure.
- // (it can always be set on a per-request basis or even using ajaxSetup)
- // For same-domain requests, won't change header if already provided.
- if (!s.crossDomain && !headers["X-Requested-With"]) {
- headers["X-Requested-With"] = "XMLHttpRequest";
- }
-
- // Need an extra try/catch for cross domain requests in Firefox 3
- try {
- for (i in headers) {
- xhr.setRequestHeader(i, headers[i]);
- }
- } catch (err) {}
-
- // Do send the request
- // This may raise an exception which is actually
- // handled in jQuery.ajax (so no try/catch here)
- xhr.send((s.hasContent && s.data) || null);
-
- // Listener
- callback = function (_, isAbort) {
- var status, responseHeaders, statusText, responses;
-
- // Firefox throws exceptions when accessing properties
- // of an xhr when a network error occurred
- // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
- try {
-
- // Was never called and is aborted or complete
- if (callback && (isAbort || xhr.readyState === 4)) {
-
- // Only called once
- callback = undefined;
-
- // Do not keep as active anymore
- if (handle) {
- xhr.onreadystatechange = jQuery.noop;
- if (xhrOnUnloadAbort) {
- delete xhrCallbacks[handle];
- }
- }
-
- // If it's an abort
- if (isAbort) {
- // Abort it manually if needed
- if (xhr.readyState !== 4) {
- xhr.abort();
- }
- } else {
- responses = {};
- status = xhr.status;
- responseHeaders = xhr.getAllResponseHeaders();
-
- // When requesting binary data, IE6-9 will throw an exception
- // on any attempt to access responseText (#11426)
- if (typeof xhr.responseText === "string") {
- responses.text = xhr.responseText;
- }
-
- // Firefox throws an exception when accessing
- // statusText for faulty cross-domain requests
- try {
- statusText = xhr.statusText;
- } catch (e) {
- // We normalize with Webkit giving an empty statusText
- statusText = "";
- }
-
- // Filter status for non standard behaviors
-
- // If the request is local and we have data: assume a success
- // (success with no data won't get notified, that's the best we
- // can do given current implementations)
- if (!status && s.isLocal && !s.crossDomain) {
- status = responses.text ? 200 : 404;
- // IE - #1450: sometimes returns 1223 when it should be 204
- } else if (status === 1223) {
- status = 204;
- }
- }
- }
- } catch (firefoxAccessException) {
- if (!isAbort) {
- complete(-1, firefoxAccessException);
- }
- }
-
- // Call complete if needed
- if (responses) {
- complete(status, statusText, responses, responseHeaders);
- }
- };
-
- if (!s.async) {
- // if we're in sync mode we fire the callback
- callback();
- } else if (xhr.readyState === 4) {
- // (IE6 & IE7) if it's in cache and has been
- // retrieved directly we need to fire the callback
- setTimeout(callback);
- } else {
- handle = ++xhrId;
- if (xhrOnUnloadAbort) {
- // Create the active xhrs callbacks list if needed
- // and attach the unload handler
- if (!xhrCallbacks) {
- xhrCallbacks = {};
- jQuery(window).unload(xhrOnUnloadAbort);
- }
- // Add to list of active xhrs callbacks
- xhrCallbacks[handle] = callback;
- }
- xhr.onreadystatechange = callback;
- }
- },
-
- abort: function () {
- if (callback) {
- callback(undefined, true);
- }
- }
- };
- }
- });
- }
- var fxNow, timerId,
- rfxtypes = /^(?:toggle|show|hide)$/,
- rfxnum = new RegExp("^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i"),
- rrun = /queueHooks$/,
- animationPrefilters = [defaultPrefilter],
- tweeners = {
- "*": [
- function (prop, value) {
- var tween = this.createTween(prop, value),
- target = tween.cur(),
- parts = rfxnum.exec(value),
- unit = parts && parts[3] || (jQuery.cssNumber[prop] ? "" : "px"),
-
- // Starting value computation is required for potential unit mismatches
- start = (jQuery.cssNumber[prop] || unit !== "px" && +target) &&
- rfxnum.exec(jQuery.css(tween.elem, prop)),
- scale = 1,
- maxIterations = 20;
-
- if (start && start[3] !== unit) {
- // Trust units reported by jQuery.css
- unit = unit || start[3];
-
- // Make sure we update the tween properties later on
- parts = parts || [];
-
- // Iteratively approximate from a nonzero starting point
- start = +target || 1;
-
- do {
- // If previous iteration zeroed out, double until we get *something*
- // Use a string for doubling factor so we don't accidentally see scale as unchanged below
- scale = scale || ".5";
-
- // Adjust and apply
- start = start / scale;
- jQuery.style(tween.elem, prop, start + unit);
-
- // Update scale, tolerating zero or NaN from tween.cur()
- // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
- } while (scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations);
- }
-
- // Update tween properties
- if (parts) {
- start = tween.start = +start || +target || 0;
- tween.unit = unit;
- // If a +=/-= token was provided, we're doing a relative animation
- tween.end = parts[1] ?
- start + (parts[1] + 1) * parts[2] : +parts[2];
- }
-
- return tween;
- }
- ]
- };
-
- // Animations created synchronously will run synchronously
-
- function createFxNow() {
- setTimeout(function () {
- fxNow = undefined;
- });
- return (fxNow = jQuery.now());
- }
-
- function createTween(value, prop, animation) {
- var tween,
- collection = (tweeners[prop] || []).concat(tweeners["*"]),
- index = 0,
- length = collection.length;
- for (; index < length; index++) {
- if ((tween = collection[index].call(animation, prop, value))) {
-
- // we're done with this property
- return tween;
- }
- }
- }
-
- function Animation(elem, properties, options) {
- var result,
- stopped,
- index = 0,
- length = animationPrefilters.length,
- deferred = jQuery.Deferred().always(function () {
- // don't match elem in the :animated selector
- delete tick.elem;
- }),
- tick = function () {
- if (stopped) {
- return false;
- }
- var currentTime = fxNow || createFxNow(),
- remaining = Math.max(0, animation.startTime + animation.duration - currentTime),
- // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
- temp = remaining / animation.duration || 0,
- percent = 1 - temp,
- index = 0,
- length = animation.tweens.length;
-
- for (; index < length; index++) {
- animation.tweens[index].run(percent);
- }
-
- deferred.notifyWith(elem, [animation, percent, remaining]);
-
- if (percent < 1 && length) {
- return remaining;
- } else {
- deferred.resolveWith(elem, [animation]);
- return false;
- }
- },
- animation = deferred.promise({
- elem: elem,
- props: jQuery.extend({}, properties),
- opts: jQuery.extend(true, {
- specialEasing: {}
- }, options),
- originalProperties: properties,
- originalOptions: options,
- startTime: fxNow || createFxNow(),
- duration: options.duration,
- tweens: [],
- createTween: function (prop, end) {
- var tween = jQuery.Tween(elem, animation.opts, prop, end,
- animation.opts.specialEasing[prop] || animation.opts.easing);
- animation.tweens.push(tween);
- return tween;
- },
- stop: function (gotoEnd) {
- var index = 0,
- // if we are going to the end, we want to run all the tweens
- // otherwise we skip this part
- length = gotoEnd ? animation.tweens.length : 0;
- if (stopped) {
- return this;
- }
- stopped = true;
- for (; index < length; index++) {
- animation.tweens[index].run(1);
- }
-
- // resolve when we played the last frame
- // otherwise, reject
- if (gotoEnd) {
- deferred.resolveWith(elem, [animation, gotoEnd]);
- } else {
- deferred.rejectWith(elem, [animation, gotoEnd]);
- }
- return this;
- }
- }),
- props = animation.props;
-
- propFilter(props, animation.opts.specialEasing);
-
- for (; index < length; index++) {
- result = animationPrefilters[index].call(animation, elem, props, animation.opts);
- if (result) {
- return result;
- }
- }
-
- jQuery.map(props, createTween, animation);
-
- if (jQuery.isFunction(animation.opts.start)) {
- animation.opts.start.call(elem, animation);
- }
-
- jQuery.fx.timer(
- jQuery.extend(tick, {
- elem: elem,
- anim: animation,
- queue: animation.opts.queue
- })
- );
-
- // attach callbacks from options
- return animation.progress(animation.opts.progress)
- .done(animation.opts.done, animation.opts.complete)
- .fail(animation.opts.fail)
- .always(animation.opts.always);
- }
-
- function propFilter(props, specialEasing) {
- var index, name, easing, value, hooks;
-
- // camelCase, specialEasing and expand cssHook pass
- for (index in props) {
- name = jQuery.camelCase(index);
- easing = specialEasing[name];
- value = props[index];
- if (jQuery.isArray(value)) {
- easing = value[1];
- value = props[index] = value[0];
- }
-
- if (index !== name) {
- props[name] = value;
- delete props[index];
- }
-
- hooks = jQuery.cssHooks[name];
- if (hooks && "expand" in hooks) {
- value = hooks.expand(value);
- delete props[name];
-
- // not quite $.extend, this wont overwrite keys already present.
- // also - reusing 'index' from above because we have the correct "name"
- for (index in value) {
- if (!(index in props)) {
- props[index] = value[index];
- specialEasing[index] = easing;
- }
- }
- } else {
- specialEasing[name] = easing;
- }
- }
- }
-
- jQuery.Animation = jQuery.extend(Animation, {
-
- tweener: function (props, callback) {
- if (jQuery.isFunction(props)) {
- callback = props;
- props = ["*"];
- } else {
- props = props.split(" ");
- }
-
- var prop,
- index = 0,
- length = props.length;
-
- for (; index < length; index++) {
- prop = props[index];
- tweeners[prop] = tweeners[prop] || [];
- tweeners[prop].unshift(callback);
- }
- },
-
- prefilter: function (callback, prepend) {
- if (prepend) {
- animationPrefilters.unshift(callback);
- } else {
- animationPrefilters.push(callback);
- }
- }
- });
-
- function defaultPrefilter(elem, props, opts) {
- /* jshint validthis: true */
- var prop, value, toggle, tween, hooks, oldfire,
- anim = this,
- orig = {},
- style = elem.style,
- hidden = elem.nodeType && isHidden(elem),
- dataShow = jQuery._data(elem, "fxshow");
-
- // handle queue: false promises
- if (!opts.queue) {
- hooks = jQuery._queueHooks(elem, "fx");
- if (hooks.unqueued == null) {
- hooks.unqueued = 0;
- oldfire = hooks.empty.fire;
- hooks.empty.fire = function () {
- if (!hooks.unqueued) {
- oldfire();
- }
- };
- }
- hooks.unqueued++;
-
- anim.always(function () {
- // doing this makes sure that the complete handler will be called
- // before this completes
- anim.always(function () {
- hooks.unqueued--;
- if (!jQuery.queue(elem, "fx").length) {
- hooks.empty.fire();
- }
- });
- });
- }
-
- // height/width overflow pass
- if (elem.nodeType === 1 && ("height" in props || "width" in props)) {
- // Make sure that nothing sneaks out
- // Record all 3 overflow attributes because IE does not
- // change the overflow attribute when overflowX and
- // overflowY are set to the same value
- opts.overflow = [style.overflow, style.overflowX, style.overflowY];
-
- // Set display property to inline-block for height/width
- // animations on inline elements that are having width/height animated
- if (jQuery.css(elem, "display") === "inline" &&
- jQuery.css(elem, "float") === "none") {
-
- // inline-level elements accept inline-block;
- // block-level elements need to be inline with layout
- if (!jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay(elem.nodeName) === "inline") {
- style.display = "inline-block";
-
- } else {
- style.zoom = 1;
- }
- }
- }
-
- if (opts.overflow) {
- style.overflow = "hidden";
- if (!jQuery.support.shrinkWrapBlocks) {
- anim.always(function () {
- style.overflow = opts.overflow[0];
- style.overflowX = opts.overflow[1];
- style.overflowY = opts.overflow[2];
- });
- }
- }
-
-
- // show/hide pass
- for (prop in props) {
- value = props[prop];
- if (rfxtypes.exec(value)) {
- delete props[prop];
- toggle = toggle || value === "toggle";
- if (value === (hidden ? "hide" : "show")) {
- continue;
- }
- orig[prop] = dataShow && dataShow[prop] || jQuery.style(elem, prop);
- }
- }
-
- if (!jQuery.isEmptyObject(orig)) {
- if (dataShow) {
- if ("hidden" in dataShow) {
- hidden = dataShow.hidden;
- }
- } else {
- dataShow = jQuery._data(elem, "fxshow", {});
- }
-
- // store state if its toggle - enables .stop().toggle() to "reverse"
- if (toggle) {
- dataShow.hidden = !hidden;
- }
- if (hidden) {
- jQuery(elem).show();
- } else {
- anim.done(function () {
- jQuery(elem).hide();
- });
- }
- anim.done(function () {
- var prop;
- jQuery._removeData(elem, "fxshow");
- for (prop in orig) {
- jQuery.style(elem, prop, orig[prop]);
- }
- });
- for (prop in orig) {
- tween = createTween(hidden ? dataShow[prop] : 0, prop, anim);
-
- if (!(prop in dataShow)) {
- dataShow[prop] = tween.start;
- if (hidden) {
- tween.end = tween.start;
- tween.start = prop === "width" || prop === "height" ? 1 : 0;
- }
- }
- }
- }
- }
-
- function Tween(elem, options, prop, end, easing) {
- return new Tween.prototype.init(elem, options, prop, end, easing);
- }
- jQuery.Tween = Tween;
-
- Tween.prototype = {
- constructor: Tween,
- init: function (elem, options, prop, end, easing, unit) {
- this.elem = elem;
- this.prop = prop;
- this.easing = easing || "swing";
- this.options = options;
- this.start = this.now = this.cur();
- this.end = end;
- this.unit = unit || (jQuery.cssNumber[prop] ? "" : "px");
- },
- cur: function () {
- var hooks = Tween.propHooks[this.prop];
-
- return hooks && hooks.get ?
- hooks.get(this) :
- Tween.propHooks._default.get(this);
- },
- run: function (percent) {
- var eased,
- hooks = Tween.propHooks[this.prop];
-
- if (this.options.duration) {
- this.pos = eased = jQuery.easing[this.easing](
- percent, this.options.duration * percent, 0, 1, this.options.duration
- );
- } else {
- this.pos = eased = percent;
- }
- this.now = (this.end - this.start) * eased + this.start;
-
- if (this.options.step) {
- this.options.step.call(this.elem, this.now, this);
- }
-
- if (hooks && hooks.set) {
- hooks.set(this);
- } else {
- Tween.propHooks._default.set(this);
- }
- return this;
- }
- };
-
- Tween.prototype.init.prototype = Tween.prototype;
-
- Tween.propHooks = {
- _default: {
- get: function (tween) {
- var result;
-
- if (tween.elem[tween.prop] != null &&
- (!tween.elem.style || tween.elem.style[tween.prop] == null)) {
- return tween.elem[tween.prop];
- }
-
- // passing an empty string as a 3rd parameter to .css will automatically
- // attempt a parseFloat and fallback to a string if the parse fails
- // so, simple values such as "10px" are parsed to Float.
- // complex values such as "rotate(1rad)" are returned as is.
- result = jQuery.css(tween.elem, tween.prop, "");
- // Empty strings, null, undefined and "auto" are converted to 0.
- return !result || result === "auto" ? 0 : result;
- },
- set: function (tween) {
- // use step hook for back compat - use cssHook if its there - use .style if its
- // available and use plain properties where available
- if (jQuery.fx.step[tween.prop]) {
- jQuery.fx.step[tween.prop](tween);
- } else if (tween.elem.style && (tween.elem.style[jQuery.cssProps[tween.prop]] != null || jQuery.cssHooks[tween.prop])) {
- jQuery.style(tween.elem, tween.prop, tween.now + tween.unit);
- } else {
- tween.elem[tween.prop] = tween.now;
- }
- }
- }
- };
-
- // Support: IE <=9
- // Panic based approach to setting things on disconnected nodes
-
- Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
- set: function (tween) {
- if (tween.elem.nodeType && tween.elem.parentNode) {
- tween.elem[tween.prop] = tween.now;
- }
- }
- };
-
- jQuery.each(["toggle", "show", "hide"], function (i, name) {
- var cssFn = jQuery.fn[name];
- jQuery.fn[name] = function (speed, easing, callback) {
- return speed == null || typeof speed === "boolean" ?
- cssFn.apply(this, arguments) :
- this.animate(genFx(name, true), speed, easing, callback);
- };
- });
-
- jQuery.fn.extend({
- fadeTo: function (speed, to, easing, callback) {
-
- // show any hidden elements after setting opacity to 0
- return this.filter(isHidden).css("opacity", 0).show()
-
- // animate to the value specified
- .end().animate({
- opacity: to
- }, speed, easing, callback);
- },
- animate: function (prop, speed, easing, callback) {
- var empty = jQuery.isEmptyObject(prop),
- optall = jQuery.speed(speed, easing, callback),
- doAnimation = function () {
- // Operate on a copy of prop so per-property easing won't be lost
- var anim = Animation(this, jQuery.extend({}, prop), optall);
-
- // Empty animations, or finishing resolves immediately
- if (empty || jQuery._data(this, "finish")) {
- anim.stop(true);
- }
- };
- doAnimation.finish = doAnimation;
-
- return empty || optall.queue === false ?
- this.each(doAnimation) :
- this.queue(optall.queue, doAnimation);
- },
- stop: function (type, clearQueue, gotoEnd) {
- var stopQueue = function (hooks) {
- var stop = hooks.stop;
- delete hooks.stop;
- stop(gotoEnd);
- };
-
- if (typeof type !== "string") {
- gotoEnd = clearQueue;
- clearQueue = type;
- type = undefined;
- }
- if (clearQueue && type !== false) {
- this.queue(type || "fx", []);
- }
-
- return this.each(function () {
- var dequeue = true,
- index = type != null && type + "queueHooks",
- timers = jQuery.timers,
- data = jQuery._data(this);
-
- if (index) {
- if (data[index] && data[index].stop) {
- stopQueue(data[index]);
- }
- } else {
- for (index in data) {
- if (data[index] && data[index].stop && rrun.test(index)) {
- stopQueue(data[index]);
- }
- }
- }
-
- for (index = timers.length; index--;) {
- if (timers[index].elem === this && (type == null || timers[index].queue === type)) {
- timers[index].anim.stop(gotoEnd);
- dequeue = false;
- timers.splice(index, 1);
- }
- }
-
- // start the next in the queue if the last step wasn't forced
- // timers currently will call their complete callbacks, which will dequeue
- // but only if they were gotoEnd
- if (dequeue || !gotoEnd) {
- jQuery.dequeue(this, type);
- }
- });
- },
- finish: function (type) {
- if (type !== false) {
- type = type || "fx";
- }
- return this.each(function () {
- var index,
- data = jQuery._data(this),
- queue = data[type + "queue"],
- hooks = data[type + "queueHooks"],
- timers = jQuery.timers,
- length = queue ? queue.length : 0;
-
- // enable finishing flag on private data
- data.finish = true;
-
- // empty the queue first
- jQuery.queue(this, type, []);
-
- if (hooks && hooks.stop) {
- hooks.stop.call(this, true);
- }
-
- // look for any active animations, and finish them
- for (index = timers.length; index--;) {
- if (timers[index].elem === this && timers[index].queue === type) {
- timers[index].anim.stop(true);
- timers.splice(index, 1);
- }
- }
-
- // look for any animations in the old queue and finish them
- for (index = 0; index < length; index++) {
- if (queue[index] && queue[index].finish) {
- queue[index].finish.call(this);
- }
- }
-
- // turn off finishing flag
- delete data.finish;
- });
- }
- });
-
- // Generate parameters to create a standard animation
-
- function genFx(type, includeWidth) {
- var which,
- attrs = {
- height: type
- },
- i = 0;
-
- // if we include width, step value is 1 to do all cssExpand values,
- // if we don't include width, step value is 2 to skip over Left and Right
- includeWidth = includeWidth ? 1 : 0;
- for (; i < 4; i += 2 - includeWidth) {
- which = cssExpand[i];
- attrs["margin" + which] = attrs["padding" + which] = type;
- }
-
- if (includeWidth) {
- attrs.opacity = attrs.width = type;
- }
-
- return attrs;
- }
-
- // Generate shortcuts for custom animations
- jQuery.each({
- slideDown: genFx("show"),
- slideUp: genFx("hide"),
- slideToggle: genFx("toggle"),
- fadeIn: {
- opacity: "show"
- },
- fadeOut: {
- opacity: "hide"
- },
- fadeToggle: {
- opacity: "toggle"
- }
- }, function (name, props) {
- jQuery.fn[name] = function (speed, easing, callback) {
- return this.animate(props, speed, easing, callback);
- };
- });
-
- jQuery.speed = function (speed, easing, fn) {
- var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : {
- complete: fn || !fn && easing || jQuery.isFunction(speed) && speed,
- duration: speed,
- easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
- };
-
- opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
- opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default;
-
- // normalize opt.queue - true/undefined/null -> "fx"
- if (opt.queue == null || opt.queue === true) {
- opt.queue = "fx";
- }
-
- // Queueing
- opt.old = opt.complete;
-
- opt.complete = function () {
- if (jQuery.isFunction(opt.old)) {
- opt.old.call(this);
- }
-
- if (opt.queue) {
- jQuery.dequeue(this, opt.queue);
- }
- };
-
- return opt;
- };
-
- jQuery.easing = {
- linear: function (p) {
- return p;
- },
- swing: function (p) {
- return 0.5 - Math.cos(p * Math.PI) / 2;
- }
- };
-
- jQuery.timers = [];
- jQuery.fx = Tween.prototype.init;
- jQuery.fx.tick = function () {
- var timer,
- timers = jQuery.timers,
- i = 0;
-
- fxNow = jQuery.now();
-
- for (; i < timers.length; i++) {
- timer = timers[i];
- // Checks the timer has not already been removed
- if (!timer() && timers[i] === timer) {
- timers.splice(i--, 1);
- }
- }
-
- if (!timers.length) {
- jQuery.fx.stop();
- }
- fxNow = undefined;
- };
-
- jQuery.fx.timer = function (timer) {
- if (timer() && jQuery.timers.push(timer)) {
- jQuery.fx.start();
- }
- };
-
- jQuery.fx.interval = 13;
-
- jQuery.fx.start = function () {
- if (!timerId) {
- timerId = setInterval(jQuery.fx.tick, jQuery.fx.interval);
- }
- };
-
- jQuery.fx.stop = function () {
- clearInterval(timerId);
- timerId = null;
- };
-
- jQuery.fx.speeds = {
- slow: 600,
- fast: 200,
- // Default speed
- _default: 400
- };
-
- // Back Compat <1.8 extension point
- jQuery.fx.step = {};
-
- if (jQuery.expr && jQuery.expr.filters) {
- jQuery.expr.filters.animated = function (elem) {
- return jQuery.grep(jQuery.timers, function (fn) {
- return elem === fn.elem;
- }).length;
- };
- }
- jQuery.fn.offset = function (options) {
- if (arguments.length) {
- return options === undefined ?
- this :
- this.each(function (i) {
- jQuery.offset.setOffset(this, options, i);
- });
- }
-
- var docElem, win,
- box = {
- top: 0,
- left: 0
- },
- elem = this[0],
- doc = elem && elem.ownerDocument;
-
- if (!doc) {
- return;
- }
-
- docElem = doc.documentElement;
-
- // Make sure it's not a disconnected DOM node
- if (!jQuery.contains(docElem, elem)) {
- return box;
- }
-
- // If we don't have gBCR, just use 0,0 rather than error
- // BlackBerry 5, iOS 3 (original iPhone)
- if (typeof elem.getBoundingClientRect !== core_strundefined) {
- box = elem.getBoundingClientRect();
- }
- win = getWindow(doc);
- return {
- top: box.top + (win.pageYOffset || docElem.scrollTop) - (docElem.clientTop || 0),
- left: box.left + (win.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || 0)
- };
- };
-
- jQuery.offset = {
-
- setOffset: function (elem, options, i) {
- var position = jQuery.css(elem, "position");
-
- // set position first, in-case top/left are set even on static elem
- if (position === "static") {
- elem.style.position = "relative";
- }
-
- var curElem = jQuery(elem),
- curOffset = curElem.offset(),
- curCSSTop = jQuery.css(elem, "top"),
- curCSSLeft = jQuery.css(elem, "left"),
- calculatePosition = (position === "absolute" || position === "fixed") && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
- props = {}, curPosition = {}, curTop, curLeft;
-
- // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
- if (calculatePosition) {
- curPosition = curElem.position();
- curTop = curPosition.top;
- curLeft = curPosition.left;
- } else {
- curTop = parseFloat(curCSSTop) || 0;
- curLeft = parseFloat(curCSSLeft) || 0;
- }
-
- if (jQuery.isFunction(options)) {
- options = options.call(elem, i, curOffset);
- }
-
- if (options.top != null) {
- props.top = (options.top - curOffset.top) + curTop;
- }
- if (options.left != null) {
- props.left = (options.left - curOffset.left) + curLeft;
- }
-
- if ("using" in options) {
- options.using.call(elem, props);
- } else {
- curElem.css(props);
- }
- }
- };
-
-
- jQuery.fn.extend({
-
- position: function () {
- if (!this[0]) {
- return;
- }
-
- var offsetParent, offset,
- parentOffset = {
- top: 0,
- left: 0
- },
- elem = this[0];
-
- // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
- if (jQuery.css(elem, "position") === "fixed") {
- // we assume that getBoundingClientRect is available when computed position is fixed
- offset = elem.getBoundingClientRect();
- } else {
- // Get *real* offsetParent
- offsetParent = this.offsetParent();
-
- // Get correct offsets
- offset = this.offset();
- if (!jQuery.nodeName(offsetParent[0], "html")) {
- parentOffset = offsetParent.offset();
- }
-
- // Add offsetParent borders
- parentOffset.top += jQuery.css(offsetParent[0], "borderTopWidth", true);
- parentOffset.left += jQuery.css(offsetParent[0], "borderLeftWidth", true);
- }
-
- // Subtract parent offsets and element margins
- // note: when an element has margin: auto the offsetLeft and marginLeft
- // are the same in Safari causing offset.left to incorrectly be 0
- return {
- top: offset.top - parentOffset.top - jQuery.css(elem, "marginTop", true),
- left: offset.left - parentOffset.left - jQuery.css(elem, "marginLeft", true)
- };
- },
-
- offsetParent: function () {
- return this.map(function () {
- var offsetParent = this.offsetParent || docElem;
- while (offsetParent && (!jQuery.nodeName(offsetParent, "html") && jQuery.css(offsetParent, "position") === "static")) {
- offsetParent = offsetParent.offsetParent;
- }
- return offsetParent || docElem;
- });
- }
- });
-
-
- // Create scrollLeft and scrollTop methods
- jQuery.each({
- scrollLeft: "pageXOffset",
- scrollTop: "pageYOffset"
- }, function (method, prop) {
- var top = /Y/.test(prop);
-
- jQuery.fn[method] = function (val) {
- return jQuery.access(this, function (elem, method, val) {
- var win = getWindow(elem);
-
- if (val === undefined) {
- return win ? (prop in win) ? win[prop] :
- win.document.documentElement[method] :
- elem[method];
- }
-
- if (win) {
- win.scrollTo(!top ? val : jQuery(win).scrollLeft(),
- top ? val : jQuery(win).scrollTop()
- );
-
- } else {
- elem[method] = val;
- }
- }, method, val, arguments.length, null);
- };
- });
-
- function getWindow(elem) {
- return jQuery.isWindow(elem) ?
- elem :
- elem.nodeType === 9 ?
- elem.defaultView || elem.parentWindow :
- false;
- }
- // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
- jQuery.each({
- Height: "height",
- Width: "width"
- }, function (name, type) {
- jQuery.each({
- padding: "inner" + name,
- content: type,
- "": "outer" + name
- }, function (defaultExtra, funcName) {
- // margin is only for outerHeight, outerWidth
- jQuery.fn[funcName] = function (margin, value) {
- var chainable = arguments.length && (defaultExtra || typeof margin !== "boolean"),
- extra = defaultExtra || (margin === true || value === true ? "margin" : "border");
-
- return jQuery.access(this, function (elem, type, value) {
- var doc;
-
- if (jQuery.isWindow(elem)) {
- // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
- // isn't a whole lot we can do. See pull request at this URL for discussion:
- // https://github.com/jquery/jquery/pull/764
- return elem.document.documentElement["client" + name];
- }
-
- // Get document width or height
- if (elem.nodeType === 9) {
- doc = elem.documentElement;
-
- // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
- // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
- return Math.max(
- elem.body["scroll" + name], doc["scroll" + name],
- elem.body["offset" + name], doc["offset" + name],
- doc["client" + name]
- );
- }
-
- return value === undefined ?
- // Get width or height on the element, requesting but not forcing parseFloat
- jQuery.css(elem, type, extra) :
-
- // Set width or height on the element
- jQuery.style(elem, type, value, extra);
- }, type, chainable ? margin : undefined, chainable, null);
- };
- });
- });
- // Limit scope pollution from any deprecated API
- // (function() {
-
- // The number of elements contained in the matched element set
- jQuery.fn.size = function () {
- return this.length;
- };
-
- jQuery.fn.andSelf = jQuery.fn.addBack;
-
- // })();
- if (typeof module === "object" && module && typeof module.exports === "object") {
- // Expose jQuery as module.exports in loaders that implement the Node
- // module pattern (including browserify). Do not create the global, since
- // the user will be storing it themselves locally, and globals are frowned
- // upon in the Node module world.
- module.exports = jQuery;
- } else {
- // Otherwise expose jQuery to the global object as usual
- window.jQuery = window.$ = jQuery;
-
- // Register as a named AMD module, since jQuery can be concatenated with other
- // files that may use define, but not via a proper concatenation script that
- // understands anonymous AMD modules. A named AMD is safest and most robust
- // way to register. Lowercase jquery is used because AMD module names are
- // derived from file names, and jQuery is normally delivered in a lowercase
- // file name. Do this after creating the global so that if an AMD module wants
- // to call noConflict to hide this version of jQuery, it will work.
- if (typeof define === "function" && define.amd) {
- define("jquery", [], function () {
- return jQuery;
- });
- }
- }
-
-})(window);
\ No newline at end of file
From 1e83d33283643167c08a0c7c60c79bcb6bdcd00d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?=
Date: Tue, 23 May 2017 15:37:32 -0400
Subject: [PATCH 36/42] tests for login
---
src/controllers/authentication.js | 54 +++++------
test/authentication.js | 146 +++++++++++++++++++-----------
2 files changed, 122 insertions(+), 78 deletions(-)
diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js
index 5ca9c421ad..cc4ef41aab 100644
--- a/src/controllers/authentication.js
+++ b/src/controllers/authentication.js
@@ -282,14 +282,14 @@ authenticationController.doLogin = function (req, uid, callback) {
if (!uid) {
return callback();
}
-
- req.login({ uid: uid }, function (err) {
- if (err) {
- return callback(err);
- }
-
- authenticationController.onSuccessfulLogin(req, uid, callback);
- });
+ async.waterfall([
+ function (next) {
+ req.login({ uid: uid }, next);
+ },
+ function (next) {
+ authenticationController.onSuccessfulLogin(req, uid, next);
+ },
+ ], callback);
};
authenticationController.onSuccessfulLogin = function (req, uid, callback) {
@@ -312,28 +312,30 @@ authenticationController.onSuccessfulLogin = function (req, uid, callback) {
version: req.useragent.version,
});
- // Associate login session with user
- async.parallel([
+ async.waterfall([
function (next) {
- user.auth.addSession(uid, req.sessionID, next);
+ async.parallel([
+ function (next) {
+ user.auth.addSession(uid, req.sessionID, next);
+ },
+ function (next) {
+ db.setObjectField('uid:' + uid + ':sessionUUID:sessionId', uuid, req.sessionID, next);
+ },
+ function (next) {
+ user.updateLastOnlineTime(uid, next);
+ },
+ ], function (err) {
+ next(err);
+ });
},
function (next) {
- db.setObjectField('uid:' + uid + ':sessionUUID:sessionId', uuid, req.sessionID, next);
- },
- function (next) {
- user.updateLastOnlineTime(uid, next);
- },
- ], function (err) {
- if (err) {
- return callback(err);
- }
+ // Force session check for all connected socket.io clients with the same session id
+ sockets.in('sess_' + req.sessionID).emit('checkSession', uid);
- // Force session check for all connected socket.io clients with the same session id
- sockets.in('sess_' + req.sessionID).emit('checkSession', uid);
-
- plugins.fireHook('action:user.loggedIn', { uid: uid, req: req });
- callback();
- });
+ plugins.fireHook('action:user.loggedIn', { uid: uid, req: req });
+ next();
+ },
+ ], callback);
};
authenticationController.localLogin = function (req, username, password, next) {
diff --git a/test/authentication.js b/test/authentication.js
index 9d49a8e0ff..4ab15b500f 100644
--- a/test/authentication.js
+++ b/test/authentication.js
@@ -7,8 +7,37 @@ var request = require('request');
var db = require('./mocks/databasemock');
var user = require('../src/user');
+var meta = require('../src/meta');
describe('authentication', function () {
+ function loginUser(username, password, callback) {
+ var jar = request.jar();
+ request({
+ url: nconf.get('url') + '/api/config',
+ json: true,
+ jar: jar,
+ }, function (err, response, body) {
+ if (err) {
+ return callback(err);
+ }
+
+ request.post(nconf.get('url') + '/login', {
+ form: {
+ username: username,
+ password: password,
+ },
+ json: true,
+ jar: jar,
+ headers: {
+ 'x-csrf-token': body.csrf_token,
+ },
+ }, function (err, response, body) {
+ callback(err, response, body, jar);
+ });
+ });
+ }
+
+
var jar = request.jar();
var regularUid;
before(function (done) {
@@ -89,43 +118,24 @@ describe('authentication', function () {
});
it('should login a user', function (done) {
- var jar = request.jar();
- request({
- url: nconf.get('url') + '/api/config',
- json: true,
- jar: jar,
- }, function (err, response, body) {
+ loginUser('regular', 'regularpwd', function (err, response, body, jar) {
assert.ifError(err);
+ assert(body);
- request.post(nconf.get('url') + '/login', {
- form: {
- username: 'regular',
- password: 'regularpwd',
- },
+ request({
+ url: nconf.get('url') + '/api/me',
json: true,
jar: jar,
- headers: {
- 'x-csrf-token': body.csrf_token,
- },
}, function (err, response, body) {
assert.ifError(err);
assert(body);
-
- request({
- url: nconf.get('url') + '/api/me',
- json: true,
- jar: jar,
- }, function (err, response, body) {
+ assert.equal(body.username, 'regular');
+ assert.equal(body.email, 'regular@nodebb.org');
+ db.getObject('uid:' + regularUid + ':sessionUUID:sessionId', function (err, sessions) {
assert.ifError(err);
- assert(body);
- assert.equal(body.username, 'regular');
- assert.equal(body.email, 'regular@nodebb.org');
- db.getObject('uid:' + regularUid + ':sessionUUID:sessionId', function (err, sessions) {
- assert.ifError(err);
- assert(sessions);
- assert(Object.keys(sessions).length > 0);
- done();
- });
+ assert(sessions);
+ assert(Object.keys(sessions).length > 0);
+ done();
});
});
});
@@ -148,33 +158,65 @@ describe('authentication', function () {
});
it('should fail to login if user does not exist', function (done) {
- var jar = request.jar();
- request({
- url: nconf.get('url') + '/api/config',
- json: true,
- jar: jar,
- }, function (err, response, body) {
+ loginUser('doesnotexist', 'nopassword', function (err, response, body) {
assert.ifError(err);
-
- request.post(nconf.get('url') + '/login', {
- form: {
- username: 'doesnotexist',
- password: 'nopassword',
- },
- json: true,
- jar: jar,
- headers: {
- 'x-csrf-token': body.csrf_token,
- },
- }, function (err, response, body) {
- assert.ifError(err);
- assert.equal(response.statusCode, 403);
- assert.equal(body, '[[error:invalid-login-credentials]]');
- done();
- });
+ assert.equal(response.statusCode, 403);
+ assert.equal(body, '[[error:invalid-login-credentials]]');
+ done();
});
});
+ it('should fail to login if username is empty', function (done) {
+ loginUser('', 'some password', function (err, response, body) {
+ assert.ifError(err);
+ assert.equal(response.statusCode, 403);
+ assert.equal(body, '[[error:invalid-username-or-password]]');
+ done();
+ });
+ });
+
+ it('should fail to login if password is empty', function (done) {
+ loginUser('someuser', '', function (err, response, body) {
+ assert.ifError(err);
+ assert.equal(response.statusCode, 403);
+ assert.equal(body, '[[error:invalid-username-or-password]]');
+ done();
+ });
+ });
+
+ it('should fail to login if username and password are empty', function (done) {
+ loginUser('', '', function (err, response, body) {
+ assert.ifError(err);
+ assert.equal(response.statusCode, 403);
+ assert.equal(body, '[[error:invalid-username-or-password]]');
+ done();
+ });
+ });
+
+ it('should fail to login if password is longer than 4096', function (done) {
+ var longPassword;
+ for (var i = 0; i < 5000; i++) {
+ longPassword += 'a';
+ }
+ loginUser('someuser', longPassword, function (err, response, body) {
+ assert.ifError(err);
+ assert.equal(response.statusCode, 403);
+ assert.equal(body, '[[error:password-too-long]]');
+ done();
+ });
+ });
+
+
+ it('should fail to login if local login is disabled', function (done) {
+ meta.config.allowLocalLogin = 0;
+ loginUser('someuser', 'somepass', function (err, response, body) {
+ meta.config.allowLocalLogin = 1;
+ assert.ifError(err);
+ assert.equal(response.statusCode, 403);
+ assert.equal(body, '[[error:local-login-disabled]]');
+ done();
+ });
+ });
after(function (done) {
db.emptydb(done);
From 542ae5e71f2e89681e74738932a8052c8748eec4 Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Wed, 17 May 2017 15:59:55 -0600
Subject: [PATCH 37/42] Extract in-place translation from bootbox wrapper for
use anywhere
---
public/src/modules/translator.js | 66 +++++++++++++++++++++++++++++++-
public/vendor/bootbox/wrapper.js | 61 ++++-------------------------
2 files changed, 72 insertions(+), 55 deletions(-)
diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js
index afd64317ef..5296e1eef4 100644
--- a/public/src/modules/translator.js
+++ b/public/src/modules/translator.js
@@ -304,7 +304,7 @@
* Load translation file (or use a cached version), and optionally return the translation of a certain key
* @param {string} namespace - The file name of the translation namespace
* @param {string} [key] - The key of the specific translation to getJSON
- * @returns {Promise|Promise}
+ * @returns {Promise<{ [key: string]: string }>|Promise}
*/
Translator.prototype.getTranslation = function getTranslation(namespace, key) {
var translation;
@@ -324,6 +324,70 @@
return translation;
};
+ /**
+ * @param {Node} node
+ * @returns {Node[]}
+ */
+ function descendantTextNodes(node) {
+ var textNodes = [];
+
+ function helper(node) {
+ if (node.nodeType === 3) {
+ textNodes.push(node);
+ } else {
+ for (var i = 0, c = node.childNodes, l = c.length; i < l; i += 1) {
+ helper(c[i]);
+ }
+ }
+ }
+
+ helper(node);
+ return textNodes;
+ }
+
+ /**
+ * Recursively translate a DOM element in place
+ * @param {Element} element - Root element to translate
+ * @param {string[]} [attributes] - Array of node attributes to translate
+ * @returns {Promise}
+ */
+ Translator.prototype.translateInPlace = function translateInPlace(element, attributes) {
+ attributes = attributes || ['placeholder', 'title'];
+
+ var nodes = descendantTextNodes(element);
+ var text = nodes.map(function (node) {
+ return node.nodeValue;
+ }).join(' || ');
+
+ var attrNodes = attributes.reduce(function (prev, attr) {
+ var tuples = Array.prototype.map.call(element.querySelectorAll('[' + attr + '*="[["]'), function (el) {
+ return [attr, el];
+ });
+ return prev.concat(tuples);
+ }, []);
+ var attrText = attrNodes.map(function (node) {
+ return node[1].getAttribute(node[0]);
+ }).join(' || ');
+
+ return Promise.all([
+ this.translate(text),
+ this.translate(attrText),
+ ]).then(function (ref) {
+ var translated = ref[0];
+ var translatedAttrs = ref[1];
+ if (translated) {
+ translated.split(' || ').forEach(function (html, i) {
+ $(nodes[i]).replaceWith(html);
+ });
+ }
+ if (translatedAttrs) {
+ translatedAttrs.split(' || ').forEach(function (text, i) {
+ attrNodes[i][1].setAttribute(attrNodes[i][0], text);
+ });
+ }
+ });
+ };
+
/**
* Get the language of the current environment, falling back to defaults
* @returns {string}
diff --git a/public/vendor/bootbox/wrapper.js b/public/vendor/bootbox/wrapper.js
index 505b42788f..35efdd8305 100644
--- a/public/vendor/bootbox/wrapper.js
+++ b/public/vendor/bootbox/wrapper.js
@@ -1,67 +1,20 @@
/* global bootbox */
require(['translator'], function (shim) {
- "use strict";
-
- function descendantTextNodes(node) {
- var textNodes = [];
-
- function helper(node) {
- if (node.nodeType === 3) {
- textNodes.push(node);
- } else {
- for (var i = 0, c = node.childNodes, l = c.length; i < l; i += 1) {
- helper(c[i]);
- }
- }
- }
-
- helper(node);
- return textNodes;
- }
+ 'use strict';
var translator = shim.Translator.create();
var dialog = bootbox.dialog;
var attrsToTranslate = ['placeholder', 'title', 'value'];
bootbox.dialog = function (options) {
- var show, $elem, nodes, text, attrNodes, attrText;
-
- show = options.show !== false;
+ var show = options.show !== false;
options.show = false;
- $elem = dialog.call(bootbox, options);
+ var $elem = dialog.call(bootbox, options);
+ var element = $elem[0];
- if (/\[\[.+\]\]/.test($elem[0].outerHTML)) {
- nodes = descendantTextNodes($elem[0]);
- text = nodes.map(function (node) {
- return node.nodeValue;
- }).join(' || ');
-
- attrNodes = attrsToTranslate.reduce(function (prev, attr) {
- return prev.concat(nodes.map.call($elem.find('[' + attr + '*="[["]'), function (el) {
- return [attr, el];
- }));
- }, []);
- attrText = attrNodes.map(function (node) {
- return node[1].getAttribute(node[0]);
- }).join(' || ');
-
- Promise.all([
- translator.translate(text),
- translator.translate(attrText),
- ]).then(function (ref) {
- var translated = ref[0];
- var translatedAttrs = ref[1];
- if (translated) {
- translated.split(' || ').forEach(function (html, i) {
- $(nodes[i]).replaceWith(html);
- });
- }
- if (translatedAttrs) {
- translatedAttrs.split(' || ').forEach(function (text, i) {
- attrNodes[i][1].setAttribute(attrNodes[i][0], text);
- });
- }
+ if (/\[\[.+\]\]/.test(element.outerHTML)) {
+ translator.translateInPlace(element, attrsToTranslate).then(function () {
if (show) {
$elem.modal('show');
}
@@ -84,7 +37,7 @@ require(['translator'], function (shim) {
CANCEL: translations[1],
CONFIRM: translations[2],
});
-
+
bootbox.setLocale(lang);
});
});
From 5297c31b1c60d11c370bc544dceb26108951742a Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Tue, 23 May 2017 15:41:12 -0400
Subject: [PATCH 38/42] adding yarn.lock to ignore file
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index 469b990863..25ad8bffae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+yarn.lock
npm-debug.log
node_modules/
sftp-config.json
From 9625f896656d522a09c5a27208447dcc74ffb05f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?=
Date: Tue, 23 May 2017 15:53:29 -0400
Subject: [PATCH 39/42] remove for loop
---
src/controllers/authentication.js | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js
index cc4ef41aab..3db8b26472 100644
--- a/src/controllers/authentication.js
+++ b/src/controllers/authentication.js
@@ -26,13 +26,7 @@ authenticationController.register = function (req, res) {
return res.sendStatus(403);
}
- var userData = {};
-
- for (var key in req.body) {
- if (req.body.hasOwnProperty(key)) {
- userData[key] = req.body[key];
- }
- }
+ var userData = req.body;
async.waterfall([
function (next) {
From e4714a0c5832a1e59db461a4c512ddd543b120e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?=
Date: Tue, 23 May 2017 17:37:16 -0400
Subject: [PATCH 40/42] more auth tests
---
src/controllers/authentication.js | 25 +++++-----
test/authentication.js | 80 +++++++++++++++++++++++++++++++
2 files changed, 91 insertions(+), 14 deletions(-)
diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js
index 3db8b26472..64823e74b7 100644
--- a/src/controllers/authentication.js
+++ b/src/controllers/authentication.js
@@ -97,21 +97,18 @@ function registerAndLoginUser(req, res, userData, callback) {
plugins.fireHook('filter:register.interstitial', {
userData: userData,
interstitials: [],
- }, function (err, data) {
- if (err) {
- return next(err);
- }
+ }, next);
+ },
+ function (data, next) {
+ // If interstitials are found, save registration attempt into session and abort
+ var deferRegistration = data.interstitials.length;
- // If interstitials are found, save registration attempt into session and abort
- var deferRegistration = data.interstitials.length;
-
- if (!deferRegistration) {
- return next();
- }
- userData.register = true;
- req.session.registration = userData;
- return res.json({ referrer: nconf.get('relative_path') + '/register/complete' });
- });
+ if (!deferRegistration) {
+ return next();
+ }
+ userData.register = true;
+ req.session.registration = userData;
+ return res.json({ referrer: nconf.get('relative_path') + '/register/complete' });
},
function (next) {
user.create(userData, next);
diff --git a/test/authentication.js b/test/authentication.js
index 4ab15b500f..1d3651e183 100644
--- a/test/authentication.js
+++ b/test/authentication.js
@@ -37,6 +37,33 @@ describe('authentication', function () {
});
}
+ function registerUser(email, username, password, callback) {
+ var jar = request.jar();
+ request({
+ url: nconf.get('url') + '/api/config',
+ json: true,
+ jar: jar,
+ }, function (err, response, body) {
+ if (err) {
+ return callback(err);
+ }
+
+ request.post(nconf.get('url') + '/register', {
+ form: {
+ email: email,
+ username: username,
+ password: password,
+ },
+ json: true,
+ jar: jar,
+ headers: {
+ 'x-csrf-token': body.csrf_token,
+ },
+ }, function (err, response, body) {
+ callback(err, response, body, jar);
+ });
+ });
+ }
var jar = request.jar();
var regularUid;
@@ -218,6 +245,59 @@ describe('authentication', function () {
});
});
+ it('should fail to register if registraton is disabled', function (done) {
+ meta.config.registrationType = 'disabled';
+ registerUser('some@user.com', 'someuser', 'somepassword', function (err, response, body) {
+ assert.ifError(err);
+ assert.equal(response.statusCode, 403);
+ assert.equal(body, 'Forbidden');
+ done();
+ });
+ });
+
+ it('should return error if invitation is not valid', function (done) {
+ meta.config.registrationType = 'invite-only';
+ registerUser('some@user.com', 'someuser', 'somepassword', function (err, response, body) {
+ meta.config.registrationType = 'normal';
+ assert.ifError(err);
+ assert.equal(response.statusCode, 400);
+ assert.equal(body, '[[error:invalid-data]]');
+ done();
+ });
+ });
+
+ it('should fail to register if email is falsy', function (done) {
+ registerUser('', 'someuser', 'somepassword', function (err, response, body) {
+ assert.ifError(err);
+ assert.equal(response.statusCode, 400);
+ assert.equal(body, '[[error:invalid-email]]');
+ done();
+ });
+ });
+
+ it('should fail to register if username is falsy or too short', function (done) {
+ registerUser('some@user.com', '', 'somepassword', function (err, response, body) {
+ assert.ifError(err);
+ assert.equal(response.statusCode, 400);
+ assert.equal(body, '[[error:username-too-short]]');
+ registerUser('some@user.com', 'a', 'somepassword', function (err, response, body) {
+ assert.ifError(err);
+ assert.equal(response.statusCode, 400);
+ assert.equal(body, '[[error:username-too-short]]');
+ done();
+ });
+ });
+ });
+
+ it('should fail to register if username is too long', function (done) {
+ registerUser('some@user.com', 'thisisareallylongusername', '123456', function (err, response, body) {
+ assert.ifError(err);
+ assert.equal(response.statusCode, 400);
+ assert.equal(body, '[[error:username-too-long]]');
+ done();
+ });
+ });
+
after(function (done) {
db.emptydb(done);
});
From a8c649cca64c7baad12a3d007167e014ddf438dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?=
Date: Tue, 23 May 2017 17:41:40 -0400
Subject: [PATCH 41/42] check data.uid
---
src/controllers/authentication.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js
index 64823e74b7..c8de5c60f1 100644
--- a/src/controllers/authentication.js
+++ b/src/controllers/authentication.js
@@ -82,7 +82,7 @@ authenticationController.register = function (req, res) {
return res.status(400).send(err.message);
}
- if (req.body.userLang) {
+ if (data.uid && req.body.userLang) {
user.setSetting(data.uid, 'userLang', req.body.userLang);
}
From 2a9cdb7be24f889cc75693f42f4f025d9898860f Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Tue, 23 May 2017 14:00:37 -0600
Subject: [PATCH 42/42] Add `--threads=#` option for setting max threads
Make grunt NODE_ENV development by default
---
Gruntfile.js | 2 ++
src/meta/build.js | 5 +++++
src/meta/minifier.js | 16 ++++++++++++++--
3 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/Gruntfile.js b/Gruntfile.js
index c97795e743..9a84c15aa0 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -8,6 +8,8 @@ var initWorker;
var incomplete = [];
var running = 0;
+env.NODE_ENV = env.NODE_ENV || 'development';
+
module.exports = function (grunt) {
var args = [];
var initArgs = ['--build'];
diff --git a/src/meta/build.js b/src/meta/build.js
index cc3321923f..290957512a 100644
--- a/src/meta/build.js
+++ b/src/meta/build.js
@@ -180,6 +180,11 @@ function build(targets, callback) {
async.series([
beforeBuild,
function (next) {
+ var threads = parseInt(nconf.get('threads'), 10);
+ if (threads) {
+ require('./minifier').maxThreads = threads - 1;
+ }
+
var parallel = !nconf.get('series');
if (parallel) {
winston.info('[build] Building in parallel mode');
diff --git a/src/meta/minifier.js b/src/meta/minifier.js
index 7c1a83bea4..07f33d8253 100644
--- a/src/meta/minifier.js
+++ b/src/meta/minifier.js
@@ -41,9 +41,21 @@ function setupDebugging() {
var pool = [];
var free = [];
-Minifier.maxThreads = os.cpus().length - 1;
+var maxThreads = 0;
-winston.verbose('[minifier] utilizing a maximum of ' + Minifier.maxThreads + ' additional threads');
+Object.defineProperty(Minifier, 'maxThreads', {
+ get: function () {
+ return maxThreads;
+ },
+ set: function (val) {
+ maxThreads = val;
+ winston.verbose('[minifier] utilizing a maximum of ' + maxThreads + ' additional threads');
+ },
+ configurable: true,
+ enumerable: true,
+});
+
+Minifier.maxThreads = os.cpus().length - 1;
Minifier.killAll = function () {
pool.forEach(function (child) {