├── .gitignore ├── .travis.yml ├── .zuul.yml ├── History.md ├── Readme.md ├── bower.json ├── component.json ├── index.js ├── package.json └── test └── basic.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | env: 5 | global: 6 | - secure: UOzXsaDRo/x7DYmfkhWHCSMf42tqJY7kuL+YcId8kK84Ffn6UN3FVHQBzIE4Ri7zH3h83lGrjkp8FZeONovRXWC/2HF5idI3FwKDiAFhpRkJvXDH72CcmLr9atMV66WVUWLWUUWbMRL8TNRk5Hhgm/y70fcy+jTYInUyX4X+lqA= 7 | - secure: R0xq3sNFDJRI08/25Vz0cJ14rEo6U/aL3D0J8QkoBb21ZQORqzTL6/3WR3tSnh+X+VKELpjS5L0G4qVgJmFQcQkyBFz+rTkcJHkE/IjO9EMV+P3EAetXirTaHBahcg33i3kOAJl4qCi9b/Y4c3atkt+ooGz3Hc67IEkHddYqp/E= 8 | -------------------------------------------------------------------------------- /.zuul.yml: -------------------------------------------------------------------------------- 1 | ui: tape 2 | browsers: 3 | - name: chrome 4 | version: 26..latest 5 | - name: firefox 6 | version: 21..latest 7 | - name: safari 8 | version: 5..latest 9 | - name: ie 10 | version: 6..latest 11 | - name: opera 12 | version: 11..latest 13 | - name: iphone 14 | version: 4.3..latest 15 | - name: ipad 16 | version: 4.3..latest 17 | - name: android 18 | version: 4.0..latest 19 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 0.2.1 / 2016-10-31 3 | ================== 4 | 5 | * loose debug version (#21, @roderickhsiao) 6 | * component: update "repo" field 7 | 8 | 0.2.0 / 2015-03-18 9 | ================== 10 | 11 | * add `bower.json` 12 | * add travis/zuul/saucelabs testing 13 | * add support for custom callback `name` 14 | * pin `debug` dep 15 | 16 | 0.1.0 / 2014-12-30 17 | ================== 18 | 19 | * Make sure `script.parentNode` exists before removing the script element (#12, @azer) 20 | * Return function to cancel in-progress jsonp request (#11, @feross) 21 | * add documentation for `prefix` option (#10, @feross) 22 | 23 | 0.0.4 / 2014-03-11 24 | ================== 25 | 26 | * simplify the variable name "id" creation logic 27 | * Readme: add Installation notes 28 | * fix jshint warnings 29 | * add repository field to `package.json` 30 | * fallback to `document.head` if there is not any script tags in the dom 31 | * remove script safely 32 | 33 | 0.0.3 / 2013-02-03 34 | ================== 35 | 36 | * use `index.js` 37 | * go back to integer ids 38 | * honor `param` option 39 | 40 | 0.0.2 / 2013-02-03 41 | ================== 42 | 43 | * make IE<=8 happy 44 | 45 | 0.0.1 / 2012-07-03 46 | ================== 47 | 48 | * Initial release. 49 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # jsonp 3 | 4 | A simple JSONP implementation. 5 | 6 | [![saucelabs][saucelabs-image]][saucelabs-url] 7 | 8 | ## Installation 9 | 10 | Install for node.js or browserify using `npm`: 11 | 12 | ``` bash 13 | $ npm install jsonp 14 | ``` 15 | 16 | Install for component(1) using `component`: 17 | 18 | ``` bash 19 | $ component install LearnBoost/jsonp 20 | ``` 21 | 22 | Install for browser using `bower`: 23 | 24 | ``` bash 25 | $ bower install jsonp 26 | ``` 27 | 28 | ## API 29 | 30 | ### jsonp(url, opts, fn) 31 | 32 | - `url` (`String`) url to fetch 33 | - `opts` (`Object`), optional 34 | - `param` (`String`) name of the query string parameter to specify 35 | the callback (defaults to `callback`) 36 | - `timeout` (`Number`) how long after a timeout error is emitted. `0` to 37 | disable (defaults to `60000`) 38 | - `prefix` (`String`) prefix for the global callback functions that 39 | handle jsonp responses (defaults to `__jp`) 40 | - `name` (`String`) name of the global callback functions that 41 | handle jsonp responses (defaults to `prefix` + incremented counter) 42 | - `fn` callback 43 | 44 | The callback is called with `err, data` parameters. 45 | 46 | If it times out, the `err` will be an `Error` object whose `message` is 47 | `Timeout`. 48 | 49 | Returns a function that, when called, will cancel the in-progress jsonp request 50 | (`fn` won't be called). 51 | 52 | ## License 53 | 54 | MIT 55 | 56 | [saucelabs-image]: https://saucelabs.com/browser-matrix/jsonp.svg 57 | [saucelabs-url]: https://saucelabs.com/u/jsonp 58 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsonp", 3 | "main": "index.js", 4 | "version": "0.2.1", 5 | "homepage": "https://github.com/webmodules/jsonp", 6 | "description": "A sane JSONP implementation.", 7 | "keywords": [ 8 | "jsonp" 9 | ], 10 | "license": "MIT", 11 | "ignore": [ 12 | "**/.*", 13 | "node_modules", 14 | "bower_components", 15 | "test", 16 | "tests" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsonp", 3 | "repo": "webmodules/jsonp", 4 | "description": "A sane JSONP implementation.", 5 | "version": "0.2.1", 6 | "dependencies": { 7 | "visionmedia/debug": "*" 8 | }, 9 | "scripts": [ 10 | "index.js" 11 | ], 12 | "main": "index.js" 13 | } 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies 3 | */ 4 | 5 | var debug = require('debug')('jsonp'); 6 | 7 | /** 8 | * Module exports. 9 | */ 10 | 11 | module.exports = jsonp; 12 | 13 | /** 14 | * Callback index. 15 | */ 16 | 17 | var count = 0; 18 | 19 | /** 20 | * Noop function. 21 | */ 22 | 23 | function noop(){} 24 | 25 | /** 26 | * JSONP handler 27 | * 28 | * Options: 29 | * - param {String} qs parameter (`callback`) 30 | * - prefix {String} qs parameter (`__jp`) 31 | * - name {String} qs parameter (`prefix` + incr) 32 | * - timeout {Number} how long after a timeout error is emitted (`60000`) 33 | * 34 | * @param {String} url 35 | * @param {Object|Function} optional options / callback 36 | * @param {Function} optional callback 37 | */ 38 | 39 | function jsonp(url, opts, fn){ 40 | if ('function' == typeof opts) { 41 | fn = opts; 42 | opts = {}; 43 | } 44 | if (!opts) opts = {}; 45 | 46 | var prefix = opts.prefix || '__jp'; 47 | 48 | // use the callback name that was passed if one was provided. 49 | // otherwise generate a unique name by incrementing our counter. 50 | var id = opts.name || (prefix + (count++)); 51 | 52 | var param = opts.param || 'callback'; 53 | var timeout = null != opts.timeout ? opts.timeout : 60000; 54 | var enc = encodeURIComponent; 55 | var target = document.getElementsByTagName('script')[0] || document.head; 56 | var script; 57 | var timer; 58 | 59 | 60 | if (timeout) { 61 | timer = setTimeout(function(){ 62 | cleanup(); 63 | if (fn) fn(new Error('Timeout')); 64 | }, timeout); 65 | } 66 | 67 | function cleanup(){ 68 | if (script.parentNode) script.parentNode.removeChild(script); 69 | window[id] = noop; 70 | if (timer) clearTimeout(timer); 71 | } 72 | 73 | function cancel(){ 74 | if (window[id]) { 75 | cleanup(); 76 | } 77 | } 78 | 79 | window[id] = function(data){ 80 | debug('jsonp got', data); 81 | cleanup(); 82 | if (fn) fn(null, data); 83 | }; 84 | 85 | // add qs component 86 | url += (~url.indexOf('?') ? '&' : '?') + param + '=' + enc(id); 87 | url = url.replace('?&', '?'); 88 | 89 | debug('jsonp req "%s"', url); 90 | 91 | // create script 92 | script = document.createElement('script'); 93 | script.src = url; 94 | target.parentNode.insertBefore(script, target); 95 | 96 | return cancel; 97 | } 98 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsonp", 3 | "description": "A sane JSONP implementation.", 4 | "version": "0.2.1", 5 | "dependencies": { 6 | "debug": "^2.1.3" 7 | }, 8 | "component": { 9 | "scripts": { 10 | "jsonp": "jsonp.js" 11 | } 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/LearnBoost/jsonp.git" 16 | }, 17 | "devDependencies": { 18 | "tape": "^3.5.0", 19 | "zuul": "^1.17.1" 20 | }, 21 | "scripts": { 22 | "test": "zuul -- test/*.js", 23 | "test-local": "zuul --local -- test/*.js" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | var jsonp = require('../'); 2 | var querystring = require('querystring'); 3 | var test = require('tape'); 4 | 5 | // See http://doc.jsfiddle.net/use/echo.html 6 | var ENDPOINT = 'http://jsfiddle.net/echo/jsonp/'; 7 | 8 | test('basic jsonp', function (t) { 9 | t.plan(1); 10 | var obj = { 11 | beep: 'boop', 12 | yo: 'dawg' 13 | }; 14 | var q = querystring.encode(obj); 15 | jsonp(ENDPOINT + '?' + q, function (err, data) { 16 | if (err) throw err; 17 | t.deepEqual(data, obj); 18 | }); 19 | }); 20 | 21 | test('timeout', function (t) { 22 | t.plan(1); 23 | var obj = { 24 | delay: 5 // time in seconds after which data should be returned 25 | }; 26 | var q = querystring.encode(obj); 27 | jsonp(ENDPOINT + '?' + q, { timeout: 3000 }, function (err, data) { 28 | t.ok(err instanceof Error); 29 | }); 30 | }); 31 | 32 | test('named callback', function (t) { 33 | t.plan(1); 34 | var obj = { 35 | beep: 'boop', 36 | yo: 'dawg' 37 | }; 38 | var q = querystring.encode(obj); 39 | jsonp(ENDPOINT + '?' + q, { name: 'namedCb' }, function (err, data) { 40 | if (err) throw err; 41 | t.deepEqual(data, obj); 42 | }); 43 | }); 44 | --------------------------------------------------------------------------------