public
Last active

jQuery Deparam -- WORK IN PROGRESS -- NOT DONE YET

  • Download Gist
jquery.ba-deparam.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
// jQuery Deparam - v0.1.0 - 6/14/2011
// http://benalman.com/
// Copyright (c) 2011 Ben Alman; Licensed MIT, GPL
 
(function($) {
// Creating an internal undef value is safer than using undefined, in case it
// was ever overwritten.
var undef;
// A handy reference.
var decode = decodeURIComponent;
 
// Document $.deparam.
var deparam = $.deparam = function(text, reviver) {
// The object to be returned.
var result = {};
// Iterate over all key=value pairs.
$.each(text.replace(/\+/g, ' ').split('&'), function(index, pair) {
// The key=value pair.
var kv = pair.split('=');
// The key, URI-decoded.
var key = decode(kv[0]);
// Abort if there's no key.
if ( !key ) { return; }
// The value, URI-decoded. If value is missing, use empty string.
var value = decode(kv[1] || '');
// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
// into its component parts.
var keys = key.split('][');
var last = keys.length - 1;
// Used when key is complex.
var i = 0;
var current = result;
 
// If the first keys part contains [ and the last ends with ], then []
// are correctly balanced.
if ( keys[0].indexOf('[') >= 0 && /\]$/.test(keys[last]) ) {
// Remove the trailing ] from the last keys part.
keys[last] = keys[last].replace(/\]$/, '');
// Split first keys part into two parts on the [ and add them back onto
// the beginning of the keys array.
keys = keys.shift().split('[').concat(keys);
// Since a key part was added, increment last.
last++;
} else {
// Basic 'foo' style key.
last = 0;
}
 
if ( $.isFunction(reviver) ) {
// If a reviver function was passed, use that function.
value = reviver(key, value);
} else if ( reviver ) {
// If true was passed, use the built-in $.deparam.reviver function.
value = deparam.reviver(key, value);
}
 
if ( last ) {
// Complex key, like 'a[]' or 'a[b][c]'. At this point, the keys array
// might look like ['a', ''] (array) or ['a', 'b', 'c'] (object).
for ( ; i <= last; i++ ) {
// If the current key part was specified, use that value as the array
// index or object key. If omitted, assume an array and use the
// array's length (effectively an array push).
key = keys[i] !== '' ? keys[i] : current.length;
if ( i < last ) {
// If not the last key part, update the reference to the current
// object/array, creating it if it doesn't already exist AND there's
// a next key. If the next key is non-numeric and not empty string,
// create an object, otherwise create an array.
current = current[key] = current[key] || (isNaN(keys[i + 1]) ? {} : []);
} else {
// If the last key part, set the value.
current[key] = value;
}
}
} else {
// Simple key.
if ( $.isArray(result[key]) ) {
// If the key already exists, and is an array, push the new value onto
// the array.
result[key].push(value);
} else if ( key in result ) {
// If the key already exists, and is NOT an array, turn it into an
// array, pushing the new value onto it.
result[key] = [result[key], value];
} else {
// Otherwise, just set the value.
result[key] = value;
}
}
});
 
return result;
};
 
// Default reviver function, used when true is passed as the second argument
// to $.deparam. Don't like it? Pass your own!
deparam.reviver = function(key, value) {
var specials = {
'true': true,
'false': false,
'null': null,
'undefined': undef
};
 
return (+value + '') === value ? +value // Number
: value in specials ? specials[value] // true, false, null, undefined
: value; // String
};
 
}(jQuery));
unit.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
module('jQuery.deparam');
 
test('basics', function() {
same($.deparam(''), {}, 'empty string yields empty object');
same($.deparam('a=1&b=2&c=3'), {a: '1', b: '2', c: '3'}, 'all params must exist in the result');
});
 
test('arrays', function() {
same($.deparam('a=1&a=2&a=3'), {a: ['1', '2', '3']}, 'implicit push');
same($.deparam('a[]=1&a[]=2&a[]=3'), {a: ['1', '2', '3']}, 'explicit push');
same($.deparam('a[0]=1&a[1]=2&a[2]=3'), {a: ['1', '2', '3']}, 'explicit by index');
same($.deparam('a[0]=1&a[2]=2'), {a: ['1', undefined, '2']}, 'explicit by index, skipping indices');
same($.deparam('a[][]=1'), {a: [['1']]}, 'basic nested array');
same($.deparam('a[][]=1&a[][]=2&a[][]=3'), {a: [['1'], ['2'], ['3']]}, 'nested arrays w/o indices is ambiguous');
same($.deparam('a[0][]=1&a[1][]=2&a[2][]=3'), {a: [['1'], ['2'], ['3']]}, 'nested arrays w/indices is not ambiguous');
same($.deparam('a[0][]=1&a[0][]=2&a[0][]=3'), {a: [['1', '2', '3']]}, 'nested arrays w/indices is not ambiguous');
same($.deparam('a[0][]=1&a[0][][]=0&a[1][][]=0&a[1][]=1&a[2][][]=0&a[2][]=1&a[2][][]=0&a[3][][][]=0'), {a: [['1',['0']], [['0'],'1'], [['0'],'1',['0']], [[['0']]]]}, 'a few permutations');
});
 
test('objects', function() {
same($.deparam('a[b]=1'), {a: {b: '1'}}, 'basic nested object');
same($.deparam('a[b]=1&a[c]=2'), {a: {b: '1', c: '2'}}, 'nested object with multiple params');
same($.deparam('a[b][c]=1'), {a: {b: {c: '1'}}}, 'deep nesting');
});
 
test('objects and arrays', function() {
same($.deparam('a[b][]=1&a[b][]=2'), {a: {b: ['1', '2']}}, 'nested object with implicit array push');
same($.deparam('a[][b]=1&a[][c]=2'), {a: [{b: '1'}, {c: '2'}]}, 'implicit array push with nested object');
same($.deparam('a[b][0]=1&a[b][2]=2'), {a: {b: ['1', undefined, '2']}}, 'nested object with explicit array indices, skipping indices');
same($.deparam('a[b][0][]=1&a[b][0][]=2'), {a: {b: [['1', '2']]}}, 'nested object with explicit and implicit array indices');
});
 
test('reviver: none', function() {
same($.deparam('a=foo'), {a: 'foo'}, 'value should be string');
same($.deparam('a='), {a: ''}, 'value should be string');
same($.deparam('a=01234'), {a: '01234'}, 'value should be string');
same($.deparam('a=0x10'), {a: '0x10'}, 'value should be string');
same($.deparam('a=1e3'), {a: '1e3'}, 'value should be string');
same($.deparam('a=-0'), {a: '-0'}, 'value should be string');
same($.deparam('a=true'), {a: 'true'}, 'value should be string');
same($.deparam('a=false'), {a: 'false'}, 'value should be string');
same($.deparam('a=null'), {a: 'null'}, 'value should be string');
same($.deparam('a=undefined'), {a: 'undefined'}, 'value should be string');
same($.deparam('a=123'), {a: '123'}, 'value should be string');
same($.deparam('a=-4.56'), {a: '-4.56'}, 'value should be string');
same($.deparam('a=0.001'), {a: '0.001'}, 'value should be string');
same($.deparam('a=0'), {a: '0'}, 'value should be string');
});
 
test('reviver: true', function() {
same($.deparam('a=foo', true), {a: 'foo'}, 'value should be string');
same($.deparam('a=', true), {a: ''}, 'value should be string');
same($.deparam('a=01234', true), {a: '01234'}, 'value should be string');
same($.deparam('a=0x10', true), {a: '0x10'}, 'value should be string');
same($.deparam('a=1e3', true), {a: '1e3'}, 'value should be string');
same($.deparam('a=-0', true), {a: '-0'}, 'value should be string');
same($.deparam('a=true', true), {a: true}, 'value should be boolean');
same($.deparam('a=false', true), {a: false}, 'value should be boolean');
same($.deparam('a=null', true), {a: null}, 'value should be null');
same($.deparam('a=undefined', true), {a: undefined}, 'value should be undefined');
same($.deparam('a=123', true), {a: 123}, 'value should be number');
same($.deparam('a=-4.56', true), {a: -4.56}, 'value should be number');
same($.deparam('a=0.001', true), {a: 0.001}, 'value should be number');
same($.deparam('a=0', true), {a: 0}, 'value should be number');
})
 
test('reviver: custom function', function() {
function fn(key, value) {
return '(' + key + '=' + value + ')';
}
same($.deparam('a=foo', fn), {a: '(a=foo)'}, 'value should be "fancy"');
same($.deparam('a=', fn), {a: '(a=)'}, 'value should be "fancy"');
});
 
test('jQuery.deparam.reviver', function() {
// \$\.deparam\('a=(.*?)', true\), \{a: (.*?)\}
// $.deparam.reviver('a', '$1'), $2
same($.deparam.reviver('a', 'foo'), 'foo', 'value should be string');
same($.deparam.reviver('a', ''), '', 'value should be string');
same($.deparam.reviver('a', '01234'), '01234', 'value should be string');
same($.deparam.reviver('a', '0x10'), '0x10', 'value should be string');
same($.deparam.reviver('a', '1e3'), '1e3', 'value should be string');
same($.deparam.reviver('a', '-0'), '-0', 'value should be string');
same($.deparam.reviver('a', 'true'), true, 'value should be boolean');
same($.deparam.reviver('a', 'false'), false, 'value should be boolean');
same($.deparam.reviver('a', 'null'), null, 'value should be null');
same($.deparam.reviver('a', 'undefined'), undefined, 'value should be undefined');
same($.deparam.reviver('a', '123'), 123, 'value should be number');
same($.deparam.reviver('a', '-4.56'), -4.56, 'value should be number');
same($.deparam.reviver('a', '0.001'), 0.001, 'value should be number');
same($.deparam.reviver('a', '0'), 0, 'value should be number');
});
 
test('param', function() {
var obj = {
a: 'foo',
b: '',
c: '01234',
d: '0x10',
e: '1e3',
f: '-0',
g: true,
h: false,
i: null,
j: undefined,
k: 123,
l: -4.56,
m: 0.001,
n: 0,
o: {b: 2, a: 1},
p: [[null], 'foo', '', '01234', '0x10', '1e3', '-0', true, false, null, undefined, 123, -4.56, 0.001, 0, {b: 2, a: 1}, [undefined]],
q: [[1,[0]], [[0],1], [[0],1,[0]], [[[0]]]],
z: {
a: 'foo',
b: '',
c: '01234',
d: '0x10',
e: '1e3',
f: '-0',
g: true,
h: false,
i: null,
j: undefined,
k: 123,
l: -4.56,
m: 0.001,
n: 0,
o: {b: 2, a: 1},
p: [[null], 'foo', '', '01234', '0x10', '1e3', '-0', true, false, null, undefined, 123, -4.56, 0.001, 0, {b: 2, a: 1}, [undefined]],
q: [[1,[0]], [[0],1], [[0],1,[0]], [[[0]]]]
}
};
same($.deparam($.param(obj), true), obj, 'deparam should be able to return $.param to the original object');
});

note to self: look into adding something like value.replace(/\+/g, ' ') in reviver function to replace + with space by default

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.