Skip to content

Commit

Permalink
Merge branch 'serialize-array'
Browse files Browse the repository at this point in the history
  • Loading branch information
jugglinmike committed Mar 1, 2015
2 parents 55f7ac0 + 81150b7 commit ccd9369
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 1 deletion.
10 changes: 10 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,16 @@ $('.apple.green').toggleClass('fruit green red', true).html()
#### .is( function(index) )
Checks the current list of elements and returns `true` if _any_ of the elements match the selector. If using an element or Cheerio selection, returns `true` if _any_ of the elements match. If using a predicate function, the function is executed in the context of the selected element, so `this` refers to the current element.

### Forms

#### .serializeArray()

Encode a set of form elements as an array of names and values.

```js
$('<form><input name="foo" value="bar" /></form>').serializeArray()
//=> [ { name: 'foo', valule: 'bar' } ]
```

### Traversing

Expand Down
54 changes: 54 additions & 0 deletions lib/api/forms.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// https://github.com/jquery/jquery/blob/2.1.3/src/manipulation/var/rcheckableType.js
// https://github.com/jquery/jquery/blob/2.1.3/src/serialize.js
var _ = require('lodash'),
submittableSelector = 'input,select,textarea,keygen',
rCRLF = /\r?\n/g,
rcheckableType = /^(?:checkbox|radio)$/i,
rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i;

exports.serializeArray = function() {
// Resolve all form elements from either forms or collections of form elements
var Cheerio = this.constructor;
return this.map(function() {
var elem = this;
var $elem = Cheerio(elem);
if (elem.name === 'form') {
return $elem.find(submittableSelector).toArray();
} else {
return $elem.filter(submittableSelector).toArray();
}
}).filter(function() {
var $elem = Cheerio(this);
var type = $elem.attr('type');

// Verify elements have a name (`attr.name`) and are not disabled (`:disabled`)
return $elem.attr('name') && !$elem.is(':disabled') &&
// and cannot be clicked (`[type=submit]`) or are used in `x-www-form-urlencoded` (`[type=file]`)
!rsubmitterTypes.test(type) &&
// and are either checked/don't have a checkable state
($elem.attr('checked') || !rcheckableType.test(type));
// Convert each of the elements to its value(s)
}).map(function(i, elem) {
var $elem = Cheerio(elem);
var name = $elem.attr('name');
var val = $elem.val();

// If there is no value set (e.g. `undefined`, `null`), then return nothing
if (val == null) {
return null;
} else {
// If we have an array of values (e.g. `<select multiple>`), return an array of key/value pairs
if (Array.isArray(val)) {
return _.map(val, function(val) {
// We trim replace any line endings (e.g. `\r` or `\r\n` with `\r\n`) to guarantee consistency across platforms
// These can occur inside of `<textarea>'s`
return {name: name, value: val.replace( rCRLF, '\r\n' )};
});
// Otherwise (e.g. `<input type="text">`, return only one key/value pair
} else {
return {name: name, value: val.replace( rCRLF, '\r\n' )};
}
}
// Convert our result to an array
}).get();
};
3 changes: 2 additions & 1 deletion lib/cheerio.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ var api = [
require('./api/attributes'),
require('./api/traversing'),
require('./api/manipulation'),
require('./api/css')
require('./api/css'),
require('./api/forms')
];

/*
Expand Down
120 changes: 120 additions & 0 deletions test/api/forms.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
var expect = require('expect.js'),
cheerio = require('../..'),
forms = require('../fixtures').forms;

describe('$(...)', function() {

var $;

beforeEach(function() {
$ = cheerio.load(forms);
});

describe('.serializeArray', function() {

it('() : should get form controls', function() {
expect($('form#simple').serializeArray()).to.eql([
{
name: 'fruit',
value: 'Apple'
}
]);
});

it('() : should get nested form controls', function() {
expect($('form#nested').serializeArray()).to.have.length(2);
var data = $('form#nested').serializeArray();
data.sort(function (a, b) {
return a.value - b.value;
});
expect(data).to.eql([
{
name: 'fruit',
value: 'Apple'
},
{
name: 'vegetable',
value: 'Carrot'
}
]);
});

it('() : should not get disabled form controls', function() {
expect($('form#disabled').serializeArray()).to.eql([]);
});

it('() : should not get form controls with the wrong type', function() {
expect($('form#submit').serializeArray()).to.eql([
{
name: 'fruit',
value: 'Apple'
}
]);
});

it('() : should get selected options', function() {
expect($('form#select').serializeArray()).to.eql([
{
name: 'fruit',
value: 'Orange'
}
]);
});

it('() : should not get unnamed form controls', function() {
expect($('form#unnamed').serializeArray()).to.eql([
{
name: 'fruit',
value: 'Apple'
}
]);
});

it('() : should get multiple selected options', function() {
expect($('form#multiple').serializeArray()).to.have.length(2);
var data = $('form#multiple').serializeArray();
data.sort(function (a, b) {
return a.value - b.value;
});
expect(data).to.eql([
{
name: 'fruit',
value: 'Apple'
},
{
name: 'fruit',
value: 'Orange'
}
]);
});

it('() : should get individually selected elements', function() {
var data = $('form#nested input').serializeArray();
data.sort(function (a, b) {
return a.value - b.value;
});
expect(data).to.eql([
{
name: 'fruit',
value: 'Apple'
},
{
name: 'vegetable',
value: 'Carrot'
}
]);

});

it('() : should standardize line breaks', function() {
expect($('form#textarea').serializeArray()).to.eql([
{
name: 'fruits',
value: 'Apple\r\nOrange'
}
]);
});

});

});
11 changes: 11 additions & 0 deletions test/fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,14 @@ exports.text = [
'<p>Apples, <b>oranges</b> and pears.</p>',
'<p>Carrots and <!-- sweetcorn --></p>'
].join('');

exports.forms = [
'<form id="simple"><input type="text" name="fruit" value="Apple" /></form>',
'<form id="nested"><div><input type="text" name="fruit" value="Apple" /></div><input type="text" name="vegetable" value="Carrot" /></form>',
'<form id="disabled"><input type="text" name="fruit" value="Apple" disabled /></form>',
'<form id="submit"><input type="text" name="fruit" value="Apple" /><input type="submit" name="submit" value="Submit" /></form>',
'<form id="select"><select name="fruit"><option value="Apple">Apple</option><option value="Orange" selected>Orange</option></select></form>',
'<form id="unnamed"><input type="text" name="fruit" value="Apple" /><input type="text" value="Carrot" /></form>',
'<form id="multiple"><select name="fruit" multiple><option value="Apple" selected>Apple</option><option value="Orange" selected>Orange</option><option value="Carrot">Carrot</option></select></form>',
'<form id="textarea"><textarea name="fruits">Apple\nOrange</textarea></form>'
].join('');

0 comments on commit ccd9369

Please sign in to comment.