├── .travis.yml ├── test ├── tap-index.js ├── common-index.js └── index.js ├── index.js ├── History.md ├── Readme.md ├── License.md ├── package.json ├── decode.js └── encode.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.6 4 | - 0.8 5 | -------------------------------------------------------------------------------- /test/tap-index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("retape")(require("./index")) -------------------------------------------------------------------------------- /test/common-index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("test").run(require("./index")) -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.decode = exports.parse = require('./decode'); 4 | exports.encode = exports.stringify = require('./encode'); 5 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | # 0.2.0 / 2013-02-21 2 | 3 | - Refactor into function per-module idiomatic style. 4 | - Improved test coverage. 5 | 6 | # 0.1.0 / 2011-12-13 7 | 8 | - Minor project reorganization 9 | 10 | # 0.0.3 / 2011-04-16 11 | - Support for AMD module loaders 12 | 13 | # 0.0.2 / 2011-04-16 14 | 15 | - Ported unit tests 16 | - Removed functionality that depended on Buffers 17 | 18 | # 0.0.1 / 2011-04-15 19 | 20 | - Initial release 21 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # New location 2 | 3 | https://github.com/SpainTrain/querystring-es3 4 | 5 | # querystring 6 | 7 | [![Build Status](https://secure.travis-ci.org/mike-spainhower/querystring.png)](http://travis-ci.org/mike-spainhower/querystring) 8 | 9 | 10 | [![Browser support](http://ci.testling.com/mike-spainhower/querystring.png)](http://ci.testling.com/mike-spainhower/querystring) 11 | 12 | 13 | 14 | Node's querystring module for all engines. 15 | 16 | ## Install ## 17 | 18 | npm install querystring-es3 19 | 20 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2012 Irakli Gozalishvili. All rights reserved. 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to 5 | deal in the Software without restriction, including without limitation the 6 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | sell copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "querystring-es3", 3 | "id": "querystring-es3", 4 | "version": "0.2.1", 5 | "description": "Node's querystring module for all engines. (ES3 compat fork)", 6 | "keywords": [ "commonjs", "query", "querystring" ], 7 | "author": "Irakli Gozalishvili ", 8 | "repository": { 9 | "type": "git", 10 | "url": "git://github.com/mike-spainhower/querystring.git", 11 | "web": "https://github.com/mike-spainhower/querystring" 12 | }, 13 | "bugs": { 14 | "url": "http://github.com/mike-spainhower/querystring/issues/" 15 | }, 16 | "devDependencies": { 17 | "test": "~0.x.0", 18 | "phantomify": "~0.x.0", 19 | "retape": "~0.x.0", 20 | "tape": "~0.1.5" 21 | }, 22 | "engines": { 23 | "node": ">=0.4.x" 24 | }, 25 | "scripts": { 26 | "test": "npm run test-node && npm run test-browser && npm run test-tap", 27 | "test-browser": "node ./node_modules/phantomify/bin/cmd.js ./test/common-index.js", 28 | "test-node": "node ./test/common-index.js", 29 | "test-tap": "node ./test/tap-index.js" 30 | }, 31 | "testling": { 32 | "files": "test/tap-index.js", 33 | "browsers": { 34 | "iexplore": [ 35 | 9, 36 | 10 37 | ], 38 | "chrome": [ 39 | 16, 40 | 20, 41 | 25, 42 | "canary" 43 | ], 44 | "firefox": [ 45 | 10, 46 | 15, 47 | 16, 48 | 17, 49 | 18, 50 | "nightly" 51 | ], 52 | "safari": [ 53 | 5, 54 | 6 55 | ], 56 | "opera": [ 57 | 12 58 | ] 59 | } 60 | }, 61 | "licenses": [{ 62 | "type" : "MIT", 63 | "url" : "https://github.com/Gozala/enchain/License.md" 64 | }] 65 | } 66 | -------------------------------------------------------------------------------- /decode.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 'use strict'; 23 | 24 | // If obj.hasOwnProperty has been overridden, then calling 25 | // obj.hasOwnProperty(prop) will break. 26 | // See: https://github.com/joyent/node/issues/1707 27 | function hasOwnProperty(obj, prop) { 28 | return Object.prototype.hasOwnProperty.call(obj, prop); 29 | } 30 | 31 | module.exports = function(qs, sep, eq, options) { 32 | sep = sep || '&'; 33 | eq = eq || '='; 34 | var obj = {}; 35 | 36 | if (typeof qs !== 'string' || qs.length === 0) { 37 | return obj; 38 | } 39 | 40 | var regexp = /\+/g; 41 | qs = qs.split(sep); 42 | 43 | var maxKeys = 1000; 44 | if (options && typeof options.maxKeys === 'number') { 45 | maxKeys = options.maxKeys; 46 | } 47 | 48 | var len = qs.length; 49 | // maxKeys <= 0 means that we should not limit keys count 50 | if (maxKeys > 0 && len > maxKeys) { 51 | len = maxKeys; 52 | } 53 | 54 | for (var i = 0; i < len; ++i) { 55 | var x = qs[i].replace(regexp, '%20'), 56 | idx = x.indexOf(eq), 57 | kstr, vstr, k, v; 58 | 59 | if (idx >= 0) { 60 | kstr = x.substr(0, idx); 61 | vstr = x.substr(idx + 1); 62 | } else { 63 | kstr = x; 64 | vstr = ''; 65 | } 66 | 67 | k = decodeURIComponent(kstr); 68 | v = decodeURIComponent(vstr); 69 | 70 | if (!hasOwnProperty(obj, k)) { 71 | obj[k] = v; 72 | } else if (isArray(obj[k])) { 73 | obj[k].push(v); 74 | } else { 75 | obj[k] = [obj[k], v]; 76 | } 77 | } 78 | 79 | return obj; 80 | }; 81 | 82 | var isArray = Array.isArray || function (xs) { 83 | return Object.prototype.toString.call(xs) === '[object Array]'; 84 | }; 85 | -------------------------------------------------------------------------------- /encode.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 'use strict'; 23 | 24 | var stringifyPrimitive = function(v) { 25 | switch (typeof v) { 26 | case 'string': 27 | return v; 28 | 29 | case 'boolean': 30 | return v ? 'true' : 'false'; 31 | 32 | case 'number': 33 | return isFinite(v) ? v : ''; 34 | 35 | default: 36 | return ''; 37 | } 38 | }; 39 | 40 | module.exports = function(obj, sep, eq, name) { 41 | sep = sep || '&'; 42 | eq = eq || '='; 43 | if (obj === null) { 44 | obj = undefined; 45 | } 46 | 47 | if (typeof obj === 'object') { 48 | return map(objectKeys(obj), function(k) { 49 | var ks = encodeURIComponent(stringifyPrimitive(k)) + eq; 50 | if (isArray(obj[k])) { 51 | return map(obj[k], function(v) { 52 | return ks + encodeURIComponent(stringifyPrimitive(v)); 53 | }).join(sep); 54 | } else { 55 | return ks + encodeURIComponent(stringifyPrimitive(obj[k])); 56 | } 57 | }).join(sep); 58 | 59 | } 60 | 61 | if (!name) return ''; 62 | return encodeURIComponent(stringifyPrimitive(name)) + eq + 63 | encodeURIComponent(stringifyPrimitive(obj)); 64 | }; 65 | 66 | var isArray = Array.isArray || function (xs) { 67 | return Object.prototype.toString.call(xs) === '[object Array]'; 68 | }; 69 | 70 | function map (xs, f) { 71 | if (xs.map) return xs.map(f); 72 | var res = []; 73 | for (var i = 0; i < xs.length; i++) { 74 | res.push(f(xs[i], i)); 75 | } 76 | return res; 77 | } 78 | 79 | var objectKeys = Object.keys || function (obj) { 80 | var res = []; 81 | for (var key in obj) { 82 | if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key); 83 | } 84 | return res; 85 | }; 86 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | "use strict"; 23 | 24 | // test using assert 25 | var qs = require('../'); 26 | 27 | // folding block, commented to pass gjslint 28 | // {{{ 29 | // [ wonkyQS, canonicalQS, obj ] 30 | var qsTestCases = [ 31 | ['foo=918854443121279438895193', 32 | 'foo=918854443121279438895193', 33 | {'foo': '918854443121279438895193'}], 34 | ['foo=bar', 'foo=bar', {'foo': 'bar'}], 35 | ['foo=bar&foo=quux', 'foo=bar&foo=quux', {'foo': ['bar', 'quux']}], 36 | ['foo=1&bar=2', 'foo=1&bar=2', {'foo': '1', 'bar': '2'}], 37 | ['my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F', 38 | 'my%20weird%20field=q1!2%22\'w%245%267%2Fz8)%3F', 39 | {'my weird field': 'q1!2"\'w$5&7/z8)?' }], 40 | ['foo%3Dbaz=bar', 'foo%3Dbaz=bar', {'foo=baz': 'bar'}], 41 | ['foo=baz=bar', 'foo=baz%3Dbar', {'foo': 'baz=bar'}], 42 | ['str=foo&arr=1&arr=2&arr=3&somenull=&undef=', 43 | 'str=foo&arr=1&arr=2&arr=3&somenull=&undef=', 44 | { 'str': 'foo', 45 | 'arr': ['1', '2', '3'], 46 | 'somenull': '', 47 | 'undef': ''}], 48 | [' foo = bar ', '%20foo%20=%20bar%20', {' foo ': ' bar '}], 49 | // disable test that fails ['foo=%zx', 'foo=%25zx', {'foo': '%zx'}], 50 | ['foo=%EF%BF%BD', 'foo=%EF%BF%BD', {'foo': '\ufffd' }], 51 | // See: https://github.com/joyent/node/issues/1707 52 | ['hasOwnProperty=x&toString=foo&valueOf=bar&__defineGetter__=baz', 53 | 'hasOwnProperty=x&toString=foo&valueOf=bar&__defineGetter__=baz', 54 | { hasOwnProperty: 'x', 55 | toString: 'foo', 56 | valueOf: 'bar', 57 | __defineGetter__: 'baz' }], 58 | // See: https://github.com/joyent/node/issues/3058 59 | ['foo&bar=baz', 'foo=&bar=baz', { foo: '', bar: 'baz' }] 60 | ]; 61 | 62 | // [ wonkyQS, canonicalQS, obj ] 63 | var qsColonTestCases = [ 64 | ['foo:bar', 'foo:bar', {'foo': 'bar'}], 65 | ['foo:bar;foo:quux', 'foo:bar;foo:quux', {'foo': ['bar', 'quux']}], 66 | ['foo:1&bar:2;baz:quux', 67 | 'foo:1%26bar%3A2;baz:quux', 68 | {'foo': '1&bar:2', 'baz': 'quux'}], 69 | ['foo%3Abaz:bar', 'foo%3Abaz:bar', {'foo:baz': 'bar'}], 70 | ['foo:baz:bar', 'foo:baz%3Abar', {'foo': 'baz:bar'}] 71 | ]; 72 | 73 | // [wonkyObj, qs, canonicalObj] 74 | var extendedFunction = function() {}; 75 | extendedFunction.prototype = {a: 'b'}; 76 | var qsWeirdObjects = [ 77 | [{regexp: /./g}, 'regexp=', {'regexp': ''}], 78 | [{regexp: new RegExp('.', 'g')}, 'regexp=', {'regexp': ''}], 79 | [{fn: function() {}}, 'fn=', {'fn': ''}], 80 | [{fn: new Function('')}, 'fn=', {'fn': ''}], 81 | [{math: Math}, 'math=', {'math': ''}], 82 | [{e: extendedFunction}, 'e=', {'e': ''}], 83 | [{d: new Date()}, 'd=', {'d': ''}], 84 | [{d: Date}, 'd=', {'d': ''}], 85 | [{f: new Boolean(false), t: new Boolean(true)}, 'f=&t=', {'f': '', 't': ''}], 86 | [{f: false, t: true}, 'f=false&t=true', {'f': 'false', 't': 'true'}], 87 | [{n: null}, 'n=', {'n': ''}], 88 | [{nan: NaN}, 'nan=', {'nan': ''}], 89 | [{inf: Infinity}, 'inf=', {'inf': ''}] 90 | ]; 91 | // }}} 92 | 93 | var qsNoMungeTestCases = [ 94 | ['', {}], 95 | ['foo=bar&foo=baz', {'foo': ['bar', 'baz']}], 96 | ['blah=burp', {'blah': 'burp'}], 97 | ['gragh=1&gragh=3&goo=2', {'gragh': ['1', '3'], 'goo': '2'}], 98 | ['frappucino=muffin&goat%5B%5D=scone&pond=moose', 99 | {'frappucino': 'muffin', 'goat[]': 'scone', 'pond': 'moose'}], 100 | ['trololol=yes&lololo=no', {'trololol': 'yes', 'lololo': 'no'}] 101 | ]; 102 | 103 | exports['test basic'] = function(assert) { 104 | assert.strictEqual('918854443121279438895193', 105 | qs.parse('id=918854443121279438895193').id, 106 | 'prase id=918854443121279438895193'); 107 | }; 108 | 109 | exports['test that the canonical qs is parsed properly'] = function(assert) { 110 | qsTestCases.forEach(function(testCase) { 111 | assert.deepEqual(testCase[2], qs.parse(testCase[0]), 112 | 'parse ' + testCase[0]); 113 | }); 114 | }; 115 | 116 | 117 | exports['test that the colon test cases can do the same'] = function(assert) { 118 | qsColonTestCases.forEach(function(testCase) { 119 | assert.deepEqual(testCase[2], qs.parse(testCase[0], ';', ':'), 120 | 'parse ' + testCase[0] + ' -> ; :'); 121 | }); 122 | }; 123 | 124 | exports['test the weird objects, that they get parsed properly'] = function(assert) { 125 | qsWeirdObjects.forEach(function(testCase) { 126 | assert.deepEqual(testCase[2], qs.parse(testCase[1]), 127 | 'parse ' + testCase[1]); 128 | }); 129 | }; 130 | 131 | exports['test non munge test cases'] = function(assert) { 132 | qsNoMungeTestCases.forEach(function(testCase) { 133 | assert.deepEqual(testCase[0], qs.stringify(testCase[1], '&', '=', false), 134 | 'stringify ' + JSON.stringify(testCase[1]) + ' -> & ='); 135 | }); 136 | }; 137 | 138 | exports['test the nested qs-in-qs case'] = function(assert) { 139 | var f = qs.parse('a=b&q=x%3Dy%26y%3Dz'); 140 | f.q = qs.parse(f.q); 141 | assert.deepEqual(f, { a: 'b', q: { x: 'y', y: 'z' } }, 142 | 'parse a=b&q=x%3Dy%26y%3Dz'); 143 | }; 144 | 145 | exports['test nested in colon'] = function(assert) { 146 | var f = qs.parse('a:b;q:x%3Ay%3By%3Az', ';', ':'); 147 | f.q = qs.parse(f.q, ';', ':'); 148 | assert.deepEqual(f, { a: 'b', q: { x: 'y', y: 'z' } }, 149 | 'parse a:b;q:x%3Ay%3By%3Az -> ; :'); 150 | }; 151 | 152 | exports['test stringifying'] = function(assert) { 153 | qsTestCases.forEach(function(testCase) { 154 | assert.equal(testCase[1], qs.stringify(testCase[2]), 155 | 'stringify ' + JSON.stringify(testCase[2])); 156 | }); 157 | 158 | qsColonTestCases.forEach(function(testCase) { 159 | assert.equal(testCase[1], qs.stringify(testCase[2], ';', ':'), 160 | 'stringify ' + JSON.stringify(testCase[2]) + ' -> ; :'); 161 | }); 162 | 163 | qsWeirdObjects.forEach(function(testCase) { 164 | assert.equal(testCase[1], qs.stringify(testCase[0]), 165 | 'stringify ' + JSON.stringify(testCase[0])); 166 | }); 167 | }; 168 | 169 | exports['test stringifying nested'] = function(assert) { 170 | var f = qs.stringify({ 171 | a: 'b', 172 | q: qs.stringify({ 173 | x: 'y', 174 | y: 'z' 175 | }) 176 | }); 177 | assert.equal(f, 'a=b&q=x%3Dy%26y%3Dz', 178 | JSON.stringify({ 179 | a: 'b', 180 | 'qs.stringify -> q': { 181 | x: 'y', 182 | y: 'z' 183 | } 184 | })); 185 | 186 | var threw = false; 187 | try { qs.parse(undefined); } catch(error) { threw = true; } 188 | assert.ok(!threw, "does not throws on undefined"); 189 | }; 190 | 191 | exports['test nested in colon'] = function(assert) { 192 | var f = qs.stringify({ 193 | a: 'b', 194 | q: qs.stringify({ 195 | x: 'y', 196 | y: 'z' 197 | }, ';', ':') 198 | }, ';', ':'); 199 | assert.equal(f, 'a:b;q:x%3Ay%3By%3Az', 200 | 'stringify ' + JSON.stringify({ 201 | a: 'b', 202 | 'qs.stringify -> q': { 203 | x: 'y', 204 | y: 'z' 205 | } 206 | }) + ' -> ; : '); 207 | 208 | 209 | assert.deepEqual({}, qs.parse(), 'parse undefined'); 210 | }; 211 | --------------------------------------------------------------------------------