├── .gitignore ├── .npmignore ├── LICENSE.txt ├── README.md ├── bower.json ├── gulpfile.js ├── package.json ├── qwest.min.js ├── src └── qwest.js └── tests ├── browser └── index.html ├── modernizr.min.js ├── package.json ├── requirejs └── index.html ├── tests.js └── tests ├── 204 └── test.php ├── async └── test.php ├── auth ├── .htaccess ├── passwd └── test.php ├── base └── test.php ├── before └── test.php ├── cache └── test.php ├── get_arraybuffer ├── img.jpg └── test.php ├── get_blob ├── img.jpg └── test.php ├── get_document └── test.php ├── get_json └── test.php ├── get_text └── test.php ├── get_xml └── test.php ├── limit └── test.php ├── promises └── test.php ├── send_arraybuffer └── test.php ├── send_blob └── test.php ├── send_document └── test.php ├── send_formdata └── test.php ├── send_json └── test.php ├── send_post └── test.php ├── send_text └── test.php ├── sync └── test.php └── timeout └── test.php /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | tests/node_modules/ 3 | tests/**/Thumbs.db -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .gitignore 3 | nodes_modules/ 4 | tests/ 5 | Gruntfile.js 6 | package.json -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | THE MIT LICENSE (MIT) 2 | Copyright © 2015 DreamySource 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | qwest 4.5.0 2 | =========== 3 | 4 | ⚠ I finally decided to archive this repository. ⚠ 5 | 6 | At first, I developed Qwest because at the time other libraries were lacking of a proper XHR2 support, concurrent requests, IE8+ support, with a small footprint. The Qwest adventure was a great one but with the emergence of the `Fetch` API, the `Axios.js` library and the fact that I do not have much time to maintain it since a few years, I came to the conclusion that the project needed an end. 7 | 8 | I advice you to stop using this library as it won't be maintained anymore and use [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) with [p-limit](https://github.com/sindresorhus/p-limit) to achieve concurrent requests with an elegant and native API. Fetch is still a work in progress but has [good support amongst browsers](https://www.caniuse.com/fetch) and can be [extended with many libraries](https://www.npmjs.com/search?q=fetch). 9 | 10 | That's all folks! 11 | 12 | --- 13 | 14 | Qwest is a simple ajax library based on `promises` and that supports `XmlHttpRequest2` special data like `ArrayBuffer`, `Blob` and `FormData`. 15 | 16 | Install 17 | ------- 18 | 19 | ``` 20 | npm install qwest 21 | bower install qwest 22 | ``` 23 | 24 | Qwest is also available via CDNJS : https://cdnjs.com/libraries/qwest 25 | 26 | If you need to import qwest in TypeScript, do : 27 | 28 | ```js 29 | import * as qwest from 'qwest'; 30 | ``` 31 | 32 | Quick examples 33 | -------------- 34 | 35 | ```js 36 | qwest.get('example.com') 37 | .then(function(xhr, response) { 38 | alert(response); 39 | }); 40 | ``` 41 | 42 | ```js 43 | qwest.post('example.com', { 44 | firstname: 'Pedro', 45 | lastname: 'Sanchez', 46 | age: 30 47 | }) 48 | .then(function(xhr, response) { 49 | // Make some useful actions 50 | }) 51 | .catch(function(e, xhr, response) { 52 | // Process the error 53 | }); 54 | ``` 55 | 56 | Basics 57 | ------ 58 | 59 | ```js 60 | qwest.`method`(`url`, `data`, `options`, `before`) 61 | .then(function(xhr, response) { 62 | // Run when the request is successful 63 | }) 64 | .catch(function(e, xhr, response) { 65 | // Process the error 66 | }) 67 | .complete(function() { 68 | // Always run 69 | }); 70 | ``` 71 | 72 | The method is either `get`, `post`, `put` or `delete`. The `data` parameter can be a multi-dimensional array or object, a string, an ArrayBuffer, a Blob, etc... If you don't want to pass any data but specify some options, set data to `null`. 73 | 74 | The available `options` are : 75 | 76 | - dataType : `post` (by default), `queryString`, `json`, `text`, `arraybuffer`, `blob`, `document` or `formdata` (you shouldn't need to specify XHR2 types since they're automatically detected) 77 | - responseType : the response type; either `auto` (default), `json`, `xml`, `text`, `arraybuffer`, `blob` or `document` 78 | - cache : browser caching; default is `false` 79 | - async : `true` (default) or `false`; used to make asynchronous or synchronous requests 80 | - user : the user to access to the URL, if needed 81 | - password : the password to access to the URL, if needed 82 | - headers : javascript object containing headers to be sent 83 | - withCredentials : `false` by default; sends [credentials](http://www.w3.org/TR/XMLHttpRequest2/#user-credentials) with your XHR2 request ([more info in that post](https://dev.opera.com/articles/xhr2/#xhrcredentials)) 84 | - timeout : the timeout for the request in ms; `30000` by default (allowed only in async mode) 85 | - attempts : the total number of times to attempt the request through timeouts; 1 by default; if you want to remove the limit set it to `null` 86 | 87 | You can change the default data type with : 88 | 89 | ```js 90 | qwest.setDefaultDataType('json'); 91 | ``` 92 | 93 | If you want to make a call with another HTTP method, you can use the `map()` function : 94 | 95 | ```js 96 | qwest.map('PATCH', 'example.com') 97 | .then(function() { 98 | // Blah blah 99 | }); 100 | ``` 101 | 102 | If you need to do a `sync` request, you must call `send()` at the end of your promise : 103 | 104 | ```js 105 | qwest.get('example.com', {async: false}) 106 | .then(function() { 107 | // Blah blah 108 | }) 109 | .send(); 110 | ``` 111 | 112 | Since service APIs often need the same type of request, you can set default options for all of your requests with : 113 | 114 | ```js 115 | qwest.setDefaultOptions({ 116 | dataType: 'arraybuffer', 117 | responseType: 'json', 118 | headers: { 119 | 'My-Header': 'Some-Value' 120 | } 121 | }); 122 | ``` 123 | 124 | Note : if you want to send your data as a query string parameter chain, pass `queryString` to the `dataType` option. 125 | 126 | Group requests 127 | -------------- 128 | 129 | Sometimes we need to call several requests and execute some tasks after all of them are completed. You can simply do it by chaining your requests like : 130 | 131 | ```js 132 | qwest.get('example.com/articles') 133 | .get('example.com/users') 134 | .post('example.com/login', auth_data) 135 | .then(function(values) { 136 | /* 137 | Prints [ [xhr, response], [xhr, response], [xhr, response] ] 138 | */ 139 | console.log(values); 140 | }); 141 | ``` 142 | 143 | If an error is encountered then `catch()` will be called and all requests will be aborted. 144 | 145 | Base URI 146 | -------- 147 | 148 | You can define a base URI for your requests. The string will be prepended to the other request URIs. 149 | 150 | ```js 151 | qwest.base = 'http://example.com'; 152 | 153 | // Will make a request to 'http://example.com/somepage' 154 | qwest.get('/somepage') 155 | .then(function() { 156 | // Blah blah 157 | }); 158 | ``` 159 | 160 | Request limit 161 | ------------- 162 | 163 | One of the greatest qwest functionnalities is the request limit. It avoids browser freezes and server overloads by freeing bandwidth and memory resources when you have a whole bunch of requests to do at the same time. Set the request limit and when the count is reached qwest will stock all further requests and start them when a slot is free. 164 | 165 | Let's say we have a gallery with a lot of images to load. We don't want the browser to download all images at the same time to have a faster loading. Let's see how we can do that. 166 | 167 | ```html 168 | 176 | ``` 177 | 178 | ```js 179 | // Browsers are limited in number of parallel downloads, setting it to 4 seems fair 180 | qwest.limit(4); 181 | 182 | $('.gallery').children().forEach(function() { 183 | var $this = $(this); 184 | qwest.get($this.data('src'), {responseType: 'blob'}) 185 | .then(function(xhr, response) { 186 | $this.attr('src', window.URL.createObjectURL(response)); 187 | $this.fadeIn(); 188 | }); 189 | }); 190 | ``` 191 | 192 | If you want to remove the limit, set it to `null`. 193 | 194 | CORS and preflight requests 195 | --------------------------- 196 | 197 | According to [#90](https://github.com/pyrsmk/qwest/issues/90) and [#99](https://github.com/pyrsmk/qwest/issues/99), a CORS request will send a preflight `OPTIONS` request to the server to know what is allowed and what's not. It's because we're adding a `Cache-Control` header to handle caching of requests. The simplest way to avoid this `OPTIONS` request is to set `cache` option to `true`. If you want to know more about preflight requests and how to really handle them, read this : https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS 198 | 199 | Aborting a request 200 | ------------------ 201 | 202 | ```js 203 | // Start the request 204 | var request = qwest.get('example.com') 205 | .then(function(xhr, response) { 206 | // Won't be called 207 | }) 208 | .catch(function(xhr, response) { 209 | // Won't be called either 210 | }); 211 | 212 | // Some code 213 | 214 | request.abort(); 215 | ``` 216 | 217 | Not that only works with asynchroneous requests since synchroneous requests are... synchroneous. 218 | 219 | Set options to the XHR object 220 | ----------------------------- 221 | 222 | If you want to apply some manual options to the `XHR` object, you can use the `before` option 223 | 224 | ```js 225 | qwest.get('example.com', null, null, function(xhr) { 226 | xhr.upload.onprogress = function(e) { 227 | // Upload in progress 228 | }; 229 | }) 230 | .then(function(xhr, response) { 231 | // Blah blah blah 232 | }); 233 | ``` 234 | 235 | Handling fallbacks 236 | ------------------ 237 | 238 | XHR2 is not available on every browser, so, if needed, you can simply verify the XHR version with : 239 | 240 | ```js 241 | if(qwest.xhr2) { 242 | // Actions for XHR2 243 | } 244 | else { 245 | // Actions for XHR1 246 | } 247 | ``` 248 | 249 | Receiving binary data in older browsers 250 | --------------------------------------- 251 | 252 | Getting binary data in legacy browsers needs a trick, as we can read it on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data#Receiving_binary_data_in_older_browsers). In qwest, that's how we could handle it : 253 | 254 | ```js 255 | qwest.get('example.com/file', null, null, function(xhr) { 256 | xhr.overrideMimeType('text\/plain; charset=x-user-defined'); 257 | }) 258 | .then(function(response) { 259 | // response is now a binary string 260 | }); 261 | ``` 262 | 263 | Compatibility notes 264 | ------------------- 265 | 266 | According to this [compatibility table](https://kangax.github.io/compat-table/es5), IE7/8 do not support using `catch` and `delete` as method name because these are reserved words. If you want to support those browsers you should write : 267 | 268 | ```js 269 | qwest.delete('example.com') 270 | .then(function(){}) 271 | .catch(function(){}); 272 | ``` 273 | 274 | Like this : 275 | 276 | ```js 277 | qwest['delete']('example.com') 278 | .then(function(){}) 279 | ['catch'](function(){}); 280 | ``` 281 | 282 | XHR2 does not support `arraybuffer`, `blob` and `document` response types in synchroneous mode. 283 | 284 | The CORS object shipped with IE8 and 9 is `XDomainRequest`. This object __does not__ support `PUT` and `DELETE` requests and XHR2 types. Moreover, the `getResponseHeader()` method is not supported too which is used in the `auto` mode for detecting the reponse type. Then, the response type automatically fallbacks to `json` when in `auto` mode. If you expect another response type, please specify it explicitly. If you want to specify another default response type to fallback in `auto` mode, you can do it like this : 285 | 286 | ```js 287 | qwest.setDefaultXdrResponseType('text'); 288 | ``` 289 | 290 | Last notes 291 | ---------- 292 | 293 | - Blackberry 10.2.0 (and maybe others) can [log an error saying json is not supported](https://github.com/pyrsmk/qwest/issues/94) : set `responseType` to `auto` to avoid the issue 294 | - the `catch` handler will be executed for status codes different from `2xx`; if no data has been received when `catch` is called, `response` will be `null` 295 | - `auto` mode is only supported for `xml`, `json` and `text` response types; for `arraybuffer`, `blob` and `document` you'll need to define explicitly the `responseType` option 296 | - if the response of your request doesn't return a valid (and recognized) `Content-Type` header, then you __must__ explicitly set the `responseType` option 297 | - the default `Content-Type` header for a `POST` request is `application/x-www-form-urlencoded`, for `post` and `xhr2` data types 298 | - if you want to set or get raw data, set `dataType` option to `text` 299 | - as stated on [StackOverflow](https://stackoverflow.com/questions/8464262/access-is-denied-error-on-xdomainrequest), XDomainRequest forbid HTTPS requests from HTTP scheme and vice versa 300 | - XDomainRequest only supports `GET` and `POST` methods 301 | 302 | License 303 | ------- 304 | 305 | [MIT license](http://dreamysource.mit-license.org) everywhere! 306 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qwest", 3 | "description": "Ajax library with XHR2, promises and request limit", 4 | "main": "src/qwest.js", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/pyrsmk/qwest.git" 8 | }, 9 | "keywords": [ 10 | "ajax", 11 | "request", 12 | "promises", 13 | "xhr" 14 | ], 15 | "authors": ["Aurélien Delogu"], 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/pyrsmk/qwest/issues" 19 | }, 20 | "homepage": "http://dreamysource.io/project/qwest", 21 | "ignore": [ 22 | ".git/", 23 | ".gitignore", 24 | "nodes_modules/", 25 | "tests/", 26 | "Gruntfile.js", 27 | "package.json" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | gulp = require('gulp'), 3 | size = require('gulp-sizereport'), 4 | jshint = require('gulp-jshint'), 5 | uglify = require('gulp-uglify'), 6 | resolve = require('resolve'), 7 | rename = require('gulp-rename'), 8 | replace = require('gulp-replace'), 9 | merge = require('merge2'), 10 | shell = require('gulp-shell'), 11 | derequire = require('gulp-derequire'), 12 | browserify = require('browserify'), 13 | resolve = require('resolve'), 14 | through2 = require('through2'), 15 | _ = require('lodash'); 16 | 17 | var name = 'qwest', 18 | version = fs.readFileSync('./src/'+name+'.js', {encoding: 'utf8'}).match(/^\/\*\! [\w-]+ ([0-9.]+)/)[1]; 19 | 20 | // ======================================== gulp version 21 | 22 | gulp.task('version', function() { 23 | 24 | var streams = merge(); 25 | 26 | streams.add( 27 | gulp.src( './package.json' ) 28 | .pipe( replace(/"version": "[0-9.]+",/, '"version": "'+version+'",') ) 29 | .pipe( gulp.dest('.') ) 30 | ); 31 | 32 | streams.add( 33 | gulp.src( './README.md' ) 34 | .pipe( replace(/^(\w+) [0-9.]+/, '$1 '+version) ) 35 | .pipe( gulp.dest('.') ) 36 | ); 37 | 38 | return streams; 39 | 40 | }); 41 | 42 | // ======================================== gulp lint 43 | 44 | gulp.task('lint', function() { 45 | 46 | return gulp.src( './src/'+name+'.js' ) 47 | .pipe( jshint() ) 48 | .pipe( jshint.reporter('jshint-stylish') ); 49 | 50 | }); 51 | 52 | // ======================================== gulp build 53 | 54 | gulp.task('build', ['version', 'lint'], function() { 55 | 56 | return gulp.src( './src/'+name+'.js' ) 57 | .pipe( size() ) 58 | .pipe( through2.obj(function(file, enc, next) { 59 | 60 | var b = browserify(null, { 61 | standalone: name, 62 | insertGlobalVars: {process: false} 63 | }); 64 | 65 | (_.keys(require('./package.json').dependencies) || []).forEach(function(name) { 66 | b.add(resolve.sync(name, {moduleDirectory: './node_modules/'})); 67 | }); 68 | 69 | b.require('./src/'+name+'.js', {expose: name}); 70 | 71 | b.bundle(function(err, res) { 72 | file.contents = res; 73 | next(null, file); 74 | }); 75 | 76 | }) ) 77 | .pipe( derequire() ) 78 | .pipe( uglify() ) 79 | .pipe( rename(name+'.min.js') ) 80 | .pipe( gulp.dest('.') ) 81 | .pipe( size({gzip:true}) ); 82 | 83 | }); 84 | 85 | // ======================================== gulp publish 86 | 87 | gulp.task('publish', shell.task([ 88 | "git tag -a "+version+" -m '"+version+"'", 89 | 'git push --tags', 90 | 'npm publish' 91 | ])); 92 | 93 | // ======================================== gulp 94 | 95 | gulp.task('default', ['build']); 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qwest", 3 | "description": "Ajax library with XHR2, promises and request limit", 4 | "version": "4.5.0", 5 | "author": "Aurélien Delogu (http://dreamysource.fr)", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/pyrsmk/qwest.git" 9 | }, 10 | "main": "src/qwest.js", 11 | "keywords": [ 12 | "ajax", 13 | "request", 14 | "promises", 15 | "xhr" 16 | ], 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/pyrsmk/qwest/issues", 20 | "email": "dev@dreamysource.fr" 21 | }, 22 | "homepage": "http://dreamysource.io/project/qwest", 23 | "devDependencies": { 24 | "browserify": "^11.1.0", 25 | "gulp": "^3.9.0", 26 | "gulp-derequire": "^2.1.0", 27 | "gulp-jshint": "^1.11.2", 28 | "gulp-rename": "^1.2.2", 29 | "gulp-replace": "^0.5.3", 30 | "gulp-shell": "^0.4.2", 31 | "gulp-sizereport": "^1.1.2", 32 | "gulp-uglify": "^1.2.0", 33 | "jshint-stylish": "^2.0.1", 34 | "lodash": "^3.10.1", 35 | "merge2": "^0.3.6", 36 | "resolve": "^1.1.6", 37 | "through2": "^2.0.0" 38 | }, 39 | "dependencies": { 40 | "jquery-param": "^0.1.2", 41 | "pinkyswear": "^2.2.2" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /qwest.min.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.qwest=e()}}(function(){var e;return function t(e,n,o){function r(s,a){if(!n[s]){if(!e[s]){var u="function"==typeof require&&require;if(!a&&u)return u(s,!0);if(i)return i(s,!0);var p=new Error("Cannot find module '"+s+"'");throw p.code="MODULE_NOT_FOUND",p}var f=n[s]={exports:{}};e[s][0].call(f.exports,function(t){var n=e[s][1][t];return r(n?n:t)},f,f.exports,t,e,n,o)}return n[s].exports}for(var i="function"==typeof require&&require,s=0;s-1?"json":n.indexOf(E.xml)>-1?"xml":"text"}switch(e){case"json":if(v.responseText.length)try{b="JSON"in t?JSON.parse(v.responseText):new Function("return ("+v.responseText+")")()}catch(o){throw"Error while parsing JSON body : "+o}break;case"xml":try{t.DOMParser?b=(new DOMParser).parseFromString(v.responseText,"text/xml"):(b=new ActiveXObject("Microsoft.XMLDOM"),b.async="false",b.loadXML(v.responseText))}catch(o){b=void 0}if(!b||!b.documentElement||b.getElementsByTagName("parsererror").length)throw"Invalid XML";break;default:b=v.responseText}}if("status"in v&&!/^2|1223/.test(v.status))throw v.status+" ("+v.statusText+")";X(!0,[v,b])}catch(o){X(!1,[o,v,b])}}},R=function(e){C||(e="string"==typeof e?e:"Connection aborted",X.abort(),X(!1,[new Error(e),v,null]))},k=function(){C||(y.attempts&&++O==y.attempts?R("Timeout ("+l+")"):(v.abort(),S=!1,X.send()))};if(y.async=!("async"in y)||!!y.async,y.cache="cache"in y&&!!y.cache,y.dataType="dataType"in y?y.dataType.toLowerCase():s,y.responseType="responseType"in y?y.responseType.toLowerCase():"auto",y.user=y.user||"",y.password=y.password||"",y.withCredentials=!!y.withCredentials,y.timeout="timeout"in y?parseInt(y.timeout,10):3e4,y.attempts="attempts"in y?parseInt(y.attempts,10):1,g=l.match(/\/\/(.+?)\//),w=g&&!!g[1]&&g[1]!=location.host,"ArrayBuffer"in t&&d instanceof ArrayBuffer?y.dataType="arraybuffer":"Blob"in t&&d instanceof Blob?y.dataType="blob":"Document"in t&&d instanceof Document?y.dataType="document":"FormData"in t&&d instanceof FormData&&(y.dataType="formdata"),null!==d)switch(y.dataType){case"json":d=JSON.stringify(d);break;case"post":case"queryString":d=o(d)}if(y.headers){var P=function(e,t,n){return t+n.toUpperCase()};for(g in y.headers)D[g.replace(/(^|-)([^-])/g,P)]=y.headers[g]}return"Content-Type"in D||"GET"==e||y.dataType in E&&E[y.dataType]&&(D["Content-Type"]=E[y.dataType]),D.Accept||(D.Accept=y.responseType in M?M[y.responseType]:"*/*"),w||"X-Requested-With"in D||(D["X-Requested-With"]="XMLHttpRequest"),y.cache||"Cache-Control"in D||(D["Cache-Control"]="no-cache"),"GET"!=e&&"queryString"!=y.dataType||!d||"string"!=typeof d||(l+=(/\?/.test(l)?"&":"?")+d),y.async&&X.send(),X},d=function(e){var t=[],o=0,r=[];return n(function(n){var i=-1,s=function(e){return function(s,a,u,p){var f=++i;return++o,t.push(l(e,n.base+s,a,u,p).then(function(e,t){r[f]=arguments,--o||n(!0,1==r.length?r[0]:[r])},function(){n(!1,arguments)})),n}};n.get=s("GET"),n.post=s("POST"),n.put=s("PUT"),n["delete"]=s("DELETE"),n["catch"]=function(e){return n.then(null,e)},n.complete=function(e){var t=function(){e()};return n.then(t,t)},n.map=function(e,t,n,o,r){return s(e.toUpperCase()).call(this,t,n,o,r)};for(var a in e)a in n||(n[a]=e[a]);return n.send=function(){for(var e=0,o=t.length;e-1) { 228 | responseType = 'json'; 229 | } 230 | else if(ct.indexOf(mimeTypes.xml) > -1) { 231 | responseType = 'xml'; 232 | } 233 | else { 234 | responseType = 'text'; 235 | } 236 | } 237 | } 238 | // Handle response type 239 | switch(responseType) { 240 | case 'json': 241 | if(xhr.responseText.length) { 242 | try { 243 | if('JSON' in global) { 244 | response = JSON.parse(xhr.responseText); 245 | } 246 | else { 247 | response = new Function('return (' + xhr.responseText + ')')(); 248 | } 249 | } 250 | catch(e) { 251 | throw "Error while parsing JSON body : "+e; 252 | } 253 | } 254 | break; 255 | case 'xml': 256 | // Based on jQuery's parseXML() function 257 | try { 258 | // Standard 259 | if(global.DOMParser) { 260 | response = (new DOMParser()).parseFromString(xhr.responseText,'text/xml'); 261 | } 262 | // IE<9 263 | else { 264 | response = new ActiveXObject('Microsoft.XMLDOM'); 265 | response.async = 'false'; 266 | response.loadXML(xhr.responseText); 267 | } 268 | } 269 | catch(e) { 270 | response = undefined; 271 | } 272 | if(!response || !response.documentElement || response.getElementsByTagName('parsererror').length) { 273 | throw 'Invalid XML'; 274 | } 275 | break; 276 | default: 277 | response = xhr.responseText; 278 | } 279 | } 280 | // Late status code verification to allow passing data when, per example, a 409 is returned 281 | // --- https://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request 282 | if('status' in xhr && !/^2|1223/.test(xhr.status)) { 283 | throw xhr.status + ' (' + xhr.statusText + ')'; 284 | } 285 | // Fulfilled 286 | promise(true, [xhr, response]); 287 | } 288 | catch(e) { 289 | // Rejected 290 | promise(false, [e, xhr, response]); 291 | } 292 | }, 293 | 294 | // Handle errors 295 | handleError = function(message) { 296 | if(!aborted) { 297 | message = typeof message == 'string' ? message : 'Connection aborted'; 298 | promise.abort(); 299 | promise(false, [new Error(message), xhr, null]); 300 | } 301 | }, 302 | 303 | // Handle timeouts 304 | handleTimeout = function() { 305 | if(!aborted) { 306 | if(!options.attempts || ++attempts != options.attempts) { 307 | xhr.abort(); 308 | sending = false; 309 | promise.send(); 310 | } 311 | else { 312 | handleError('Timeout (' + url + ')'); 313 | } 314 | } 315 | }; 316 | 317 | // Normalize options 318 | options.async = 'async' in options ? !!options.async : true; 319 | options.cache = 'cache' in options ? !!options.cache : false; 320 | options.dataType = 'dataType' in options ? options.dataType.toLowerCase() : defaultDataType; 321 | options.responseType = 'responseType' in options ? options.responseType.toLowerCase() : 'auto'; 322 | options.user = options.user || ''; 323 | options.password = options.password || ''; 324 | options.withCredentials = !!options.withCredentials; 325 | options.timeout = 'timeout' in options ? parseInt(options.timeout, 10) : 30000; 326 | options.attempts = 'attempts' in options ? parseInt(options.attempts, 10) : 1; 327 | 328 | // Guess if we're dealing with a cross-origin request 329 | i = url.match(/\/\/(.+?)\//); 330 | crossOrigin = i && (i[1] ? i[1] != location.host : false); 331 | 332 | // Prepare data 333 | if('ArrayBuffer' in global && data instanceof ArrayBuffer) { 334 | options.dataType = 'arraybuffer'; 335 | } 336 | else if('Blob' in global && data instanceof Blob) { 337 | options.dataType = 'blob'; 338 | } 339 | else if('Document' in global && data instanceof Document) { 340 | options.dataType = 'document'; 341 | } 342 | else if('FormData' in global && data instanceof FormData) { 343 | options.dataType = 'formdata'; 344 | } 345 | if(data !== null) { 346 | switch(options.dataType) { 347 | case 'json': 348 | data = JSON.stringify(data); 349 | break; 350 | case 'post': 351 | case 'queryString': 352 | data = jparam(data); 353 | } 354 | } 355 | 356 | // Prepare headers 357 | if(options.headers) { 358 | var format = function(match,p1,p2) { 359 | return p1 + p2.toUpperCase(); 360 | }; 361 | for(i in options.headers) { 362 | headers[i.replace(/(^|-)([^-])/g,format)] = options.headers[i]; 363 | } 364 | } 365 | if(!('Content-Type' in headers) && method!='GET') { 366 | if(options.dataType in mimeTypes) { 367 | if(mimeTypes[options.dataType]) { 368 | headers['Content-Type'] = mimeTypes[options.dataType]; 369 | } 370 | } 371 | } 372 | if(!headers.Accept) { 373 | headers.Accept = (options.responseType in accept) ? accept[options.responseType] : '*/*'; 374 | } 375 | if(!crossOrigin && !('X-Requested-With' in headers)) { // (that header breaks in legacy browsers with CORS) 376 | headers['X-Requested-With'] = 'XMLHttpRequest'; 377 | } 378 | if(!options.cache && !('Cache-Control' in headers)) { 379 | headers['Cache-Control'] = 'no-cache'; 380 | } 381 | 382 | // Prepare URL 383 | if((method == 'GET' || options.dataType == 'queryString') && data && typeof data == 'string') { 384 | url += (/\?/.test(url)?'&':'?') + data; 385 | } 386 | 387 | // Start the request 388 | if(options.async) { 389 | promise.send(); 390 | } 391 | 392 | // Return promise 393 | return promise; 394 | 395 | }; 396 | 397 | // Define external qwest object 398 | var getNewPromise = function(q) { 399 | // Prepare 400 | var promises = [], 401 | loading = 0, 402 | values = []; 403 | // Create a new promise to handle all requests 404 | return pinkyswear(function(pinky) { 405 | // Basic request method 406 | var method_index = -1, 407 | createMethod = function(method) { 408 | return function(url, data, options, before) { 409 | var index = ++method_index; 410 | ++loading; 411 | promises.push(qwest(method, pinky.base + url, data, options, before).then(function(xhr, response) { 412 | values[index] = arguments; 413 | if(!--loading) { 414 | pinky(true, values.length == 1 ? values[0] : [values]); 415 | } 416 | }, function() { 417 | pinky(false, arguments); 418 | })); 419 | return pinky; 420 | }; 421 | }; 422 | // Define external API 423 | pinky.get = createMethod('GET'); 424 | pinky.post = createMethod('POST'); 425 | pinky.put = createMethod('PUT'); 426 | pinky['delete'] = createMethod('DELETE'); 427 | pinky['catch'] = function(f) { 428 | return pinky.then(null, f); 429 | }; 430 | pinky.complete = function(f) { 431 | var func = function() { 432 | f(); // otherwise arguments will be passed to the callback 433 | }; 434 | return pinky.then(func, func); 435 | }; 436 | pinky.map = function(type, url, data, options, before) { 437 | return createMethod(type.toUpperCase()).call(this, url, data, options, before); 438 | }; 439 | // Populate methods from external object 440 | for(var prop in q) { 441 | if(!(prop in pinky)) { 442 | pinky[prop] = q[prop]; 443 | } 444 | } 445 | // Set last methods 446 | pinky.send = function() { 447 | for(var i=0, j=promises.length; i 2 | 3 | 4 | qwest 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/modernizr.min.js: -------------------------------------------------------------------------------- 1 | /* Modernizr 2.8.3 (Custom Build) | MIT & BSD 2 | * Build: http://modernizr.com/download/#-shiv-network_xhr2 3 | */ 4 | ;window.Modernizr=function(a,b,c){function t(a){i.cssText=a}function u(a,b){return t(prefixes.join(a+";")+(b||""))}function v(a,b){return typeof a===b}function w(a,b){return!!~(""+a).indexOf(b)}function x(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:v(f,"function")?f.bind(d||b):f}return!1}var d="2.8.3",e={},f=b.documentElement,g="modernizr",h=b.createElement(g),i=h.style,j,k={}.toString,l={},m={},n={},o=[],p=o.slice,q,r={}.hasOwnProperty,s;!v(r,"undefined")&&!v(r.call,"undefined")?s=function(a,b){return r.call(a,b)}:s=function(a,b){return b in a&&v(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=p.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(p.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(p.call(arguments)))};return e});for(var y in l)s(l,y)&&(q=y.toLowerCase(),e[q]=l[y](),o.push((e[q]?"":"no-")+q));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)s(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof enableClasses!="undefined"&&enableClasses&&(f.className+=" "+(b?"":"no-")+a),e[a]=b}return e},t(""),h=j=null,function(a,b){function l(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function m(){var a=s.elements;return typeof a=="string"?a.split(" "):a}function n(a){var b=j[a[h]];return b||(b={},i++,a[h]=i,j[i]=b),b}function o(a,c,d){c||(c=b);if(k)return c.createElement(a);d||(d=n(c));var g;return d.cache[a]?g=d.cache[a].cloneNode():f.test(a)?g=(d.cache[a]=d.createElem(a)).cloneNode():g=d.createElem(a),g.canHaveChildren&&!e.test(a)&&!g.tagUrn?d.frag.appendChild(g):g}function p(a,c){a||(a=b);if(k)return a.createDocumentFragment();c=c||n(a);var d=c.frag.cloneNode(),e=0,f=m(),g=f.length;for(;e",g="hidden"in a,k=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){g=!0,k=!0}})();var s={elements:d.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:c,shivCSS:d.shivCSS!==!1,supportsUnknownElements:k,shivMethods:d.shivMethods!==!1,type:"default",shivDocument:r,createElement:o,createDocumentFragment:p};a.html5=s,r(b)}(this,b),e._version=d,e}(this,this.document),Modernizr.addTest("xhr2","FormData"in window); -------------------------------------------------------------------------------- /tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "domready": "^1.0.8", 4 | "pyrsmk-toast": "^1.2.4", 5 | "qunit-tap": "^1.5.0", 6 | "qunitjs": "^1.19.0", 7 | "requirejs": "^2.1.20" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/requirejs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | qwest 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/tests.js: -------------------------------------------------------------------------------- 1 | var global = this, 2 | methods = ['get', 'post', 'put', 'delete'], 3 | i, j; 4 | 5 | QUnit.test('Qwest object',function(assert) { 6 | assert.expect(1); 7 | assert.ok(typeof qwest == 'object', 'is '+(typeof qwest)); 8 | }); 9 | 10 | QUnit.test('XHR2',function(assert) { 11 | assert.expect(1); 12 | assert.ok(Modernizr.xhr2 == qwest.xhr2); 13 | }); 14 | 15 | QUnit.test('204 No Content',function(assert) { 16 | var done = assert.async(); 17 | assert.expect(1); 18 | qwest.get('../tests/204/test.php') 19 | .then(function(xhr, response) { 20 | assert.ok(true); 21 | done(); 22 | }) 23 | ['catch'](function(e, xhr, response) { 24 | assert.ok(false, e); 25 | done(); 26 | }); 27 | }); 28 | 29 | QUnit.test('Complete() promise',function(assert) { 30 | var done = assert.async(); 31 | assert.expect(2); 32 | qwest.get('../tests/base/test.php') 33 | .complete(function() { 34 | assert.ok(true, 'complete called'); 35 | assert.ok(!arguments.length, 'no argument passed'); 36 | done(); 37 | }); 38 | }); 39 | 40 | QUnit.test('Base URL',function(assert){ 41 | var done = assert.async(); 42 | assert.expect(1); 43 | qwest.base = /^.*\//.exec(global.location.href); 44 | qwest.get('../tests/base/test.php') 45 | .then(function(xhr, response) { 46 | //console.log(response.debug); 47 | assert.ok(response.status=='ok'); 48 | done(); 49 | }) 50 | ['catch'](function(e, xhr, response){ 51 | assert.ok(false, e); 52 | done(); 53 | }); 54 | qwest.base = ''; 55 | }); 56 | 57 | QUnit.test('Aborting a request (async)',function(assert) { 58 | var done = assert.async(); 59 | assert.expect(0); 60 | qwest.get('../tests/base/test.php') 61 | .then(function(xhr, response) { 62 | console.log(response); 63 | assert.ok(false, 'then called'); 64 | done(); 65 | }) 66 | ['catch'](function(e, xhr, response) { 67 | console.log(e.message); 68 | assert.ok(false, 'catch called'); 69 | done(); 70 | }) 71 | .abort(); 72 | done(); 73 | }); 74 | 75 | QUnit.test('Promises.all() implementation',function(assert) { 76 | var done = assert.async(); 77 | assert.expect(2); 78 | qwest.get('../tests/promises/test.php') 79 | .post('../tests/promises/test.php') 80 | .put('../tests/promises/test.php') 81 | ['delete']('../tests/promises/test.php') 82 | .then(function(values) { 83 | assert.ok(values.length == 4, values.length + ' value stack passed'); 84 | assert.ok(values[0][1].response == 'GET' && values[1][1].response == 'POST' && values[2][1].response == 'PUT' && values[3][1].response == 'DELETE', 'ordered values'); 85 | done(); 86 | }); 87 | }); 88 | 89 | QUnit.test('REST requests (async)',function(assert){ 90 | var done = assert.async(); 91 | assert.expect(methods.length); 92 | var executed=0; 93 | for(i=0,j=methods.length;i=1000,(+new Date-t)+'ms'); 247 | done(); 248 | }); 249 | }); 250 | 251 | QUnit.test('CORS',function(assert){ 252 | var done = assert.async(); 253 | assert.expect(1); 254 | qwest.get('http://sandbox.dreamysource.fr/cors/index.php') 255 | .then(function(xhr, response){ 256 | assert.ok(response.status=='ok'); 257 | done(); 258 | }) 259 | ['catch'](function(e, xhr, response){ 260 | assert.ok(false, e); 261 | done(); 262 | }); 263 | }); 264 | 265 | QUnit.test('Before',function(assert){ 266 | var done = assert.async(); 267 | assert.expect(1); 268 | qwest.get('../tests/before/test.php', null, null, function(xhr){ 269 | xhr.setRequestHeader('X-Running-Test','before'); 270 | }) 271 | .then(function(xhr, response){ 272 | //console.log(response.debug); 273 | assert.ok(response.status=='ok'); 274 | done(); 275 | }) 276 | ['catch'](function(e, xhr, response){ 277 | assert.ok(false, e); 278 | done(); 279 | }); 280 | }); 281 | 282 | QUnit.test('Cache',function(assert){ 283 | var done = assert.async(); 284 | assert.expect(2); 285 | var a,b, 286 | phase2=function(){ 287 | qwest.get('../tests/cache/test.php', null, {responseType: 'text', cache: true}) 288 | .then(function(xhr, response){ 289 | //console.log(response); 290 | b=response*1000; 291 | qwest.get('../tests/cache/test.php', null, {responseType: 'text', cache: true}) 292 | .then(function(xhr, response){ 293 | //console.log(response); 294 | assert.ok(response==(b*1000),'Cached'); 295 | done(); 296 | }) 297 | ['catch'](function(message){ 298 | assert.ok(false,message); 299 | done(); 300 | }); 301 | }) 302 | ['catch'](function(e, xhr, response){ 303 | assert.ok(false, e); 304 | done(); 305 | }); 306 | }; 307 | qwest.get('../tests/cache/test.php',null,{responseType:'text'}) 308 | .then(function(xhr, response){ 309 | //console.log(response); 310 | a=response*1000; 311 | qwest.get('../tests/cache/test.php',null,{responseType:'text'}) 312 | .then(function(xhr, response){ 313 | //console.log(response); 314 | assert.ok(response!=(a*1000),'Not cached'); 315 | phase2(); 316 | }) 317 | ['catch'](function(e, xhr, response){ 318 | assert.ok(false, e); 319 | phase2(); 320 | }); 321 | }) 322 | ['catch'](function(e, xhr, response){ 323 | assert.ok(false, e); 324 | phase2(); 325 | }); 326 | }); 327 | 328 | QUnit.test('Authentication',function(assert){ 329 | var done = assert.async(); 330 | assert.expect(1); 331 | qwest.get('../tests/auth/test.php',null,{ 332 | user: 'pyrsmk', 333 | password: 'test' 334 | }) 335 | .then(function(xhr, response){ 336 | //console.log(response.debug); 337 | assert.ok(response.status=='ok'); 338 | done(); 339 | }) 340 | ['catch'](function(e, xhr, response){ 341 | assert.ok(false, e); 342 | done(); 343 | }); 344 | }); 345 | 346 | QUnit.test('Get JSON response',function(assert){ 347 | var done = assert.async(); 348 | assert.expect(2); 349 | qwest.get('../tests/get_json/test.php',null,{responseType:'json'}) 350 | .then(function(xhr, response){ 351 | //console.log(response.debug); 352 | assert.ok(response.status=='ok','Manual'); 353 | qwest.get('../tests/get_json/test.php',null,{headers:{'Accept':'application/json'}}) 354 | .then(function(xhr, response){ 355 | //console.log(response.debug); 356 | assert.ok(response.status=='ok','Auto'); 357 | done(); 358 | }) 359 | ['catch'](function(e, xhr, response){ 360 | assert.ok(false, e); 361 | done(); 362 | }); 363 | }) 364 | ['catch'](function(e, xhr, response){ 365 | assert.ok(false, e); 366 | done(); 367 | }); 368 | }); 369 | 370 | QUnit.test('Get DOMString response',function(assert){ 371 | var done = assert.async(); 372 | assert.expect(2); 373 | qwest.get('../tests/get_text/test.php',null,{responseType:'text'}) 374 | .then(function(xhr, response){ 375 | assert.ok(response=='ok','Manual'); 376 | qwest.get('../tests/get_text/test.php') 377 | .then(function(xhr, response){ 378 | //console.log(response.debug); 379 | assert.ok(response=='ok','Auto'); 380 | done(); 381 | }) 382 | ['catch'](function(e, xhr, response){ 383 | assert.ok(false, e); 384 | done(); 385 | }); 386 | }) 387 | ['catch'](function(e, xhr, response){ 388 | assert.ok(false, e); 389 | done(); 390 | }); 391 | }); 392 | 393 | QUnit.test('Get XML response',function(assert){ 394 | var done = assert.async(); 395 | assert.expect(2); 396 | qwest.get('../tests/get_xml/test.php',null,{responseType:'xml'}) 397 | .then(function(xhr, response){ 398 | //console.log(response.getElementsByTagName('status')[0].text); 399 | var status = response.getElementsByTagName('status')[0]; 400 | assert.ok((status.textContent || status.text)=='ok','Manual'); 401 | qwest.get('../tests/get_xml/test.php') 402 | .then(function(xhr, response){ 403 | //console.log(response.getElementsByTagName('status')[0]); 404 | var status = response.getElementsByTagName('status')[0]; 405 | assert.ok((status.textContent || status.text)=='ok','Auto'); 406 | done(); 407 | }) 408 | ['catch'](function(e, xhr, response){ 409 | assert.ok(false, e); 410 | done(); 411 | }); 412 | }) 413 | ['catch'](function(e, xhr, response){ 414 | assert.ok(false, e); 415 | done(); 416 | }); 417 | }); 418 | 419 | if('ArrayBuffer' in global){ 420 | QUnit.test('Get ArrayBuffer response',function(assert){ 421 | var done = assert.async(); 422 | assert.expect(1); 423 | qwest.get('../tests/get_arraybuffer/test.php',null,{responseType:'arraybuffer'}) 424 | .then(function(xhr, response){ 425 | var arrayBuffer=new Uint8Array(response), 426 | length=arrayBuffer.length; 427 | //console.log(arrayBuffer[0].toString(16)); 428 | //console.log(arrayBuffer[1].toString(16)); 429 | //console.log(arrayBuffer[length-2].toString(16)); 430 | //console.log(arrayBuffer[length-1].toString(16)); 431 | assert.ok( 432 | arrayBuffer[0].toString(16)=='ff' && 433 | arrayBuffer[1].toString(16)=='d8' && 434 | arrayBuffer[length-2].toString(16)=='ff' && 435 | arrayBuffer[length-1].toString(16)=='d9' 436 | ); 437 | done(); 438 | }) 439 | ['catch'](function(e, xhr, response){ 440 | assert.ok(false, e); 441 | done(); 442 | }); 443 | }); 444 | } 445 | 446 | if('Blob' in global){ 447 | QUnit.test('Get Blob response',function(assert){ 448 | var done = assert.async(); 449 | assert.expect(1); 450 | qwest.get('../tests/get_blob/test.php',null,{responseType:'blob'}) 451 | .then(function(xhr, response){ 452 | //console.log(response); 453 | assert.ok(response.size); 454 | done(); 455 | }) 456 | ['catch'](function(e, xhr, response){ 457 | assert.ok(false, e); 458 | done(); 459 | }); 460 | }); 461 | } 462 | 463 | if(qwest.xhr2 && global.document && 'querySelector' in global.document){ 464 | QUnit.test('Get Document response',function(assert){ 465 | var done = assert.async(); 466 | assert.expect(1); 467 | qwest.get('../tests/get_document/test.php',null,{responseType:'document'}) 468 | .then(function(xhr, response){ 469 | assert.ok(response.querySelector('p').innerHTML=='ok'); 470 | done(); 471 | }) 472 | ['catch'](function(e, xhr, response){ 473 | assert.ok(false, e); 474 | done(); 475 | }); 476 | }); 477 | } 478 | 479 | QUnit.test('Send basic POST data',function(assert){ 480 | var done = assert.async(); 481 | assert.expect(1); 482 | qwest.post('../tests/send_post/test.php',{ 483 | foo: 'bar', 484 | bar: [{foo:'bar'}] 485 | }) 486 | .then(function(xhr, response){ 487 | //console.log(response.debug); 488 | assert.ok(response.status=='ok'); 489 | done(); 490 | }) 491 | ['catch'](function(e, xhr, response){ 492 | assert.ok(false, e); 493 | done(); 494 | }); 495 | }); 496 | 497 | QUnit.test('Send JSON data',function(assert){ 498 | var done = assert.async(); 499 | assert.expect(1); 500 | qwest.post('../tests/send_json/test.php',{ 501 | foo: 'bar', 502 | bar: [{foo:'bar'}] 503 | },{ 504 | dataType: 'json' 505 | }) 506 | .then(function(xhr, response){ 507 | //console.log(response.debug); 508 | assert.ok(response.status=='ok'); 509 | done(); 510 | }) 511 | ['catch'](function(e, xhr, response){ 512 | assert.ok(false, e); 513 | done(); 514 | }); 515 | }); 516 | 517 | QUnit.test('Send DOMString data',function(assert){ 518 | var done = assert.async(); 519 | assert.expect(1); 520 | qwest.post('../tests/send_text/test.php','text',{dataType:'text'}) 521 | .then(function(xhr, response){ 522 | //console.log(response.debug); 523 | assert.ok(response.status=='ok'); 524 | done(); 525 | }) 526 | ['catch'](function(e, xhr, response){ 527 | assert.ok(false, e); 528 | done(); 529 | }); 530 | }); 531 | 532 | if('FormData' in global){ 533 | QUnit.test('Send FormData data',function(assert){ 534 | var done = assert.async(); 535 | assert.expect(1); 536 | var formData=new FormData(); 537 | formData.append('firstname','Pedro'); 538 | formData.append('lastname','Sanchez'); 539 | qwest.post('../tests/send_formdata/test.php',formData) 540 | .then(function(xhr, response){ 541 | //console.log(response.debug); 542 | assert.ok(response.status=='ok'); 543 | done(); 544 | }) 545 | ['catch'](function(e, xhr, response){ 546 | assert.ok(false, e); 547 | done(); 548 | }); 549 | }); 550 | } 551 | 552 | if('Blob' in global){ 553 | QUnit.test('Send Blob data',function(assert){ 554 | var done = assert.async(); 555 | assert.expect(1); 556 | var blob=new Blob(['test'],{type:'text/plain'}); 557 | qwest.post('../tests/send_blob/test.php',blob) 558 | .then(function(xhr, response){ 559 | //console.log(response.debug); 560 | assert.ok(response.status=='ok'); 561 | done(); 562 | }) 563 | ['catch'](function(e, xhr, response){ 564 | assert.ok(false, e); 565 | done(); 566 | }); 567 | }); 568 | } 569 | 570 | if(qwest.xhr2){ 571 | QUnit.test('Send Document data',function(assert){ 572 | var done = assert.async(); 573 | assert.expect(1); 574 | qwest.post('../tests/send_document/test.php',document) 575 | .then(function(xhr, response){ 576 | //console.log(response.debug); 577 | assert.ok(response.status=='ok'); 578 | done(); 579 | }) 580 | ['catch'](function(e, xhr, response){ 581 | assert.ok(false, e); 582 | done(); 583 | }); 584 | }); 585 | } 586 | 587 | if('ArrayBuffer' in global){ 588 | QUnit.test('Send ArrayBuffer data',function(assert){ 589 | var done = assert.async(); 590 | assert.expect(1); 591 | var arrayBuffer=new Uint8Array([1,2,3]); 592 | qwest.post('../tests/send_arraybuffer/test.php',arrayBuffer) 593 | .then(function(xhr, response){ 594 | //console.log(response.debug); 595 | assert.ok(response.status=='ok'); 596 | done(); 597 | }) 598 | ['catch'](function(e, xhr, response){ 599 | assert.ok(false, e); 600 | done(); 601 | }); 602 | }); 603 | } 604 | 605 | QUnit.start(); 606 | -------------------------------------------------------------------------------- /tests/tests/204/test.php: -------------------------------------------------------------------------------- 1 | $_SERVER['REQUEST_METHOD']==$_GET['method']?'ok':'error', 8 | 'debug' => array( 9 | 'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'], 10 | 'method' => $_GET['method'] 11 | ) 12 | )); -------------------------------------------------------------------------------- /tests/tests/auth/.htaccess: -------------------------------------------------------------------------------- 1 | AuthName "Authentication Required" 2 | AuthUserFile D:\Dev\JavaScript\qwest\tests\tests\auth\passwd 3 | AuthType Basic 4 | Require valid-user -------------------------------------------------------------------------------- /tests/tests/auth/passwd: -------------------------------------------------------------------------------- 1 | pyrsmk:$apr1$w5pQFUov$J.dubERvtL3HrTqYXCWeE0 2 | -------------------------------------------------------------------------------- /tests/tests/auth/test.php: -------------------------------------------------------------------------------- 1 | 'ok', 8 | 'debug' => new Stdclass 9 | )); -------------------------------------------------------------------------------- /tests/tests/base/test.php: -------------------------------------------------------------------------------- 1 | 'ok' 8 | )); 9 | -------------------------------------------------------------------------------- /tests/tests/before/test.php: -------------------------------------------------------------------------------- 1 | $headers['X-Running-Test']=='before'?'ok':'error', 10 | 'debug' => array( 11 | 'X-Running-Test' => $headers['X-Running-Test'] 12 | ) 13 | )); -------------------------------------------------------------------------------- /tests/tests/cache/test.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | test 6 | 7 | 8 | 9 |

ok

10 | 11 | -------------------------------------------------------------------------------- /tests/tests/get_json/test.php: -------------------------------------------------------------------------------- 1 | strpos($headers['Accept'],'application/json')===0?'ok':'error', 10 | 'debug' => array( 11 | 'Accept' => $headers['Accept'] 12 | ) 13 | )); -------------------------------------------------------------------------------- /tests/tests/get_text/test.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ok 5 | -------------------------------------------------------------------------------- /tests/tests/limit/test.php: -------------------------------------------------------------------------------- 1 | 'ok', 8 | 'debug' => new Stdclass 9 | )); -------------------------------------------------------------------------------- /tests/tests/promises/test.php: -------------------------------------------------------------------------------- 1 | 'ok', 11 | 'response' => $_SERVER['REQUEST_METHOD'] 12 | )); 13 | -------------------------------------------------------------------------------- /tests/tests/send_arraybuffer/test.php: -------------------------------------------------------------------------------- 1 | $_POST[0]==1 && $_POST[1]==2 && $_POST[2]==3?'ok':'error', 10 | 'debug' => array( 11 | '$_POST' => $_POST 12 | ) 13 | )); -------------------------------------------------------------------------------- /tests/tests/send_blob/test.php: -------------------------------------------------------------------------------- 1 | file_get_contents('php://input')=='test'?'ok':'error', 10 | 'debug' => array( 11 | 'php://input' => file_get_contents('php://input') 12 | ) 13 | )); -------------------------------------------------------------------------------- /tests/tests/send_document/test.php: -------------------------------------------------------------------------------- 1 | is_int(strpos(file_get_contents('php://input'),' array( 11 | 'php://input' => strpos(file_get_contents('php://input'),' ($_POST['firstname']=='Pedro' && $_POST['lastname']=='Sanchez')?'ok':'error', 10 | 'debug' => array( 11 | '$_POST' => $_POST, 12 | 'headers' => $headers 13 | ) 14 | )); -------------------------------------------------------------------------------- /tests/tests/send_json/test.php: -------------------------------------------------------------------------------- 1 | (strpos($headers['Content-Type'],'application/json')===0 && $data->foo=='bar' && $data->bar[0]->foo=='bar')?'ok':'error', 12 | 'debug' => array( 13 | 'Content-Type' => $headers['Content-Type'], 14 | 'php://input' => file_get_contents('php://input') 15 | ) 16 | )); -------------------------------------------------------------------------------- /tests/tests/send_post/test.php: -------------------------------------------------------------------------------- 1 | (strpos($headers['Content-Type'],'application/x-www-form-urlencoded')===0 && $_POST['foo']=='bar' && $_POST['bar'][0]['foo']=='bar')?'ok':'error', 10 | 'debug' => array( 11 | 'Content-Type' => $headers['Content-Type'], 12 | '$_POST' => $_POST 13 | ) 14 | )); -------------------------------------------------------------------------------- /tests/tests/send_text/test.php: -------------------------------------------------------------------------------- 1 | (strpos($headers['Content-Type'],'*/*')===0 && file_get_contents('php://input')=='text')?'ok':'error', 10 | 'debug' => array( 11 | 'Content-Type' => $headers['Content-Type'], 12 | 'php://input' => file_get_contents('php://input') 13 | ) 14 | )); -------------------------------------------------------------------------------- /tests/tests/sync/test.php: -------------------------------------------------------------------------------- 1 | $_SERVER['REQUEST_METHOD']==$_GET['method']?'ok':'error', 8 | 'debug' => array( 9 | 'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'], 10 | 'method' => $_GET['method'] 11 | ) 12 | )); -------------------------------------------------------------------------------- /tests/tests/timeout/test.php: -------------------------------------------------------------------------------- 1 | 'ok' 10 | )); --------------------------------------------------------------------------------