form-serialize
Advanced tools
Comparing version 0.6.0 to 0.7.0
@@ -0,1 +1,5 @@ | ||
# 0.7.0 (2015-10-17) | ||
* add bracket notation support for hash serialization | ||
# 0.6.0 (2015-02-23) | ||
@@ -2,0 +6,0 @@ |
201
index.js
@@ -11,6 +11,4 @@ // get successful control from form and assemble into object | ||
// keys with brackets for hash keys | ||
var object_brackets_regex = /\[(.+?)\]/g; | ||
var array_brackets_regex = /\[\]$/; | ||
var brackeks_prefix_regex = /^(.+?)\[/; | ||
// Matches bracket notation. | ||
var brackets = /(\[[^\[\]]*\])/g; | ||
@@ -39,3 +37,3 @@ // serializes form fields | ||
var elements = form.elements || []; | ||
var elements = form && form.elements ? form.elements : []; | ||
@@ -66,3 +64,3 @@ //Object store each radio and set if it's empty or not | ||
} | ||
// If we want empty elements | ||
@@ -78,6 +76,6 @@ if (options.empty) { | ||
if (!radio_store[element.name] && !element.checked) { | ||
radio_store[element.name] = false | ||
radio_store[element.name] = false; | ||
} | ||
else if (element.checked) { | ||
radio_store[element.name] = true | ||
radio_store[element.name] = true; | ||
} | ||
@@ -106,5 +104,18 @@ } | ||
var option = selectOptions[j]; | ||
if (option.selected) { | ||
isSelectedOptions = true | ||
result = serializer(result, key, option.value); | ||
var allowedEmpty = options.empty && !option.value; | ||
var hasValue = (option.value || allowedEmpty); | ||
if (option.selected && hasValue) { | ||
isSelectedOptions = true; | ||
// If using a hash serializer be sure to add the | ||
// correct notation for an array in the multi-select | ||
// context. Here the name attribute on the select element | ||
// might be missing the trailing bracket pair. Both names | ||
// "foo" and "foo[]" should be arrays. | ||
if (options.hash && key.slice(key.length - 2) !== '[]') { | ||
result = serializer(result, key + '[]', option.value); | ||
} | ||
else { | ||
result = serializer(result, key, option.value); | ||
} | ||
} | ||
@@ -117,5 +128,6 @@ } | ||
} | ||
continue; | ||
} | ||
result = serializer(result, key, val); | ||
@@ -136,22 +148,64 @@ } | ||
// obj/hash encoding serializer | ||
function hash_serializer(result, key, value) { | ||
var is_array_key = has_array_brackets(key); | ||
if (is_array_key) { | ||
key = key.replace(array_brackets_regex, ''); | ||
function parse_keys(string) { | ||
var keys = []; | ||
var prefix = /^([^\[\]]*)/; | ||
var children = new RegExp(brackets); | ||
var match = prefix.exec(string); | ||
if (match[1]) { | ||
keys.push(match[1]); | ||
} | ||
if (key in result) { | ||
var existing = result[key]; | ||
if (!Array.isArray(existing)) { | ||
result[key] = [existing]; | ||
while ((match = children.exec(string)) !== null) { | ||
keys.push(match[1]); | ||
} | ||
return keys; | ||
} | ||
function hash_assign(result, keys, value) { | ||
if (keys.length === 0) { | ||
result = value; | ||
return result; | ||
} | ||
var key = keys.shift(); | ||
var between = key.match(/^\[(.+?)\]$/); | ||
if (key === '[]') { | ||
result = result || []; | ||
if (Array.isArray(result)) { | ||
result.push(hash_assign(null, keys, value)); | ||
} | ||
result[key].push(value); | ||
else { | ||
// This might be the result of bad name attributes like "[][foo]", | ||
// in this case the original `result` object will already be | ||
// assigned to an object literal. Rather than coerce the object to | ||
// an array, or cause an exception the attribute "_values" is | ||
// assigned as an array. | ||
result._values = result._values || []; | ||
result._values.push(hash_assign(null, keys, value)); | ||
} | ||
return result; | ||
} | ||
// Key is an attribute name and can be assigned directly. | ||
if (!between) { | ||
result[key] = hash_assign(result[key], keys, value); | ||
} | ||
else { | ||
if (has_object_brackets(key)) { | ||
extract_from_brackets(result, key, value); | ||
var string = between[1]; | ||
var index = parseInt(string, 10); | ||
// If the characters between the brackets is not a number it is an | ||
// attribute name and can be assigned directly. | ||
if (isNaN(index)) { | ||
result = result || {}; | ||
result[string] = hash_assign(result[string], keys, value); | ||
} | ||
else { | ||
result[key] = is_array_key ? [value] : value; | ||
result = result || []; | ||
result[index] = hash_assign(result[index], keys, value); | ||
} | ||
@@ -161,76 +215,51 @@ } | ||
return result; | ||
}; | ||
// urlform encoding serializer | ||
function str_serialize(result, key, value) { | ||
// encode newlines as \r\n cause the html spec says so | ||
value = value.replace(/(\r)?\n/g, '\r\n'); | ||
value = encodeURIComponent(value); | ||
// spaces should be '+' rather than '%20'. | ||
value = value.replace(/%20/g, '+'); | ||
return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + value; | ||
}; | ||
function has_object_brackets(string) { | ||
return string.match(object_brackets_regex); | ||
}; | ||
function has_array_brackets(string) { | ||
return string.match(array_brackets_regex); | ||
} | ||
function matches_between_brackets(string) { | ||
// Make sure to isolate object_brackets_regex from .exec() calls | ||
var regex = new RegExp(object_brackets_regex); | ||
var matches = []; | ||
var match; | ||
// Object/hash encoding serializer. | ||
function hash_serializer(result, key, value) { | ||
var matches = key.match(brackets); | ||
while (match = regex.exec(string)) { | ||
matches.push(match[1]); | ||
// Has brackets? Use the recursive assignment function to walk the keys, | ||
// construct any missing objects in the result tree and make the assignment | ||
// at the end of the chain. | ||
if (matches) { | ||
var keys = parse_keys(key); | ||
hash_assign(result, keys, value); | ||
} | ||
else { | ||
// Non bracket notation can make assignments directly. | ||
var existing = result[key]; | ||
return matches; | ||
}; | ||
function extract_from_brackets(result, key, value) { | ||
var prefix = key.match(brackeks_prefix_regex)[1]; | ||
// Set the key if it doesn't exist | ||
if (! result[prefix]) result[prefix] = {}; | ||
var parent = result[prefix]; | ||
var matches_between = matches_between_brackets(key); | ||
var length = matches_between.length; | ||
for (var i = 0; i < length; i++) { | ||
var child = matches_between[i]; | ||
var isLast = (length === i + 1); | ||
if (isLast) { | ||
var existing = parent[child]; | ||
if (existing) { | ||
if (! Array.isArray(existing)) { | ||
parent[child] = [ existing ]; | ||
} | ||
parent[child].push(value); | ||
// If the value has been assigned already (for instance when a radio and | ||
// a checkbox have the same name attribute) convert the previous value | ||
// into an array before pushing into it. | ||
// | ||
// NOTE: If this requirement were removed all hash creation and | ||
// assignment could go through `hash_assign`. | ||
if (existing) { | ||
if (!Array.isArray(existing)) { | ||
result[key] = [ existing ]; | ||
} | ||
else { | ||
// Finally make the assignment | ||
parent[child] = value; | ||
} | ||
result[key].push(value); | ||
} | ||
else { | ||
// This is a nested key, set it properly for the next iteration | ||
parent[child] = parent[child] || {}; | ||
parent = parent[child]; | ||
result[key] = value; | ||
} | ||
} | ||
parent = value; | ||
}; | ||
return result; | ||
} | ||
// urlform encoding serializer | ||
function str_serialize(result, key, value) { | ||
// encode newlines as \r\n cause the html spec says so | ||
value = value.replace(/(\r)?\n/g, '\r\n'); | ||
value = encodeURIComponent(value); | ||
// spaces should be '+' rather than '%20'. | ||
value = value.replace(/%20/g, '+'); | ||
return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + value; | ||
} | ||
module.exports = serialize; |
{ | ||
"name": "form-serialize", | ||
"version": "0.6.0", | ||
"version": "0.7.0", | ||
"description": "serialize html forms", | ||
@@ -11,3 +11,3 @@ "main": "index.js", | ||
"domify": "~1.0.0", | ||
"zuul": "~1.15.1" | ||
"zuul": "~3.6.0" | ||
}, | ||
@@ -14,0 +14,0 @@ "scripts": { |
@@ -93,2 +93,50 @@ # form-serialize [![Build Status](https://travis-ci.org/defunctzombie/form-serialize.png?branch=master)](https://travis-ci.org/defunctzombie/form-serialize) | ||
### indexed arrays | ||
Adding numbers between brackets for the array notation above will result in a hash serialization with explicit ordering based on the index number regardless of element ordering. | ||
Like the "[explicit array fields](explicit-array-fields)" this does not affect url-encoding mode output in any way. | ||
```html | ||
<form id="todos-form"> | ||
<input type="text" name="todos[1]" value="milk" /> | ||
<input type="text" name="todos[0]" value="eggs" /> | ||
<input type="text" name="todos[2]" value="flour" /> | ||
</form> | ||
``` | ||
```js | ||
var serialize = require('form-serialize'); | ||
var form = document.querySelector('#todos-form'); | ||
var obj = serialize(form, { hash: true }); | ||
// obj -> { todos: ['eggs', 'milk', 'flour'] } | ||
var str = serialize(form); | ||
// str -> "todos[1]=milk&todos[0]=eggs&todos[2]=flour" | ||
``` | ||
### nested objects | ||
Similar to the indexed array notation, attribute names can be added by inserting a string value between brackets. The notation can be used to create deep objects and mixed with the array notation. | ||
Like the "[explicit array fields](explicit-array-fields)" this does not affect url-encoding mode output. | ||
```html | ||
<form id="nested-example"> | ||
<input type="text" name="foo[bar][baz]" value="qux" /> | ||
<input type="text" name="foo[norf][]" value="item 1" /> | ||
</form> | ||
``` | ||
```js | ||
var serialize = require('form-serialize'); | ||
var form = document.querySelector('#todos-form'); | ||
var obj = serialize(form, { hash: true }); | ||
// obj -> { foo: { bar: { baz: 'qux' } }, norf: [ 'item 1' ] } | ||
``` | ||
## references | ||
@@ -95,0 +143,0 @@ |
@@ -26,3 +26,18 @@ var assert = require('assert'); | ||
test('nothing', function() { | ||
test('null form', function() { | ||
hash_check(null, {}); | ||
str_check(null, ''); | ||
empty_check(null, ''); | ||
empty_check_hash(null, {}); | ||
}); | ||
test('bad form', function() { | ||
var form = {}; | ||
hash_check(form, {}); | ||
str_check(form, ''); | ||
empty_check(form, ''); | ||
empty_check_hash(form, {}); | ||
}); | ||
test('empty form', function() { | ||
var form = domify('<form></form>'); | ||
@@ -260,3 +275,3 @@ hash_check(form, {}); | ||
// leading checkbox | ||
var form = domify('<form>' + | ||
form = domify('<form>' + | ||
'<input type="checkbox" name="foo" value="bar3" checked="checked"/>' + | ||
@@ -266,10 +281,11 @@ '<input type="radio" name="foo" value="bar1" checked="checked"/>' + | ||
'<input type="checkbox" name="foo" value="bar4"/>' + | ||
'<input type="checkbox" name="foo" value="bar5" checked="checked"/>' + | ||
'</form>'); | ||
hash_check(form, { | ||
foo: ['bar3', 'bar1'] | ||
foo: ['bar3', 'bar1', 'bar5'] | ||
}); | ||
str_check(form, 'foo=bar3&foo=bar1'); | ||
str_check(form, 'foo=bar3&foo=bar1&foo=bar5'); | ||
}); | ||
test('nested hashes with brackets', function() { | ||
test('bracket notation - hashes', function() { | ||
var form = domify('<form>' + | ||
@@ -281,12 +297,47 @@ '<input type="email" name="account[name]" value="Foo Dude">' + | ||
'<input type="text" name="account[address][empty]" value="">' + | ||
'<select name="beer[type]" multiple>' + | ||
' <option value="ipa" selected>IPA</option>' + | ||
' <option value="pale-ale">Pale Ale</option>' + | ||
' <option value="amber-ale" selected>Amber Ale</option>' + | ||
'</form>'); | ||
hash_check(form, { | ||
account: { | ||
name: 'Foo Dude', | ||
email: 'foobar@example.org', | ||
address: { | ||
city: 'Qux', | ||
state: 'CA' | ||
} | ||
} | ||
}); | ||
empty_check_hash(form, { | ||
account: { | ||
name: 'Foo Dude', | ||
email: 'foobar@example.org', | ||
address: { | ||
city: 'Qux', | ||
state: 'CA', | ||
empty: '' | ||
} | ||
} | ||
}); | ||
}); | ||
test('bracket notation - select multiple', function() { | ||
var form = domify('<form>' + | ||
'<select name="foo" multiple>' + | ||
' <option value="bar" selected>Bar</option>' + | ||
' <option value="baz">Baz</option>' + | ||
' <option value="qux" selected>Qux</option>' + | ||
'</select>' + | ||
'<select name="wine[type]" multiple>' + | ||
' <option value="">No wine</option>' + | ||
' <option value="white">White</option>' + | ||
' <option value="red">Red</option>' + | ||
' <option value="sparkling">Sparkling</option>' + | ||
'</form>'); | ||
hash_check(form, { | ||
foo: [ 'bar', 'qux' ] | ||
}); | ||
// Trailing notation on select.name. | ||
form = domify('<form>' + | ||
'<select name="foo[]" multiple>' + | ||
' <option value="bar" selected>Bar</option>' + | ||
' <option value="baz">Baz</option>' + | ||
' <option value="qux" selected>Qux</option>' + | ||
'</select>' + | ||
@@ -296,35 +347,124 @@ '</form>'); | ||
hash_check(form, { | ||
account: { | ||
name: 'Foo Dude', | ||
email: 'foobar@example.org', | ||
address: { | ||
city: 'Qux', | ||
state: 'CA' | ||
foo: [ 'bar', 'qux' ] | ||
}); | ||
}); | ||
test('bracket notation - select multiple, nested', function() { | ||
var form = domify('<form>' + | ||
'<select name="foo[bar]" multiple>' + | ||
' <option value="baz" selected>Baz</option>' + | ||
' <option value="qux">Qux</option>' + | ||
' <option value="norf" selected>Norf</option>' + | ||
'</select>' + | ||
'</form>'); | ||
hash_check(form, { | ||
foo: { | ||
bar: [ 'baz', 'norf' ] | ||
} | ||
}, | ||
beer: { | ||
type: [ 'ipa', 'amber-ale' ] | ||
}); | ||
}); | ||
test('bracket notation - select multiple, empty values', function() { | ||
var form = domify('<form>' + | ||
'<select name="foo[bar]" multiple>' + | ||
' <option selected>Default value</option>' + | ||
' <option value="" selected>Empty value</option>' + | ||
' <option value="baz" selected>Baz</option>' + | ||
' <option value="qux">Qux</option>' + | ||
' <option value="norf" selected>Norf</option>' + | ||
'</select>' + | ||
'</form>'); | ||
hash_check(form, { | ||
foo: { | ||
bar: [ 'Default value', 'baz', 'norf' ] | ||
} | ||
}); | ||
empty_check_hash(form, { | ||
account: { | ||
name: 'Foo Dude', | ||
email: 'foobar@example.org', | ||
address: { | ||
city: 'Qux', | ||
state: 'CA', | ||
empty: '' | ||
foo: { | ||
bar: [ 'Default value', '', 'baz', 'norf' ] | ||
} | ||
}, | ||
beer: { | ||
type: [ 'ipa', 'amber-ale' ] | ||
}, | ||
wine: { | ||
type: "" | ||
}); | ||
}); | ||
test('bracket notation - non-indexed arrays', function() { | ||
var form = domify('<form>' + | ||
'<input name="people[][name]" value="fred" />' + | ||
'<input name="people[][name]" value="bob" />' + | ||
'<input name="people[][name]" value="bubba" />' + | ||
'</form>'); | ||
hash_check(form, { | ||
people: [ | ||
{ name: "fred" }, | ||
{ name: "bob" }, | ||
{ name: "bubba" }, | ||
] | ||
}); | ||
}); | ||
test('bracket notation - nested, non-indexed arrays', function() { | ||
var form = domify('<form>' + | ||
'<input name="user[tags][]" value="cow" />' + | ||
'<input name="user[tags][]" value="milk" />' + | ||
'</form>'); | ||
hash_check(form, { | ||
user: { | ||
tags: [ "cow", "milk" ], | ||
} | ||
}); | ||
str_check(form, 'account%5Bname%5D=Foo+Dude&account%5Bemail%5D=foobar%40example.org&account%5Baddress%5D%5Bcity%5D=Qux&account%5Baddress%5D%5Bstate%5D=CA&beer%5Btype%5D=ipa&beer%5Btype%5D=amber-ale'); | ||
empty_check(form, 'account%5Bname%5D=Foo+Dude&account%5Bemail%5D=foobar%40example.org&account%5Baddress%5D%5Bcity%5D=Qux&account%5Baddress%5D%5Bstate%5D=CA&account%5Baddress%5D%5Bempty%5D=&beer%5Btype%5D=ipa&beer%5Btype%5D=amber-ale&wine%5Btype%5D=') | ||
}); | ||
test('bracket notation - indexed arrays', function() { | ||
var form = domify('<form>' + | ||
'<input name="people[2][name]" value="bubba" />' + | ||
'<input name="people[2][age]" value="15" />' + | ||
'<input name="people[0][name]" value="fred" />' + | ||
'<input name="people[0][age]" value="12" />' + | ||
'<input name="people[1][name]" value="bob" />' + | ||
'<input name="people[1][age]" value="14" />' + | ||
'<input name="people[][name]" value="frank">' + | ||
'<input name="people[3][age]" value="2">' + | ||
'</form>'); | ||
hash_check(form, { | ||
people: [ | ||
{ | ||
name: "fred", | ||
age: "12" | ||
}, | ||
{ | ||
name: "bob", | ||
age: "14" | ||
}, | ||
{ | ||
name: "bubba", | ||
age: "15" | ||
}, | ||
{ | ||
name: "frank", | ||
age: "2" | ||
} | ||
] | ||
}); | ||
}); | ||
test('bracket notation - bad notation', function() { | ||
var form = domify('<form>' + | ||
'<input name="[][foo]" value="bar" />' + | ||
'<input name="[baz][qux]" value="norf" />' + | ||
'</form>'); | ||
hash_check(form, { | ||
_values: [ | ||
{ foo: 'bar' } | ||
], | ||
baz: { qux: 'norf' } | ||
}); | ||
}); | ||
test('custom serializer', function() { | ||
@@ -331,0 +471,0 @@ var form = domify('<form><input type="text" name="node" value="zuul">/</form>'); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
29411
10
641
148
1