Created
June 14, 2011 20:39
-
-
Save cowboy/1025817 to your computer and use it in GitHub Desktop.
jQuery Deparam -- WORK IN PROGRESS -- NOT DONE YET
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// jQuery Deparam - v0.1.0 - 6/14/2011 | |
// http://benalman.com/ | |
// Copyright (c) 2011 Ben Alman; Licensed MIT, GPL | |
(function($){var a,b=decodeURIComponent,c=$.deparam=function(a,d){var e={};$.each(a.replace(/\+/g," ").split("&"),function(a,f){var g=f.split("="),h=b(g[0]);if(!!h){var i=b(g[1]||""),j=h.split("]["),k=j.length-1,l=0,m=e;j[0].indexOf("[")>=0&&/\]$/.test(j[k])?(j[k]=j[k].replace(/\]$/,""),j=j.shift().split("[").concat(j),k++):k=0,$.isFunction(d)?i=d(h,i):d&&(i=c.reviver(h,i));if(k)for(;l<=k;l++)h=j[l]!==""?j[l]:m.length,l<k?m=m[h]=m[h]||(isNaN(j[l+1])?{}:[]):m[h]=i;else $.isArray(e[h])?e[h].push(i):h in e?e[h]=[e[h],i]:e[h]=i}});return e};c.reviver=function(b,c){var d={"true":!0,"false":!1,"null":null,"undefined":a};return+c+""===c?+c:c in d?d[c]:c}})(jQuery) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
note to self: look into adding something like
value.replace(/\+/g, ' ')
in reviver function to replace+
with space by default