package/package.json000644 000765 000024 0000001350 12056220612013010 0ustar00000000 000000 { "name": "reqwest" , "description": "A wrapper for asynchronous http requests" , "keywords": ["ender", "ajax", "xhr", "connection", "web 2.0", "async"] , "version": "0.6.0" , "homepage": "https://github.com/ded/reqwest" , "author": "Dustin Diaz (http://dustindiaz.com)" , "repository": { "type": "git" , "url": "https://github.com/ded/reqwest.git" } , "main": "./reqwest.js" , "ender": "./src/ender.js" , "devDependencies": { "connect": "1.8.x" , "mime": "1.x.x" , "sink-test": ">=0.1.2" , "dispatch": "0.x.x" , "valentine": ">=1.4.7" , "smoosh": "0.4.0" } , "scripts": { "boosh": "smoosh make ./build.json" , "test": "node make/tests.js" } } package/.npmignore000644 000765 000024 0000000014 11564540215012524 0ustar00000000 000000 node_modulespackage/README.md000644 000765 000024 0000013433 12056220403012004 0ustar00000000 000000 # It's AJAX All over again. Includes support for xmlHttpRequest, JSONP, CORS, and CommonJS Promises A. The happs --------- ``` sh $ git clone git://github.com/ded/reqwest.git reqwest $ cd !$ $ npm install $ make ``` API --------- ``` js reqwest('path/to/html', function (resp) { qwery('#content').html(resp) }) ``` ``` js reqwest({ url: 'path/to/html' , method: 'post' , data: { foo: 'bar', baz: 100 } , success: function (resp) { qwery('#content').html(resp) } }) ``` ``` js reqwest({ url: 'path/to/html' , method: 'get' , data: { [ name: 'foo', value: 'bar' ], [ name: 'baz', value: 100 ] } , success: function (resp) { qwery('#content').html(resp) } }) ``` ``` js reqwest({ url: 'path/to/json' , type: 'json' , method: 'post' , error: function (err) { } , success: function (resp) { qwery('#content').html(resp.content) } }) ``` ``` js reqwest({ url: 'path/to/json' , type: 'json' , method: 'post' , contentType: 'application/json' , headers: { 'X-My-Custom-Header': 'SomethingImportant' } , error: function (err) { } , success: function (resp) { qwery('#content').html(resp.content) } }) ``` ``` js // Uses XMLHttpRequest2 credentialled requests (cookies, HTTP basic auth) if supported reqwest({ url: 'path/to/json' , type: 'json' , method: 'post' , contentType: 'application/json' , crossOrigin: true , withCredentials: true , error: function (err) { } , success: function (resp) { qwery('#content').html(resp.content) } }) ``` ``` js reqwest({ url: 'path/to/data.jsonp?callback=?' , type: 'jsonp' , success: function (resp) { qwery('#content').html(resp.content) } }) ``` ``` js reqwest({ url: 'path/to/data.jsonp?foo=bar' , type: 'jsonp' , jsonpCallback: 'foo' , jsonpCallbackName: 'bar' , success: function (resp) { qwery('#content').html(resp.content) } }) ``` ``` js reqwest({ url: 'path/to/data.jsonp?foo=bar' , type: 'jsonp' , jsonpCallback: 'foo' , success: function (resp) { qwery('#content').html(resp.content) } , complete: function (resp) { qwery('#hide-this').hide() } }) ``` ## Promises ``` js reqwest({ url: 'path/to/data.jsonp?foo=bar' , type: 'jsonp' , jsonpCallback: 'foo' }) .then(function (resp) { qwery('#content').html(resp.content) }, function (err, msg) { qwery('#errors').html(msg) }) .always(function (resp) { qwery('#hide-this').hide() }) ``` ``` js reqwest({ url: 'path/to/data.jsonp?foo=bar' , type: 'jsonp' , jsonpCallback: 'foo' }) .then(function (resp) { qwery('#content').html(resp.content) }) .fail(function (err, msg) { qwery('#errors').html(msg) }) .always(function (resp) { qwery('#hide-this').hide() }) ``` ``` js var r = reqwest({ url: 'path/to/data.jsonp?foo=bar' , type: 'jsonp' , jsonpCallback: 'foo' , success: function () { setTimeout(function () { r .then(function (resp) { qwery('#content').html(resp.content) }, function (err) { }) .always(function (resp) { qwery('#hide-this').hide() }) }, 15) } }) ``` The Tests --------- $ npm test Browser support --------------- * IE6+ * Chrome 1+ * Safari 3+ * Firefox 1+ * Opera Ender Support ------------- Reqwest can be used as an [Ender](http://ender.no.de) module. Add it to your existing build as such: $ ender add reqwest Use it as such: ``` js $.ajax({ ... }) ``` Serialize things: ``` js $(form).serialize() // returns query string -> x=y&... $(form).serialize({type:'array'}) // returns array name/value pairs -> [ { name: x, value: y}, ... ] $(form).serialize({type:'map'}) // returns an object representation -> { x: y, ... } $(form).serializeArray() $.toQueryString({ foo: 'bar' , baz: 'thunk' }) // returns query string -> foo=bar&baz=thunk ``` Or, get a bit fancy: ``` js $('#myform input[name=myradios]').serialize({type:'map'})['myradios'] // get the selected value $('input[type=text],#specialthing').serialize() // turn any arbitrary set of form elements into a query string ``` RequireJs and Jam ------------------ Reqwest can also be used with RequireJs and can be installed via jam ``` jam install reqwest ``` ```js define(function(require){ var reqwest = require('reqwest'); }); ``` jQuery and Zepto Compatibility ------------------------------ There are some differences between the *Reqwest way* and the *jQuery/Zepto way*. ### method ### jQuery/Zepto use `type` to specify the request method while Reqwest uses `method` and reserves `type` for the response data type. ### dataType ### When using jQuery/Zepto you use the `dataType` option to specify the type of data to expect from the server, Reqwest uses `type`. jQuery also can also take a space-separated list of data types to specify the request, response and response-conversion types but Reqwest uses the `type` parameter to infer the response type and leaves conversion up to you. ### JSONP ### Reqwest also takes optional `jsonpCallback` and `jsonpCallbackName` options to specify the callback query-string key and the callback function name respectively while jQuery uses `jsonp` and `jsonpCallback` for these same options. But fear not! If you must work the jQuery/Zepto way then Reqwest has a wrapper that will remap these options for you: ```js reqwest.compat({ url: 'path/to/data.jsonp?foo=bar' , dataType: 'jsonp' , jsonp: 'foo' , jsonpCallback: 'bar' , success: function (resp) { qwery('#content').html(resp.content) } }) // or from Ender: $.ajax.compat({ ... }) ``` If you want to install jQuery/Zepto compatibility mode as the default then simply place this snippet at the top of your code: ```js $.ajax.compat && $.ender({ ajax: $.ajax.compat }); ``` **Happy Ajaxing!** package/reqwest.js000644 000765 000024 0000033031 12056220504012553 0ustar00000000 000000 /*! * Reqwest! A general purpose XHR connection manager * (c) Dustin Diaz 2012 * https://github.com/ded/reqwest * license MIT */ !function (name, definition) { if (typeof module != 'undefined') module.exports = definition() else if (typeof define == 'function' && define.amd) define(definition) else this[name] = definition() }('reqwest', function () { var win = window , doc = document , twoHundo = /^20\d$/ , byTag = 'getElementsByTagName' , readyState = 'readyState' , contentType = 'Content-Type' , requestedWith = 'X-Requested-With' , head = doc[byTag]('head')[0] , uniqid = 0 , callbackPrefix = 'reqwest_' + (+new Date()) , lastValue // data stored by the most recent JSONP callback , xmlHttpRequest = 'XMLHttpRequest' var isArray = typeof Array.isArray == 'function' ? Array.isArray : function (a) { return a instanceof Array } var defaultHeaders = { contentType: 'application/x-www-form-urlencoded' , requestedWith: xmlHttpRequest , accept: { '*': 'text/javascript, text/html, application/xml, text/xml, */*' , xml: 'application/xml, text/xml' , html: 'text/html' , text: 'text/plain' , json: 'application/json, text/javascript' , js: 'application/javascript, text/javascript' } } var xhr = win[xmlHttpRequest] ? function () { return new XMLHttpRequest() } : function () { return new ActiveXObject('Microsoft.XMLHTTP') } function handleReadyState(o, success, error) { return function () { if (o && o[readyState] == 4) { if (twoHundo.test(o.status)) { success(o) } else { error(o) } } } } function setHeaders(http, o) { var headers = o.headers || {}, h headers.Accept = headers.Accept || defaultHeaders.accept[o.type] || defaultHeaders.accept['*'] // breaks cross-origin requests with legacy browsers if (!o.crossOrigin && !headers[requestedWith]) headers[requestedWith] = defaultHeaders.requestedWith if (!headers[contentType]) headers[contentType] = o.contentType || defaultHeaders.contentType for (h in headers) { headers.hasOwnProperty(h) && http.setRequestHeader(h, headers[h]) } } function setCredentials(http, o) { if (typeof o.withCredentials !== "undefined" && typeof http.withCredentials !== "undefined") { http.withCredentials = !!o.withCredentials } } function generalCallback(data) { lastValue = data } function urlappend(url, s) { return url + (/\?/.test(url) ? '&' : '?') + s } function handleJsonp(o, fn, err, url) { var reqId = uniqid++ , cbkey = o.jsonpCallback || 'callback' // the 'callback' key , cbval = o.jsonpCallbackName || reqwest.getcallbackPrefix(reqId) // , cbval = o.jsonpCallbackName || ('reqwest_' + reqId) // the 'callback' value , cbreg = new RegExp('((^|\\?|&)' + cbkey + ')=([^&]+)') , match = url.match(cbreg) , script = doc.createElement('script') , loaded = 0 if (match) { if (match[3] === '?') { url = url.replace(cbreg, '$1=' + cbval) // wildcard callback func name } else { cbval = match[3] // provided callback func name } } else { url = urlappend(url, cbkey + '=' + cbval) // no callback details, add 'em } win[cbval] = generalCallback script.type = 'text/javascript' script.src = url script.async = true if (typeof script.onreadystatechange !== 'undefined') { // need this for IE due to out-of-order onreadystatechange(), binding script // execution to an event listener gives us control over when the script // is executed. See http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html script.event = 'onclick' script.htmlFor = script.id = '_reqwest_' + reqId } script.onload = script.onreadystatechange = function () { if ((script[readyState] && script[readyState] !== 'complete' && script[readyState] !== 'loaded') || loaded) { return false } script.onload = script.onreadystatechange = null script.onclick && script.onclick() // Call the user callback with the last value stored and clean up values and scripts. o.success && o.success(lastValue) lastValue = undefined head.removeChild(script) loaded = 1 } // Add the script to the DOM head head.appendChild(script) } function getRequest(o, fn, err) { var method = (o.method || 'GET').toUpperCase() , url = typeof o === 'string' ? o : o.url // convert non-string objects to query-string form unless o.processData is false , data = (o.processData !== false && o.data && typeof o.data !== 'string') ? reqwest.toQueryString(o.data) : (o.data || null) , http // if we're working on a GET request and we have data then we should append // query string to end of URL and not post data if ((o.type == 'jsonp' || method == 'GET') && data) { url = urlappend(url, data) data = null } if (o.type == 'jsonp') return handleJsonp(o, fn, err, url) http = xhr() http.open(method, url, true) setHeaders(http, o) setCredentials(http, o) http.onreadystatechange = handleReadyState(http, fn, err) o.before && o.before(http) http.send(data) return http } function Reqwest(o, fn) { this.o = o this.fn = fn init.apply(this, arguments) } function setType(url) { var m = url.match(/\.(json|jsonp|html|xml)(\?|$)/) return m ? m[1] : 'js' } function init(o, fn) { this.url = typeof o == 'string' ? o : o.url this.timeout = null // whether request has been fulfilled for purpose // of tracking the Promises this._fulfilled = false // success handlers this._fulfillmentHandlers = [] // error handlers this._errorHandlers = [] // complete (both success and fail) handlers this._completeHandlers = [] this._erred = false this._responseArgs = {} var self = this , type = o.type || setType(this.url) fn = fn || function () {} if (o.timeout) { this.timeout = setTimeout(function () { self.abort() }, o.timeout) } if (o.success) { this._fulfillmentHandlers.push(function () { o.success.apply(o, arguments) }) } if (o.error) { this._errorHandlers.push(function () { o.error.apply(o, arguments) }) } if (o.complete) { this._completeHandlers.push(function () { o.complete.apply(o, arguments) }) } function complete(resp) { o.timeout && clearTimeout(self.timeout) self.timeout = null while (self._completeHandlers.length > 0) { self._completeHandlers.shift()(resp) } } function success(resp) { var r = resp.responseText if (r) { switch (type) { case 'json': try { resp = win.JSON ? win.JSON.parse(r) : eval('(' + r + ')') } catch (err) { return error(resp, 'Could not parse JSON in response', err) } break; case 'js': resp = eval(r) break; case 'html': resp = r break; case 'xml': resp = resp.responseXML; break; } } self._responseArgs.resp = resp self._fulfilled = true fn(resp) while (self._fulfillmentHandlers.length > 0) { self._fulfillmentHandlers.shift()(resp) } complete(resp) } function error(resp, msg, t) { self._responseArgs.resp = resp self._responseArgs.msg = msg self._responseArgs.t = t self._erred = true while (self._errorHandlers.length > 0) { self._errorHandlers.shift()(resp, msg, t) } complete(resp) } this.request = getRequest(o, success, error) } Reqwest.prototype = { abort: function () { this.request.abort() } , retry: function () { init.call(this, this.o, this.fn) } /** * Small deviation from the Promises A CommonJs specification * http://wiki.commonjs.org/wiki/Promises/A */ /** * `then` will execute upon successful requests */ , then: function (success, fail) { if (this._fulfilled) { success(this._responseArgs.resp) } else if (this._erred) { fail(this._responseArgs.resp, this._responseArgs.msg, this._responseArgs.t) } else { this._fulfillmentHandlers.push(success) this._errorHandlers.push(fail) } return this } /** * `always` will execute whether the request succeeds or fails */ , always: function (fn) { if (this._fulfilled || this._erred) { fn(this._responseArgs.resp) } else { this._completeHandlers.push(fn) } return this } /** * `fail` will execute when the request fails */ , fail: function (fn) { if (this._erred) { fn(this._responseArgs.resp, this._responseArgs.msg, this._responseArgs.t) } else { this._errorHandlers.push(fn) } return this } } function reqwest(o, fn) { return new Reqwest(o, fn) } // normalize newline variants according to spec -> CRLF function normalize(s) { return s ? s.replace(/\r?\n/g, '\r\n') : '' } function serial(el, cb) { var n = el.name , t = el.tagName.toLowerCase() , optCb = function (o) { // IE gives value="" even where there is no value attribute // 'specified' ref: http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-862529273 if (o && !o.disabled) cb(n, normalize(o.attributes.value && o.attributes.value.specified ? o.value : o.text)) } // don't serialize elements that are disabled or without a name if (el.disabled || !n) return; switch (t) { case 'input': if (!/reset|button|image|file/i.test(el.type)) { var ch = /checkbox/i.test(el.type) , ra = /radio/i.test(el.type) , val = el.value; // WebKit gives us "" instead of "on" if a checkbox has no value, so correct it here (!(ch || ra) || el.checked) && cb(n, normalize(ch && val === '' ? 'on' : val)) } break; case 'textarea': cb(n, normalize(el.value)) break; case 'select': if (el.type.toLowerCase() === 'select-one') { optCb(el.selectedIndex >= 0 ? el.options[el.selectedIndex] : null) } else { for (var i = 0; el.length && i < el.length; i++) { el.options[i].selected && optCb(el.options[i]) } } break; } } // collect up all form elements found from the passed argument elements all // the way down to child elements; pass a '
' or form fields. // called with 'this'=callback to use for serial() on each element function eachFormElement() { var cb = this , e, i, j , serializeSubtags = function (e, tags) { for (var i = 0; i < tags.length; i++) { var fa = e[byTag](tags[i]) for (j = 0; j < fa.length; j++) serial(fa[j], cb) } } for (i = 0; i < arguments.length; i++) { e = arguments[i] if (/input|select|textarea/i.test(e.tagName)) serial(e, cb) serializeSubtags(e, [ 'input', 'select', 'textarea' ]) } } // standard query string style serialization function serializeQueryString() { return reqwest.toQueryString(reqwest.serializeArray.apply(null, arguments)) } // { 'name': 'value', ... } style serialization function serializeHash() { var hash = {} eachFormElement.apply(function (name, value) { if (name in hash) { hash[name] && !isArray(hash[name]) && (hash[name] = [hash[name]]) hash[name].push(value) } else hash[name] = value }, arguments) return hash } // [ { name: 'name', value: 'value' }, ... ] style serialization reqwest.serializeArray = function () { var arr = [] eachFormElement.apply(function (name, value) { arr.push({name: name, value: value}) }, arguments) return arr } reqwest.serialize = function () { if (arguments.length === 0) return '' var opt, fn , args = Array.prototype.slice.call(arguments, 0) opt = args.pop() opt && opt.nodeType && args.push(opt) && (opt = null) opt && (opt = opt.type) if (opt == 'map') fn = serializeHash else if (opt == 'array') fn = reqwest.serializeArray else fn = serializeQueryString return fn.apply(null, args) } reqwest.toQueryString = function (o) { var qs = '', i , enc = encodeURIComponent , push = function (k, v) { qs += enc(k) + '=' + enc(v) + '&' } if (isArray(o)) { for (i = 0; o && i < o.length; i++) push(o[i].name, o[i].value) } else { for (var k in o) { if (!Object.hasOwnProperty.call(o, k)) continue; var v = o[k] if (isArray(v)) { for (i = 0; i < v.length; i++) push(k, v[i]) } else push(k, o[k]) } } // spaces should be + according to spec return qs.replace(/&$/, '').replace(/%20/g, '+') } reqwest.getcallbackPrefix = function (reqId) { return callbackPrefix } // jQuery and Zepto compatibility, differences can be remapped here so you can call // .ajax.compat(options, callback) reqwest.compat = function (o, fn) { if (o) { o.type && (o.method = o.type) && delete o.type o.dataType && (o.type = o.dataType) o.jsonpCallback && (o.jsonpCallbackName = o.jsonpCallback) && delete o.jsonpCallback o.jsonp && (o.jsonpCallback = o.jsonp) } return new Reqwest(o, fn) } return reqwest }); package/reqwest.min.js000644 000765 000024 0000016433 12056220504013344 0ustar00000000 000000 /*! * Reqwest! A general purpose XHR connection manager * (c) Dustin Diaz 2012 * https://github.com/ded/reqwest * license MIT */ !function(e,t){typeof module!="undefined"?module.exports=t():typeof define=="function"&&define.amd?define(t):this[e]=t()}("reqwest",function(){function handleReadyState(e,t,n){return function(){e&&e[readyState]==4&&(twoHundo.test(e.status)?t(e):n(e))}}function setHeaders(e,t){var n=t.headers||{},r;n.Accept=n.Accept||defaultHeaders.accept[t.type]||defaultHeaders.accept["*"],!t.crossOrigin&&!n[requestedWith]&&(n[requestedWith]=defaultHeaders.requestedWith),n[contentType]||(n[contentType]=t.contentType||defaultHeaders.contentType);for(r in n)n.hasOwnProperty(r)&&e.setRequestHeader(r,n[r])}function setCredentials(e,t){typeof t.withCredentials!="undefined"&&typeof e.withCredentials!="undefined"&&(e.withCredentials=!!t.withCredentials)}function generalCallback(e){lastValue=e}function urlappend(e,t){return e+(/\?/.test(e)?"&":"?")+t}function handleJsonp(e,t,n,r){var i=uniqid++,s=e.jsonpCallback||"callback",o=e.jsonpCallbackName||reqwest.getcallbackPrefix(i),u=new RegExp("((^|\\?|&)"+s+")=([^&]+)"),a=r.match(u),f=doc.createElement("script"),l=0;a?a[3]==="?"?r=r.replace(u,"$1="+o):o=a[3]:r=urlappend(r,s+"="+o),win[o]=generalCallback,f.type="text/javascript",f.src=r,f.async=!0,typeof f.onreadystatechange!="undefined"&&(f.event="onclick",f.htmlFor=f.id="_reqwest_"+i),f.onload=f.onreadystatechange=function(){if(f[readyState]&&f[readyState]!=="complete"&&f[readyState]!=="loaded"||l)return!1;f.onload=f.onreadystatechange=null,f.onclick&&f.onclick(),e.success&&e.success(lastValue),lastValue=undefined,head.removeChild(f),l=1},head.appendChild(f)}function getRequest(e,t,n){var r=(e.method||"GET").toUpperCase(),i=typeof e=="string"?e:e.url,s=e.processData!==!1&&e.data&&typeof e.data!="string"?reqwest.toQueryString(e.data):e.data||null,o;return(e.type=="jsonp"||r=="GET")&&s&&(i=urlappend(i,s),s=null),e.type=="jsonp"?handleJsonp(e,t,n,i):(o=xhr(),o.open(r,i,!0),setHeaders(o,e),setCredentials(o,e),o.onreadystatechange=handleReadyState(o,t,n),e.before&&e.before(o),o.send(s),o)}function Reqwest(e,t){this.o=e,this.fn=t,init.apply(this,arguments)}function setType(e){var t=e.match(/\.(json|jsonp|html|xml)(\?|$)/);return t?t[1]:"js"}function init(o,fn){function complete(e){o.timeout&&clearTimeout(self.timeout),self.timeout=null;while(self._completeHandlers.length>0)self._completeHandlers.shift()(e)}function success(resp){var r=resp.responseText;if(r)switch(type){case"json":try{resp=win.JSON?win.JSON.parse(r):eval("("+r+")")}catch(err){return error(resp,"Could not parse JSON in response",err)}break;case"js":resp=eval(r);break;case"html":resp=r;break;case"xml":resp=resp.responseXML}self._responseArgs.resp=resp,self._fulfilled=!0,fn(resp);while(self._fulfillmentHandlers.length>0)self._fulfillmentHandlers.shift()(resp);complete(resp)}function error(e,t,n){self._responseArgs.resp=e,self._responseArgs.msg=t,self._responseArgs.t=n,self._erred=!0;while(self._errorHandlers.length>0)self._errorHandlers.shift()(e,t,n);complete(e)}this.url=typeof o=="string"?o:o.url,this.timeout=null,this._fulfilled=!1,this._fulfillmentHandlers=[],this._errorHandlers=[],this._completeHandlers=[],this._erred=!1,this._responseArgs={};var self=this,type=o.type||setType(this.url);fn=fn||function(){},o.timeout&&(this.timeout=setTimeout(function(){self.abort()},o.timeout)),o.success&&this._fulfillmentHandlers.push(function(){o.success.apply(o,arguments)}),o.error&&this._errorHandlers.push(function(){o.error.apply(o,arguments)}),o.complete&&this._completeHandlers.push(function(){o.complete.apply(o,arguments)}),this.request=getRequest(o,success,error)}function reqwest(e,t){return new Reqwest(e,t)}function normalize(e){return e?e.replace(/\r?\n/g,"\r\n"):""}function serial(e,t){var n=e.name,r=e.tagName.toLowerCase(),i=function(e){e&&!e.disabled&&t(n,normalize(e.attributes.value&&e.attributes.value.specified?e.value:e.text))};if(e.disabled||!n)return;switch(r){case"input":if(!/reset|button|image|file/i.test(e.type)){var s=/checkbox/i.test(e.type),o=/radio/i.test(e.type),u=e.value;(!s&&!o||e.checked)&&t(n,normalize(s&&u===""?"on":u))}break;case"textarea":t(n,normalize(e.value));break;case"select":if(e.type.toLowerCase()==="select-one")i(e.selectedIndex>=0?e.options[e.selectedIndex]:null);else for(var a=0;e.length&&a -1) ? cookies.split('=')[1] : '') res.writeHead(200, { 'Access-Control-Allow-Origin': 'http://localhost:1234', 'Access-Control-Allow-Credentials': 'true', 'Content-Type': 'text/plain' }) res.end(value) } } Connect.createServer(Connect.query(), dispatch(otherOriginRoutes)).listen(5678) exec('open http://localhost:1234', function () { console.log('opening tests at http://localhost:1234') }) package/Makefile000644 000765 000024 0000000133 12054733313012165 0ustar00000000 000000 .PHONY: boosh test boosh: node_modules/smoosh/bin/smoosh make build.json test: npm testpackage/src/copyright.js000644 000765 000024 0000000212 12054732551013663 0ustar00000000 000000 /*! * Reqwest! A general purpose XHR connection manager * (c) Dustin Diaz 2012 * https://github.com/ded/reqwest * license MIT */package/src/ender.js000644 000765 000024 0000001143 12054732476012762 0ustar00000000 000000 !function ($) { var r = require('reqwest') , integrate = function(method) { return function() { var args = Array.prototype.slice.call(arguments, 0) , i = (this && this.length) || 0 while (i--) args.unshift(this[i]) return r[method].apply(null, args) } } , s = integrate('serialize') , sa = integrate('serializeArray') $.ender({ ajax: r , serialize: r.serialize , serializeArray: r.serializeArray , toQueryString: r.toQueryString }) $.ender({ serialize: s , serializeArray: sa }, true) }(ender); package/src/reqwest.js000644 000765 000024 0000032616 12056217377013370 0ustar00000000 000000 !function (name, definition) { if (typeof module != 'undefined') module.exports = definition() else if (typeof define == 'function' && define.amd) define(definition) else this[name] = definition() }('reqwest', function () { var win = window , doc = document , twoHundo = /^20\d$/ , byTag = 'getElementsByTagName' , readyState = 'readyState' , contentType = 'Content-Type' , requestedWith = 'X-Requested-With' , head = doc[byTag]('head')[0] , uniqid = 0 , callbackPrefix = 'reqwest_' + (+new Date()) , lastValue // data stored by the most recent JSONP callback , xmlHttpRequest = 'XMLHttpRequest' var isArray = typeof Array.isArray == 'function' ? Array.isArray : function (a) { return a instanceof Array } var defaultHeaders = { contentType: 'application/x-www-form-urlencoded' , requestedWith: xmlHttpRequest , accept: { '*': 'text/javascript, text/html, application/xml, text/xml, */*' , xml: 'application/xml, text/xml' , html: 'text/html' , text: 'text/plain' , json: 'application/json, text/javascript' , js: 'application/javascript, text/javascript' } } var xhr = win[xmlHttpRequest] ? function () { return new XMLHttpRequest() } : function () { return new ActiveXObject('Microsoft.XMLHTTP') } function handleReadyState(o, success, error) { return function () { if (o && o[readyState] == 4) { if (twoHundo.test(o.status)) { success(o) } else { error(o) } } } } function setHeaders(http, o) { var headers = o.headers || {}, h headers.Accept = headers.Accept || defaultHeaders.accept[o.type] || defaultHeaders.accept['*'] // breaks cross-origin requests with legacy browsers if (!o.crossOrigin && !headers[requestedWith]) headers[requestedWith] = defaultHeaders.requestedWith if (!headers[contentType]) headers[contentType] = o.contentType || defaultHeaders.contentType for (h in headers) { headers.hasOwnProperty(h) && http.setRequestHeader(h, headers[h]) } } function setCredentials(http, o) { if (typeof o.withCredentials !== "undefined" && typeof http.withCredentials !== "undefined") { http.withCredentials = !!o.withCredentials } } function generalCallback(data) { lastValue = data } function urlappend(url, s) { return url + (/\?/.test(url) ? '&' : '?') + s } function handleJsonp(o, fn, err, url) { var reqId = uniqid++ , cbkey = o.jsonpCallback || 'callback' // the 'callback' key , cbval = o.jsonpCallbackName || reqwest.getcallbackPrefix(reqId) // , cbval = o.jsonpCallbackName || ('reqwest_' + reqId) // the 'callback' value , cbreg = new RegExp('((^|\\?|&)' + cbkey + ')=([^&]+)') , match = url.match(cbreg) , script = doc.createElement('script') , loaded = 0 if (match) { if (match[3] === '?') { url = url.replace(cbreg, '$1=' + cbval) // wildcard callback func name } else { cbval = match[3] // provided callback func name } } else { url = urlappend(url, cbkey + '=' + cbval) // no callback details, add 'em } win[cbval] = generalCallback script.type = 'text/javascript' script.src = url script.async = true if (typeof script.onreadystatechange !== 'undefined') { // need this for IE due to out-of-order onreadystatechange(), binding script // execution to an event listener gives us control over when the script // is executed. See http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html script.event = 'onclick' script.htmlFor = script.id = '_reqwest_' + reqId } script.onload = script.onreadystatechange = function () { if ((script[readyState] && script[readyState] !== 'complete' && script[readyState] !== 'loaded') || loaded) { return false } script.onload = script.onreadystatechange = null script.onclick && script.onclick() // Call the user callback with the last value stored and clean up values and scripts. o.success && o.success(lastValue) lastValue = undefined head.removeChild(script) loaded = 1 } // Add the script to the DOM head head.appendChild(script) } function getRequest(o, fn, err) { var method = (o.method || 'GET').toUpperCase() , url = typeof o === 'string' ? o : o.url // convert non-string objects to query-string form unless o.processData is false , data = (o.processData !== false && o.data && typeof o.data !== 'string') ? reqwest.toQueryString(o.data) : (o.data || null) , http // if we're working on a GET request and we have data then we should append // query string to end of URL and not post data if ((o.type == 'jsonp' || method == 'GET') && data) { url = urlappend(url, data) data = null } if (o.type == 'jsonp') return handleJsonp(o, fn, err, url) http = xhr() http.open(method, url, true) setHeaders(http, o) setCredentials(http, o) http.onreadystatechange = handleReadyState(http, fn, err) o.before && o.before(http) http.send(data) return http } function Reqwest(o, fn) { this.o = o this.fn = fn init.apply(this, arguments) } function setType(url) { var m = url.match(/\.(json|jsonp|html|xml)(\?|$)/) return m ? m[1] : 'js' } function init(o, fn) { this.url = typeof o == 'string' ? o : o.url this.timeout = null // whether request has been fulfilled for purpose // of tracking the Promises this._fulfilled = false // success handlers this._fulfillmentHandlers = [] // error handlers this._errorHandlers = [] // complete (both success and fail) handlers this._completeHandlers = [] this._erred = false this._responseArgs = {} var self = this , type = o.type || setType(this.url) fn = fn || function () {} if (o.timeout) { this.timeout = setTimeout(function () { self.abort() }, o.timeout) } if (o.success) { this._fulfillmentHandlers.push(function () { o.success.apply(o, arguments) }) } if (o.error) { this._errorHandlers.push(function () { o.error.apply(o, arguments) }) } if (o.complete) { this._completeHandlers.push(function () { o.complete.apply(o, arguments) }) } function complete(resp) { o.timeout && clearTimeout(self.timeout) self.timeout = null while (self._completeHandlers.length > 0) { self._completeHandlers.shift()(resp) } } function success(resp) { var r = resp.responseText if (r) { switch (type) { case 'json': try { resp = win.JSON ? win.JSON.parse(r) : eval('(' + r + ')') } catch (err) { return error(resp, 'Could not parse JSON in response', err) } break; case 'js': resp = eval(r) break; case 'html': resp = r break; case 'xml': resp = resp.responseXML; break; } } self._responseArgs.resp = resp self._fulfilled = true fn(resp) while (self._fulfillmentHandlers.length > 0) { self._fulfillmentHandlers.shift()(resp) } complete(resp) } function error(resp, msg, t) { self._responseArgs.resp = resp self._responseArgs.msg = msg self._responseArgs.t = t self._erred = true while (self._errorHandlers.length > 0) { self._errorHandlers.shift()(resp, msg, t) } complete(resp) } this.request = getRequest(o, success, error) } Reqwest.prototype = { abort: function () { this.request.abort() } , retry: function () { init.call(this, this.o, this.fn) } /** * Small deviation from the Promises A CommonJs specification * http://wiki.commonjs.org/wiki/Promises/A */ /** * `then` will execute upon successful requests */ , then: function (success, fail) { if (this._fulfilled) { success(this._responseArgs.resp) } else if (this._erred) { fail(this._responseArgs.resp, this._responseArgs.msg, this._responseArgs.t) } else { this._fulfillmentHandlers.push(success) this._errorHandlers.push(fail) } return this } /** * `always` will execute whether the request succeeds or fails */ , always: function (fn) { if (this._fulfilled || this._erred) { fn(this._responseArgs.resp) } else { this._completeHandlers.push(fn) } return this } /** * `fail` will execute when the request fails */ , fail: function (fn) { if (this._erred) { fn(this._responseArgs.resp, this._responseArgs.msg, this._responseArgs.t) } else { this._errorHandlers.push(fn) } return this } } function reqwest(o, fn) { return new Reqwest(o, fn) } // normalize newline variants according to spec -> CRLF function normalize(s) { return s ? s.replace(/\r?\n/g, '\r\n') : '' } function serial(el, cb) { var n = el.name , t = el.tagName.toLowerCase() , optCb = function (o) { // IE gives value="" even where there is no value attribute // 'specified' ref: http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-862529273 if (o && !o.disabled) cb(n, normalize(o.attributes.value && o.attributes.value.specified ? o.value : o.text)) } // don't serialize elements that are disabled or without a name if (el.disabled || !n) return; switch (t) { case 'input': if (!/reset|button|image|file/i.test(el.type)) { var ch = /checkbox/i.test(el.type) , ra = /radio/i.test(el.type) , val = el.value; // WebKit gives us "" instead of "on" if a checkbox has no value, so correct it here (!(ch || ra) || el.checked) && cb(n, normalize(ch && val === '' ? 'on' : val)) } break; case 'textarea': cb(n, normalize(el.value)) break; case 'select': if (el.type.toLowerCase() === 'select-one') { optCb(el.selectedIndex >= 0 ? el.options[el.selectedIndex] : null) } else { for (var i = 0; el.length && i < el.length; i++) { el.options[i].selected && optCb(el.options[i]) } } break; } } // collect up all form elements found from the passed argument elements all // the way down to child elements; pass a '' or form fields. // called with 'this'=callback to use for serial() on each element function eachFormElement() { var cb = this , e, i, j , serializeSubtags = function (e, tags) { for (var i = 0; i < tags.length; i++) { var fa = e[byTag](tags[i]) for (j = 0; j < fa.length; j++) serial(fa[j], cb) } } for (i = 0; i < arguments.length; i++) { e = arguments[i] if (/input|select|textarea/i.test(e.tagName)) serial(e, cb) serializeSubtags(e, [ 'input', 'select', 'textarea' ]) } } // standard query string style serialization function serializeQueryString() { return reqwest.toQueryString(reqwest.serializeArray.apply(null, arguments)) } // { 'name': 'value', ... } style serialization function serializeHash() { var hash = {} eachFormElement.apply(function (name, value) { if (name in hash) { hash[name] && !isArray(hash[name]) && (hash[name] = [hash[name]]) hash[name].push(value) } else hash[name] = value }, arguments) return hash } // [ { name: 'name', value: 'value' }, ... ] style serialization reqwest.serializeArray = function () { var arr = [] eachFormElement.apply(function (name, value) { arr.push({name: name, value: value}) }, arguments) return arr } reqwest.serialize = function () { if (arguments.length === 0) return '' var opt, fn , args = Array.prototype.slice.call(arguments, 0) opt = args.pop() opt && opt.nodeType && args.push(opt) && (opt = null) opt && (opt = opt.type) if (opt == 'map') fn = serializeHash else if (opt == 'array') fn = reqwest.serializeArray else fn = serializeQueryString return fn.apply(null, args) } reqwest.toQueryString = function (o) { var qs = '', i , enc = encodeURIComponent , push = function (k, v) { qs += enc(k) + '=' + enc(v) + '&' } if (isArray(o)) { for (i = 0; o && i < o.length; i++) push(o[i].name, o[i].value) } else { for (var k in o) { if (!Object.hasOwnProperty.call(o, k)) continue; var v = o[k] if (isArray(v)) { for (i = 0; i < v.length; i++) push(k, v[i]) } else push(k, o[k]) } } // spaces should be + according to spec return qs.replace(/&$/, '').replace(/%20/g, '+') } reqwest.getcallbackPrefix = function (reqId) { return callbackPrefix } // jQuery and Zepto compatibility, differences can be remapped here so you can call // .ajax.compat(options, callback) reqwest.compat = function (o, fn) { if (o) { o.type && (o.method = o.type) && delete o.type o.dataType && (o.type = o.dataType) o.jsonpCallback && (o.jsonpCallbackName = o.jsonpCallback) && delete o.jsonpCallback o.jsonp && (o.jsonpCallback = o.jsonp) } return new Reqwest(o, fn) } return reqwest }); package/tests/ender.js000644 000765 000024 0000006263 12054732476013345 0ustar00000000 000000 /*! * Ender: open module JavaScript framework (client-lib) * copyright Dustin Diaz & Jacob Thornton 2011-2012 (@ded @fat) * http://ender.no.de * License MIT */ (function (context) { // a global object for node.js module compatiblity // ============================================ context['global'] = context // Implements simple module system // losely based on CommonJS Modules spec v1.1.1 // ============================================ var modules = {} , old = context['$'] , oldRequire = context['require'] , oldProvide = context['provide'] function require (identifier) { // modules can be required from ender's build system, or found on the window var module = modules['$' + identifier] || window[identifier] if (!module) throw new Error("Ender Error: Requested module '" + identifier + "' has not been defined.") return module } function provide (name, what) { return (modules['$' + name] = what) } context['provide'] = provide context['require'] = require function aug(o, o2) { for (var k in o2) k != 'noConflict' && k != '_VERSION' && (o[k] = o2[k]) return o } /** * main Ender return object * @constructor * @param {Array|Node|string} s a CSS selector or DOM node(s) * @param {Array.|Node} r a root node(s) */ function Ender(s, r) { var elements , i this.selector = s // string || node || nodelist || window if (typeof s == 'undefined') { elements = [] this.selector = '' } else if (typeof s == 'string' || s.nodeName || (s.length && 'item' in s) || s == window) { elements = ender._select(s, r) } else { elements = isFinite(s.length) ? s : [s] } this.length = elements.length for (i = this.length; i--;) this[i] = elements[i] } /** * @param {function(el, i, inst)} fn * @param {Object} opt_scope * @returns {Ender} */ Ender.prototype['forEach'] = function (fn, opt_scope) { var i, l // opt out of native forEach so we can intentionally call our own scope // defaulting to the current item and be able to return self for (i = 0, l = this.length; i < l; ++i) i in this && fn.call(opt_scope || this[i], this[i], i, this) // return self for chaining return this } Ender.prototype.$ = ender // handy reference to self function ender(s, r) { return new Ender(s, r) } ender['_VERSION'] = '0.4.3-dev' ender.fn = Ender.prototype // for easy compat to jQuery plugins ender.ender = function (o, chain) { aug(chain ? Ender.prototype : ender, o) } ender._select = function (s, r) { if (typeof s == 'string') return (r || document).querySelectorAll(s) if (s.nodeName) return [s] return s } // use callback to receive Ender's require & provide ender.noConflict = function (callback) { context['$'] = old if (callback) { context['provide'] = oldProvide context['require'] = oldRequire callback(require, provide, this) } return this } if (typeof module !== 'undefined' && module.exports) module.exports = ender // use subscript notation as extern for Closure compilation context['ender'] = context['$'] = context['ender'] || ender }(this)); package/tests/tests.js000644 000765 000024 0000124070 12056213246013376 0ustar00000000 000000 /*global reqwest:true, sink:true, start:true, ender:true, v:true */ !function (ajax) { var FakeXHR = function () { this.args = {} FakeXHR.last = this } FakeXHR.setup = function () { FakeXHR.oldxhr = window['XMLHttpRequest'] FakeXHR.oldaxo = window['ActiveXObject'] window['XMLHttpRequest'] = FakeXHR window['ActiveXObject'] = FakeXHR FakeXHR.last = null } FakeXHR.restore = function () { window['XMLHttpRequest'] = FakeXHR.oldxhr window['ActiveXObject'] = FakeXHR.oldaxo } FakeXHR.prototype.methodCallCount = function (name) { return this.args[name] ? this.args[name].length : 0 } FakeXHR.prototype.methodCallArgs = function (name, i, j) { var a = this.args[name] && this.args[name].length > i ? this.args[name][i] : null if (arguments.length > 2) return a && a.length > j ? a[j] : null return a } v.each(['open', 'send', 'setRequestHeader' ], function (f) { FakeXHR.prototype[f] = function () { if (!this.args[f]) this.args[f] = [] this.args[f].push(arguments) } }) sink('Mime Types', function (test, ok) { test('JSON', function (complete) { ajax({ url: '/tests/fixtures/fixtures.json', type: 'json', success: function (resp) { ok(resp, 'received response') ok(resp && resp.boosh == 'boosh', 'correctly evaluated response as JSON') complete() } }) }) test('JSONP', function (complete) { // stub callback prefix reqwest.getcallbackPrefix = function (id) { return 'reqwest_' + id } ajax({ url: '/tests/fixtures/fixtures_jsonp.jsonp?callback=?', type: 'jsonp', success: function (resp) { console.log('RESPONSE IS', resp) ok(resp, 'received response for unique generated callback') ok(resp && resp.boosh == "boosh", "correctly evaluated response for unique generated callback as JSONP") complete() } }) }) test('JS', function (complete) { ajax({ url: '/tests/fixtures/fixtures.js', type: 'js', success: function (resp) { ok(typeof boosh !== 'undefined' && boosh == 'boosh', 'evaluated response as JavaScript') complete() } }) }) test('HTML', function (complete) { ajax({ url: '/tests/fixtures/fixtures.html', type: 'html', success: function (resp) { ok(resp == '

boosh

', 'evaluated response as HTML') complete() } }) }) test('XML', 4, function(){ ajax({ url:'/tests/fixtures/fixtures.xml', type:'xml', success:function(resp){ ok(resp &&resp instanceof Document, 'XML Response is a Document') ok(resp && resp.documentElement && resp.documentElement.nodeName=='root', 'XML Response root is ') ok(resp && resp.documentElement && resp.documentElement.hasChildNodes && resp.documentElement.firstChild.nodeName=='boosh' && resp.documentElement.firstChild.firstChild.nodeValue==='boosh', 'Correct XML response') }, error:function(err){ ok(false, err.responseText) } }) ajax({ url:'/tests/fixtures/badfixtures.xml', type:'xml', success:function(resp){ ok(resp===null, 'No XML Response') }, error:function(err){ ok(true, 'No XML Response') } }) }) }) sink('JSONP', function (test, ok) { test('Named callback in query string', function (complete) { ajax({ url: '/tests/fixtures/fixtures_jsonp2.jsonp?foo=bar', type: 'jsonp', jsonpCallback: 'foo', success: function (resp) { ok(resp, 'received response for custom callback') ok(resp && resp.boosh == "boosh", "correctly evaluated response as JSONP with custom callback") complete() } }) }) test('Unnamed callback in query string', function (complete) { ajax({ url: '/tests/fixtures/fixtures_jsonp3.jsonp?foo=?', type: 'jsonp', jsonpCallback: 'foo', success: function (resp) { ok(resp, 'received response for custom wildcard callback') ok(resp && resp.boosh == "boosh", "correctly evaluated response as JSONP with custom wildcard callback") complete() } }) }) test('No callback, no query string', function (complete) { ajax({ url: '/tests/fixtures/fixtures_jsonp3.jsonp', type: 'jsonp', jsonpCallback: 'foo', success: function (resp) { ok(resp, 'received response for custom wildcard callback') ok(resp && resp.boosh == "boosh", "correctly evaluated response as JSONP with custom callback not in url") complete() } }) }) test('No callback in existing query string', function (complete) { ajax({ url: '/tests/none.jsonp?echo&somevar=some+long+string+here', type: 'jsonp', jsonpCallbackName: 'yohoho', success: function (resp) { ok(resp && resp.query, 'received response from echo callback') ok(resp && resp.query && resp.query.somevar == 'some long string here', 'correctly evaluated response as JSONP with echo callback') complete() } }) }) test('Append data to existing query string', function (complete) { ajax({ url: '/tests/none.jsonp?echo', // should append &somevar... type: 'jsonp', data: { somevar: 'some long string here', anothervar: 'yo ho ho!' }, success: function (resp) { ok(resp && resp.query, 'received response from echo callback') ok(resp && resp.query && resp.query.somevar == 'some long string here', 'correctly sent and received data object from JSONP echo (1)') ok(resp && resp.query && resp.query.anothervar == 'yo ho ho!', 'correctly sent and received data object from JSONP echo (2)') complete() } }) }) test('Generate complete query string from data', function (complete) { ajax({ url: '/tests/none.jsonp', // should append ?echo...etc. type: 'jsonp', data: [ { name: 'somevar', value: 'some long string here' }, { name: 'anothervar', value: 'yo ho ho!' }, { name: 'echo', value: true } ], success: function (resp) { ok(resp && resp.query, 'received response from echo callback') ok(resp && resp.query && resp.query.somevar == 'some long string here', 'correctly sent and received data array from JSONP echo (1)') ok(resp && resp.query && resp.query.anothervar == 'yo ho ho!', 'correctly sent and received data array from JSONP echo (2)') complete() } }) }) test('Append data to query string and insert callback name', function (complete) { ajax({ url: '/tests/none.jsonp?callback=?', // should append data and match callback correctly type: 'jsonp', jsonpCallbackName: 'reqwest_foo', data: { foo: 'bar', boo: 'baz', echo: true }, success: function (resp) { ok(resp && resp.query, 'received response from echo callback') ok(resp && resp.query && resp.query.callback == 'reqwest_foo', 'correctly matched callback in URL') complete() } }) }) }) sink('Callbacks', function (test, ok) { test('no callbacks', function (complete) { var pass = true try { ajax('/tests/fixtures/fixtures.js') } catch (ex) { pass = false } finally { ok(pass, 'successfully doesnt fail without callback') complete() } }) test('complete is called', function (complete) { ajax({ url: '/tests/fixtures/fixtures.js', complete: function () { ok(true, 'called complete') complete() } }) }) test('invalid JSON sets error on resp object', function (complete) { ajax({ url: '/tests/fixtures/invalidJSON.json', type: 'json', success: function (resp) { ok(false, 'success callback fired') complete() }, error: function (resp, msg) { ok(msg == 'Could not parse JSON in response', 'error callback fired') complete() } }) }) test('multiple parallel named JSONP callbacks', 8, function () { ajax({ url: '/tests/fixtures/fixtures_jsonp_multi.jsonp?callback=reqwest_0', type: 'jsonp', success: function (resp) { ok(resp, 'received response from call #1') ok(resp && resp.a == "a", "evaluated response from call #1 as JSONP") } }) ajax({ url: '/tests/fixtures/fixtures_jsonp_multi_b.jsonp?callback=reqwest_0', type: 'jsonp', success: function (resp) { ok(resp, 'received response from call #2') ok(resp && resp.b == "b", "evaluated response from call #2 as JSONP") } }) ajax({ url: '/tests/fixtures/fixtures_jsonp_multi_c.jsonp?callback=reqwest_0', type: 'jsonp', success: function (resp) { ok(resp, 'received response from call #2') ok(resp && resp.c == "c", "evaluated response from call #3 as JSONP") } }) ajax({ url: '/tests/fixtures/fixtures_jsonp_multi.jsonp?callback=reqwest_0', type: 'jsonp', success: function (resp) { ok(resp, 'received response from call #2') ok(resp && resp.a == "a", "evaluated response from call #4 as JSONP") } }) }) }) sink('Cross-origin Resource Sharing', function(test, ok) { var supportsCors = window.XMLHttpRequest && ("withCredentials" in new window.XMLHttpRequest) test('make request to another origin', 1, function() { ajax({ url: 'http://localhost:5678/get-value', type: 'text', method: 'get', crossOrigin: true, complete: function (resp) { if (supportsCors) ok(resp.responseText === 'hello', 'request made successfully') else ok(true, 'browser does not support Cross-Origin Resource Sharing') } }) }) test('set cookie on other origin', 2, function() { ajax({ url: 'http://localhost:5678/set-cookie', type: 'text', method: 'get', crossOrigin: true, withCredentials: true, before: function (http) { if (supportsCors) ok(http.withCredentials === true, 'has set withCredentials on connection object') else ok(true, 'browser does not support Cross-Origin Resource Sharing') }, complete: function (resp) { if (supportsCors) ok(resp.status === 200, 'cookie set successfully') else ok(true, 'browser does not support Cross-Origin Resource Sharing') } }) }) test('get cookie from other origin', 1, function() { ajax({ url: 'http://localhost:5678/get-cookie-value', type: 'text', method: 'get', crossOrigin: true, withCredentials: true, complete: function (resp) { if (supportsCors) ok(resp.responseText === 'hello', 'cookie value retrieved successfully') else ok(true, 'browser does not support Cross-Origin Resource Sharing') } }) }) }) sink('Connection Object', function (test, ok) { test('setRequestHeaders', function (complete) { ajax({ url: '/tests/fixtures/fixtures.html', data: 'foo=bar&baz=thunk', method: 'post', headers: { 'Accept': 'application/x-foo' }, success: function (resp) { ok(true, 'can post headers') complete() } }) }) test('can inspect http before send', function (complete) { var connection = ajax({ url: '/tests/fixtures/fixtures.js', method: 'post', type: 'js', before: function (http) { ok(http.readyState == 1, 'received http connection object') }, success: function () { // Microsoft.XMLHTTP appears not to run this async in IE6&7, it processes the request and // triggers success() before ajax() even returns. Perhaps a better solution would be to // defer the calls within handleReadyState(). setTimeout(function () { ok(connection.request.readyState == 4, 'success callback has readyState of 4') complete() }, 0) } }) }) test('ajax() encodes array `data`', function (complete) { FakeXHR.setup() try { ajax({ url: '/tests/fixtures/fixtures.html', method: 'post', data: [ { name: 'foo', value: 'bar' }, { name: 'baz', value: 'thunk' }] }) ok(FakeXHR.last.methodCallCount('send') == 1, 'send called') ok(FakeXHR.last.methodCallArgs('send', 0).length == 1, 'send called with 1 arg') ok(FakeXHR.last.methodCallArgs('send', 0, 0) == 'foo=bar&baz=thunk', 'send called with encoded array') complete() } finally { FakeXHR.restore() } }) test('ajax() encodes hash `data`', function (complete) { FakeXHR.setup() try { ajax({ url: '/tests/fixtures/fixtures.html', method: 'post', data: { bar: 'foo', thunk: 'baz' } }) ok(FakeXHR.last.methodCallCount('send') == 1, 'send called') ok(FakeXHR.last.methodCallArgs('send', 0).length == 1, 'send called with 1 arg') ok(FakeXHR.last.methodCallArgs('send', 0, 0) == 'bar=foo&thunk=baz', 'send called with encoded array') complete() } finally { FakeXHR.restore() } }) test('ajax() obeys `processData`', function (complete) { FakeXHR.setup() try { var d = { bar: 'foo', thunk: 'baz' } ajax({ url: '/tests/fixtures/fixtures.html', processData: false, method: 'post', data: d }) ok(FakeXHR.last.methodCallCount('send') == 1, 'send called') ok(FakeXHR.last.methodCallArgs('send', 0).length == 1, 'send called with 1 arg') ok(FakeXHR.last.methodCallArgs('send', 0, 0) === d, 'send called with exact `data` object') complete() } finally { FakeXHR.restore() } }) function testXhrGetUrlAdjustment(url, data, expectedUrl, complete) { FakeXHR.setup() try { ajax({ url: url, data: data }) ok(FakeXHR.last.methodCallCount('open') == 1, 'open called') ok(FakeXHR.last.methodCallArgs('open', 0).length == 3, 'open called with 3 args') ok(FakeXHR.last.methodCallArgs('open', 0, 0) == 'GET', 'first arg of open() is "GET"') ok(FakeXHR.last.methodCallArgs('open', 0, 1) == expectedUrl , 'second arg of open() is URL with query string') ok(FakeXHR.last.methodCallArgs('open', 0, 2) === true, 'third arg of open() is `true`') ok(FakeXHR.last.methodCallCount('send') == 1, 'send called') ok(FakeXHR.last.methodCallArgs('send', 0).length == 1, 'send called with 1 arg') ok(FakeXHR.last.methodCallArgs('send', 0, 0) === null, 'send called with null') complete() } finally { FakeXHR.restore() } } test('ajax() appends GET URL with ?`data`', function (complete) { testXhrGetUrlAdjustment('/tests/fixtures/fixtures.html', 'bar=foo&thunk=baz', '/tests/fixtures/fixtures.html?bar=foo&thunk=baz', complete) }) test('ajax() appends GET URL with ?`data` (serialized object)', function (complete) { testXhrGetUrlAdjustment('/tests/fixtures/fixtures.html', { bar: 'foo', thunk: 'baz' }, '/tests/fixtures/fixtures.html?bar=foo&thunk=baz', complete) }) test('ajax() appends GET URL with &`data` (serialized array)', function (complete) { testXhrGetUrlAdjustment( '/tests/fixtures/fixtures.html?x=y' , [ { name: 'bar', value: 'foo'}, {name: 'thunk', value: 'baz' } ] , '/tests/fixtures/fixtures.html?x=y&bar=foo&thunk=baz' , complete ) }) }) sink('Standard vs compat mode', function (test, ok) { function methodMatch(resp, method) { return resp && resp.method === method } function headerMatch(resp, key, expected) { return resp && resp.headers && resp.headers[key] === expected } function queryMatch(resp, key, expected) { return resp && resp.query && resp.query[key] === expected } test('standard mode default', function (complete) { ajax({ url: '/tests/none.json?echo' , success: function (resp) { ok(methodMatch(resp, 'GET'), 'correct request method (GET)') ok(headerMatch(resp, 'content-type', 'application/x-www-form-urlencoded'), 'correct Content-Type request header') ok(headerMatch(resp, 'x-requested-with', 'XMLHttpRequest'), 'correct X-Requested-With header') ok(headerMatch(resp, 'accept', 'text/javascript, text/html, application/xml, text/xml, */*'), 'correct Accept header') complete() } }) }) test('standard mode custom content-type', function (complete) { ajax({ url: '/tests/none.json?echo' , contentType: 'yapplication/foobar' , success: function (resp) { ok(methodMatch(resp, 'GET'), 'correct request method (GET)') ok(headerMatch(resp, 'content-type', 'yapplication/foobar'), 'correct Content-Type request header') ok(headerMatch(resp, 'x-requested-with', 'XMLHttpRequest'), 'correct X-Requested-With header') ok(headerMatch(resp, 'accept', 'text/javascript, text/html, application/xml, text/xml, */*'), 'correct Accept header') complete() } }) }) test('compat mode "dataType=json" headers', function (complete) { ajax.compat({ url: '/tests/none.json?echo' , dataType: 'json' // should map to 'type' , success: function (resp) { ok(methodMatch(resp, 'GET'), 'correct request method (GET)') ok(headerMatch(resp, 'content-type', 'application/x-www-form-urlencoded'), 'correct Content-Type request header') ok(headerMatch(resp, 'x-requested-with', 'XMLHttpRequest'), 'correct X-Requested-With header') ok(headerMatch(resp, 'accept', 'application/json, text/javascript'), 'correct Accept header') complete() } }) }) test('compat mode "dataType=json" with "type=post" headers', function (complete) { ajax.compat({ url: '/tests/none.json?echo' , type: 'post' , dataType: 'json' // should map to 'type' , success: function (resp) { ok(methodMatch(resp, 'POST'), 'correct request method (POST)') ok(headerMatch(resp, 'content-type', 'application/x-www-form-urlencoded'), 'correct Content-Type request header') ok(headerMatch(resp, 'x-requested-with', 'XMLHttpRequest'), 'correct X-Requested-With header') ok(headerMatch(resp, 'accept', 'application/json, text/javascript'), 'correct Accept header') complete() } }) }) test('compat mode "dataType=json" headers (with additional headers)', function (complete) { ajax.compat({ url: '/tests/none.json?echo' , dataType: 'json' // should map to 'type' , headers: { one: 1, two: 2 } // verify that these are left intact and nothing screwey happens with headers , success: function (resp) { ok(headerMatch(resp, 'content-type', 'application/x-www-form-urlencoded'), 'correct Content-Type request header') ok(headerMatch(resp, 'x-requested-with', 'XMLHttpRequest'), 'correct X-Requested-With header') ok(headerMatch(resp, 'accept', 'application/json, text/javascript'), 'correct Accept header') ok(headerMatch(resp, 'one', '1') && headerMatch(resp, 'two', '2'), 'left additional headers intact') complete() } }) }) test('compat mode "dataType=jsonp" query string', function (complete) { ajax.compat({ url: '/tests/none.jsonp?echo' , dataType: 'jsonp' , jsonp: 'testCallback' // should map to jsonpCallback , jsonpCallback: 'foobar' // should map to jsonpCallbackName , success: function (resp) { ok(queryMatch(resp, 'echo', ''), 'correct Content-Type request header') ok(queryMatch(resp, 'testCallback', 'foobar'), 'correct X-Requested-With header') complete() } }) }) }) /***************** SERIALIZER TESTS ***********************/ // define some helpers for the serializer tests that are used often and shared with // the ender integration tests var BIND_ARGS = 'bind' , PASS_ARGS = 'pass' function createSerializeHelper(ok) { var forms = document.forms , foo = forms[0].getElementsByTagName('input')[1] , bar = forms[0].getElementsByTagName('input')[2] , choices = forms[0].getElementsByTagName('select')[0] , BIND_ARGS = 'bind' , PASS_ARGS = 'pass' function reset() { forms[1].reset() } function formElements(formIndex, tagName, elementIndex) { return forms[formIndex].getElementsByTagName(tagName)[elementIndex] } function isArray(a) { return Object.prototype.toString.call(a) == '[object Array]' } function sameValue(value, expected) { if (expected == null) { return value === null } else if (isArray(expected)) { if (value.length !== expected.length) return false for (var i = 0; i < expected.length; i++) { if (value[i] != expected[i]) return false } return true } else return value == expected } function testInput(input, name, value, str) { var sa = ajax.serialize(input, { type: 'array' }) var sh = ajax.serialize(input, { type: 'map' }) if (value != null) { var av = isArray(value) ? value : [ value ] ok(sa.length == av.length, 'serialize(' + str + ', {type:\'array\'}) returns array [{name,value}]') for (var i = 0; i < av.length; i++) { ok(name == sa[i].name, 'serialize(' + str + ', {type:\'array\'})[' + i + '].name') ok(av[i] == sa[i].value, 'serialize(' + str + ', {type:\'array\'})[' + i + '].value') } ok(sameValue(sh[name], value), 'serialize(' + str + ', {type:\'map\'})') } else { // the cases where an element shouldn't show up at all, checkbox not checked for example ok(sa.length === 0, 'serialize(' + str + ', {type:\'array\'}) is []') ok(v.keys(sh).length === 0, 'serialize(' + str + ', {type:\'map\'}) is {}') } } function testFormSerialize(method, type) { var expected = 'foo=bar&bar=baz&wha=1&wha=3&who=tawoo&%24escapable+name%24=escapeme&choices=two&opinions=world+peace+is+not+real' ok(method, 'serialize() bound to context') ok((method ? method(forms[0]) : null) == expected, 'serialized form (' + type + ')') } function executeMultiArgumentMethod(method, argType, options) { var els = [ foo, bar, choices ] , ths = argType === BIND_ARGS ? ender(els) : null , args = argType === PASS_ARGS ? els : [] if (!!options) args.push(options) return method.apply(ths, args) } function testMultiArgumentSerialize(method, type, argType) { ok(method, 'serialize() bound in context') var result = method ? executeMultiArgumentMethod(method, argType) : null ok(result == "foo=bar&bar=baz&choices=two", "serialized all 3 arguments together") } function testFormSerializeArray(method, type) { ok(method, 'serialize(..., {type:\'array\'}) bound to context') var result = method ? method(forms[0], { type: 'array' }) : [] if (!result) result = [] verifyFormSerializeArray(result, type) } function verifyFormSerializeArray(result, type) { var expected = [ { name: 'foo', value: 'bar' }, { name: 'bar', value: 'baz' }, { name: 'wha', value: 1 }, { name: 'wha', value: 3 }, { name: 'who', value: 'tawoo' }, { name: '$escapable name$', value: 'escapeme' }, { name: 'choices', value: 'two' }, { name: 'opinions', value: 'world peace is not real' } ] for (var i = 0; i < expected.length; i++) { ok(v.some(result, function (v) { return v.name == expected[i].name && v.value == expected[i].value }), 'serialized ' + expected[i].name + ' (' + type + ')') } } function testMultiArgumentSerializeArray(method, type, argType) { ok(method, 'serialize(..., {type:\'array\'}) bound to context') var result = method ? executeMultiArgumentMethod(method, argType, { type: 'array' }) : [] if (!result) result = [] ok(result.length == 3, 'serialized as array of 3') ok(result.length == 3 && result[0].name == 'foo' && result[0].value == 'bar', 'serialized first element (' + type + ')') ok(result.length == 3 && result[1].name == 'bar' && result[1].value == 'baz', 'serialized second element (' + type + ')') ok(result.length == 3 && result[2].name == 'choices' && result[2].value == 'two', 'serialized third element (' + type + ')') } function testFormSerializeHash(method, type) { var expected = { foo: 'bar', bar: 'baz', wha: [ "1", "3" ], who: 'tawoo', '$escapable name$': 'escapeme', choices: 'two', opinions: 'world peace is not real' } ok(method, 'serialize({type:\'map\'}) bound to context') var result = method ? method(forms[0], { type: 'map' }) : {} if (!result) result = {} ok(v.keys(expected).length === v.keys(result).length, 'same number of keys (' + type + ')') v.each(v.keys(expected), function (k) { ok(sameValue(expected[k], result[k]), 'same value for ' + k + ' (' + type + ')') }) } function testMultiArgumentSerializeHash(method, type, argType) { ok(method, 'serialize({type:\'map\'}) bound to context') var result = method ? executeMultiArgumentMethod(method, argType, { type: 'map' }) : {} if (!result) result = {} ok(result.foo == 'bar', 'serialized first element (' + type + ')') ok(result.bar == 'baz', 'serialized second element (' + type + ')') ok(result.choices == 'two', 'serialized third element (' + type + ')') } return { reset: reset , formElements: formElements , testInput: testInput , testFormSerialize: testFormSerialize , testMultiArgumentSerialize: testMultiArgumentSerialize , testFormSerializeArray: testFormSerializeArray , verifyFormSerializeArray: verifyFormSerializeArray , testMultiArgumentSerializeArray: testMultiArgumentSerializeArray , testFormSerializeHash: testFormSerializeHash , testMultiArgumentSerializeHash: testMultiArgumentSerializeHash } } sink('Serializing', function (test, ok) { /* * Serialize forms according to spec. * * reqwest.serialize(ele[, ele...]) returns a query string style serialization * * reqwest.serialize(ele[, ele...], {type:'array'}) returns a [ { name: 'name', value: 'value'}, ... ] * style serialization, compatible with jQuery.serializeArray() * * reqwest.serialize(ele[, ele...], {type:\'map\'}) returns a { 'name': 'value', ... } style * serialization, compatible with Prototype Form.serializeElements({hash:true}) * Some tests based on spec notes here: http://malsup.com/jquery/form/comp/test.html */ var sHelper = createSerializeHelper(ok) sHelper.reset() test('correctly serialize textarea', function (complete) { var textarea = sHelper.formElements(1, 'textarea', 0) // the texarea has 2 different newline styles, should come out as normalized CRLF as per forms spec ok('T3=%3F%0D%0AA+B%0D%0AZ' == ajax.serialize(textarea), 'serialize(textarea)') var sa = ajax.serialize(textarea, { type: 'array' }) ok(sa.length == 1, 'serialize(textarea, {type:\'array\'}) returns array') sa = sa[0] ok('T3' == sa.name, 'serialize(textarea, {type:\'array\'}).name') ok('?\r\nA B\r\nZ' == sa.value, 'serialize(textarea, {type:\'array\'}).value') ok('?\r\nA B\r\nZ' == ajax.serialize(textarea, { type: 'map' }).T3, 'serialize(textarea, {type:\'map\'})') complete() }) test('correctly serialize input[type=hidden]', function (complete) { sHelper.testInput(sHelper.formElements(1, 'input', 0), 'H1', 'x', 'hidden') sHelper.testInput(sHelper.formElements(1, 'input', 1), 'H2', '', 'hidden[no value]') complete() }) test('correctly serialize input[type=password]', function (complete) { sHelper.testInput(sHelper.formElements(1, 'input', 2), 'PWD1', 'xyz', 'password') sHelper.testInput(sHelper.formElements(1, 'input', 3), 'PWD2', '', 'password[no value]') complete() }) test('correctly serialize input[type=text]', function (complete) { sHelper.testInput(sHelper.formElements(1, 'input', 4), 'T1', '', 'text[no value]') sHelper.testInput(sHelper.formElements(1, 'input', 5), 'T2', 'YES', 'text[readonly]') sHelper.testInput(sHelper.formElements(1, 'input', 10), 'My Name', 'me', 'text[space name]') complete() }) test('correctly serialize input[type=checkbox]', function (complete) { var cb1 = sHelper.formElements(1, 'input', 6) , cb2 = sHelper.formElements(1, 'input', 7) sHelper.testInput(cb1, 'C1', null, 'checkbox[not checked]') cb1.checked = true sHelper.testInput(cb1, 'C1', '1', 'checkbox[checked]') // special case here, checkbox with no value='' should give you 'on' for cb.value sHelper.testInput(cb2, 'C2', null, 'checkbox[no value, not checked]') cb2.checked = true sHelper.testInput(cb2, 'C2', 'on', 'checkbox[no value, checked]') complete() }) test('correctly serialize input[type=radio]', function (complete) { var r1 = sHelper.formElements(1, 'input', 8) , r2 = sHelper.formElements(1, 'input', 9) sHelper.testInput(r1, 'R1', null, 'radio[not checked]') r1.checked = true sHelper.testInput(r1, 'R1', '1', 'radio[not checked]') sHelper.testInput(r2, 'R1', null, 'radio[no value, not checked]') r2.checked = true sHelper.testInput(r2, 'R1', '', 'radio[no value, checked]') complete() }) test('correctly serialize input[type=reset]', function (complete) { sHelper.testInput(sHelper.formElements(1, 'input', 11), 'rst', null, 'reset') complete() }) test('correctly serialize input[type=file]', function (complete) { sHelper.testInput(sHelper.formElements(1, 'input', 12), 'file', null, 'file') complete() }) test('correctly serialize input[type=submit]', function (complete) { // we're only supposed to serialize a submit button if it was clicked to perform this // serialization: http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2 // but we'll pretend to be oblivious to this part of the spec... sHelper.testInput(sHelper.formElements(1, 'input', 13), 'sub', 'NO', 'submit') complete() }) test('correctly serialize select with no options', function (complete) { var select = sHelper.formElements(1, 'select', 0) sHelper.testInput(select, 'S1', null, 'select, no options') complete() }) test('correctly serialize select with values', function (complete) { var select = sHelper.formElements(1, 'select', 1) sHelper.testInput(select, 'S2', 'abc', 'select option 1 (default)') select.selectedIndex = 1 sHelper.testInput(select, 'S2', 'def', 'select option 2') select.selectedIndex = 6 sHelper.testInput(select, 'S2', 'disco stu', 'select option 7') // a special case where we have , should return "" rather than X // which will happen if you just do a simple `value=(option.value||option.text)` select.selectedIndex = 9 sHelper.testInput(select, 'S2', '', 'select option 9, value="" should yield ""') select.selectedIndex = -1 sHelper.testInput(select, 'S2', null, 'select, unselected') complete() }) test('correctly serialize select without explicit values', function (complete) { var select = sHelper.formElements(1, 'select', 2) sHelper.testInput(select, 'S3', 'ABC', 'select option 1 (default)') select.selectedIndex = 1 sHelper.testInput(select, 'S3', 'DEF', 'select option 2') select.selectedIndex = 6 sHelper.testInput(select, 'S3', 'DISCO STU!', 'select option 7') select.selectedIndex = -1 sHelper.testInput(select, 'S3', null, 'select, unselected') complete() }) test('correctly serialize select multiple', function (complete) { var select = sHelper.formElements(1, 'select', 3) sHelper.testInput(select, 'S4', null, 'select, unselected (default)') select.options[1].selected = true sHelper.testInput(select, 'S4', '2', 'select option 2') select.options[3].selected = true sHelper.testInput(select, 'S4', [ '2', '4' ], 'select options 2 & 4') select.options[8].selected = true sHelper.testInput(select, 'S4', [ '2', '4', 'Disco Stu!' ], 'select option 2 & 4 & 9') select.options[3].selected = false sHelper.testInput(select, 'S4', [ '2', 'Disco Stu!' ], 'select option 2 & 9') select.options[1].selected = false select.options[8].selected = false sHelper.testInput(select, 'S4', null, 'select, all unselected') complete() }) test('correctly serialize options', function (complete) { var option = sHelper.formElements(1, 'select', 1).options[6] sHelper.testInput(option, '-', null, 'just option (with value), shouldn\'t serialize') var option = sHelper.formElements(1, 'select', 2).options[6] sHelper.testInput(option, '-', null, 'option (without value), shouldn\'t serialize') complete() }) test('correctly serialize disabled', function (complete) { var input = sHelper.formElements(1, 'input', 14) sHelper.testInput(input, 'D1', null, 'disabled text input') input = sHelper.formElements(1, 'input', 15) sHelper.testInput(input, 'D2', null, 'disabled checkbox') input = sHelper.formElements(1, 'input', 16) sHelper.testInput(input, 'D3', null, 'disabled radio') var select = sHelper.formElements(1, 'select', 4) sHelper.testInput(select, 'D4', null, 'disabled select') select = sHelper.formElements(1, 'select', 3) sHelper.testInput(select, 'D5', null, 'disabled select option') select = sHelper.formElements(1, 'select', 6) sHelper.testInput(select, 'D6', null, 'disabled multi select') select = sHelper.formElements(1, 'select', 7) sHelper.testInput(select, 'D7', null, 'disabled multi select option') complete() }) test('serialize(form)', function (complete) { sHelper.testFormSerialize(ajax.serialize, 'direct') complete() }) test('serialize(form, {type:\'array\'})', function (complete) { sHelper.testFormSerializeArray(ajax.serialize, 'direct') complete() }) test('serialize(form, {type:\'map\'})', function (complete) { sHelper.testFormSerializeHash(ajax.serialize, 'direct') complete() }) // mainly for Ender integration, so you can do this: // $('input[name=T2],input[name=who],input[name=wha]').serialize() test('serialize(element, element, element...)', function (complete) { sHelper.testMultiArgumentSerialize(ajax.serialize, 'direct', PASS_ARGS) complete() }) // mainly for Ender integration, so you can do this: // $('input[name=T2],input[name=who],input[name=wha]').serialize({type:'array'}) test('serialize(element, element, element..., {type:\'array\'})', function (complete) { sHelper.testMultiArgumentSerializeArray(ajax.serialize, 'direct', PASS_ARGS) complete() }) // mainly for Ender integration, so you can do this: // $('input[name=T2],input[name=who],input[name=wha]').serialize({type:'map'}) test('serialize(element, element, element...)', function (complete) { sHelper.testMultiArgumentSerializeHash(ajax.serialize, 'direct', PASS_ARGS) complete() }) test('toQueryString([{ name: x, value: y }, ... ]) name/value array', function (complete) { var arr = [ { name: 'foo', value: 'bar' }, {name: 'baz', value: ''}, { name: 'x', value: -20 }, { name: 'x', value: 20 } ] ok(ajax.toQueryString(arr) == "foo=bar&baz=&x=-20&x=20", "simple") var arr = [ { name: 'dotted.name.intact', value: '$@%' }, { name: '$ $', value: 20 }, { name: 'leave britney alone', value: 'waa haa haa' } ] ok(ajax.toQueryString(arr) == "dotted.name.intact=%24%40%25&%24+%24=20&leave+britney+alone=waa+haa+haa", "escaping required") complete() }) test('toQueryString({name: value,...} complex object', function (complete) { var obj = { 'foo': 'bar', 'baz': '', 'x': -20 } ok(ajax.toQueryString(obj) == "foo=bar&baz=&x=-20", "simple") var obj = { 'dotted.name.intact': '$@%', '$ $': 20, 'leave britney alone': 'waa haa haa' } ok(ajax.toQueryString(obj) == "dotted.name.intact=%24%40%25&%24+%24=20&leave+britney+alone=waa+haa+haa", "escaping required") complete() }) test('toQueryString({name: [ value1, value2 ...],...} object with arrays', function (complete) { var obj = { 'foo': 'bar', 'baz': [ '', '', 'boo!' ], 'x': [ -20, 2.2, 20 ] } ok(ajax.toQueryString(obj) == "foo=bar&baz=&baz=&baz=boo!&x=-20&x=2.2&x=20", "object with arrays") complete() }) }) sink('Ender Integration', function (test, ok) { var sHelper = createSerializeHelper(ok) sHelper.reset() test('$.ajax alias for reqwest, not bound to boosh', 1, function () { ok(ender.ajax === ajax, '$.ajax is reqwest') }) // sHelper.test that you can do $.serialize(form) test('$.serialize(form)', function (complete) { sHelper.testFormSerialize(ender.serialize, 'ender') complete() }) // sHelper.test that you can do $.serialize(form) test('$.serialize(form, {type:\'array\'})', function (complete) { sHelper.testFormSerializeArray(ender.serialize, 'ender') complete() }) // sHelper.test that you can do $.serialize(form) test('$.serialize(form, {type:\'map\'})', function (complete) { sHelper.testFormSerializeHash(ender.serialize, 'ender') complete() }) // sHelper.test that you can do $.serializeObject(form) test('$.serializeArray(...) alias for serialize(..., {type:\'map\'}', function (complete) { sHelper.verifyFormSerializeArray(ender.serializeArray(document.forms[0]), 'ender') complete() }) test('$.serialize(element, element, element...)', function (complete) { sHelper.testMultiArgumentSerialize(ender.serialize, 'ender', PASS_ARGS) complete() }) test('$.serialize(element, element, element..., {type:\'array\'})', function (complete) { sHelper.testMultiArgumentSerializeArray(ender.serialize, 'ender', PASS_ARGS) complete() }) test('$.serialize(element, element, element..., {type:\'map\'})', function (complete) { sHelper.testMultiArgumentSerializeHash(ender.serialize, 'ender', PASS_ARGS) complete() }) test('$(element, element, element...).serialize()', function (complete) { sHelper.testMultiArgumentSerialize(ender.fn.serialize, 'ender', BIND_ARGS) complete() }) test('$(element, element, element...).serialize({type:\'array\'})', function (complete) { sHelper.testMultiArgumentSerializeArray(ender.fn.serialize, 'ender', BIND_ARGS) complete() }) test('$(element, element, element...).serialize({type:\'map\'})', function (complete) { sHelper.testMultiArgumentSerializeHash(ender.fn.serialize, 'ender', BIND_ARGS) complete() }) test('$.toQueryString alias for reqwest.toQueryString, not bound to boosh', 1, function () { ok(ender.toQueryString === ajax.toQueryString, '$.toQueryString is reqwest.toQueryString') }) }) /** * Promise tests for `then` `fail` and `always` */ sink('Promises', function (test, ok) { test('always callback is called', function (complete) { ajax({ url: '/tests/fixtures/fixtures.js' }) .always(function () { ok(true, 'called complete') complete() }) }) test('success and error handlers are called', 3, function () { ajax({ url: '/tests/fixtures/invalidJSON.json', type: 'json' }) .then(function (resp) { ok(false, 'success callback fired') }, function (resp, msg) { ok(msg == 'Could not parse JSON in response', 'error callback fired') }) ajax({ url: '/tests/fixtures/invalidJSON.json', type: 'json' }) .fail(function (resp, msg) { ok(msg == 'Could not parse JSON in response', 'fail callback fired') }) ajax({ url: '/tests/fixtures/fixtures.json', type: 'json' }) .then(function (resp) { ok(true, 'success callback fired') }, function (resp) { ok(false, 'error callback fired') }) }) test('then and always handlers can be added after a response has been received', 2, function () { var a = ajax({ url: '/tests/fixtures/fixtures.json', type: 'json' }) .always(function () { setTimeout(function () { a .then(function () { ok(true, 'success callback called') }, function () { ok(false, 'error callback called') }) .always(function () { ok(true, 'complete callback called') }) }, 1) }) }) test('failure handlers can be added after a response has been received', function (complete) { var a = ajax({ url: '/tests/fixtures/invalidJSON.json', type: 'json' }) .always(function () { setTimeout(function () { a .fail(function () { ok(true, 'fail callback called') complete() }) }, 1) }) }) }) start() }(reqwest) package/tests/fixtures/fixtures.js000644 000765 000024 0000000027 11564540215015753 0ustar00000000 000000 window.boosh = 'boosh';package/tests/fixtures/fixtures_jsonp2.jsonp000644 000765 000024 0000000032 11652672366017772 0ustar00000000 000000 bar({ "boosh": "boosh" });package/tests/fixtures/badfixtures.xml000644 000765 000024 0000000030 12054732476016607 0ustar00000000 000000 Not a valid xml documentpackage/tests/fixtures/fixtures.json000644 000765 000024 0000000024 11564540215016305 0ustar00000000 000000 { "boosh": "boosh" }package/tests/fixtures/fixtures.xml000644 000765 000024 0000000041 12054732476016142 0ustar00000000 000000 booshpackage/tests/fixtures/fixtures_jsonp.jsonp000644 000765 000024 0000000040 11652672366017707 0ustar00000000 000000 reqwest_0({ "boosh": "boosh" });package/tests/fixtures/fixtures.html000644 000765 000024 0000000014 11564540215016277 0ustar00000000 000000

boosh

package/tests/fixtures/fixtures_jsonp3.jsonp000644 000765 000024 0000000041 11652672366017773 0ustar00000000 000000 reqwest_2({ "boosh": "boosh" }); package/tests/fixtures/fixtures_jsonp_multi.jsonp000644 000765 000024 0000000031 11652672366021121 0ustar00000000 000000 reqwest_0({ "a": "a" }); package/tests/fixtures/fixtures_jsonp_multi_b.jsonp000644 000765 000024 0000000031 11652672366021422 0ustar00000000 000000 reqwest_0({ "b": "b" }); package/tests/fixtures/fixtures_jsonp_multi_c.jsonp000644 000765 000024 0000000031 11652672366021423 0ustar00000000 000000 reqwest_0({ "c": "c" }); package/tests/fixtures/invalidJSON.json000644 000765 000024 0000000126 11652672366016572 0ustar00000000 000000 this is not valid JSON!, there: are ~!_+ punctuation marks all, over, the:place ^ 2 package/tests/tests.html000644 000765 000024 0000011540 12054732476013734 0ustar00000000 000000 Reqwest tests

Reqwest Tests