Permalink
Browse files

added FormData form serializer

  • Loading branch information...
1 parent 12a41c8 commit 8278c3d5467b3fc98e93fd0cc3bcde9689d6ad56 @coolaj86 committed Jan 13, 2011
Showing with 226 additions and 0 deletions.
  1. +41 −0 examples/form-data-test.js
  2. +1 −0 files/file1.txt
  3. +3 −0 files/file2.txt
  4. BIN files/smiley-cool.png
  5. +181 −0 lib/form-data.js
View
@@ -0,0 +1,41 @@
+(function () {
+ "use strict";
+
+ var File = require('../lib/file.js'),
+ FormData = require('../lib/form-data.js'),
+ formData = new FormData(),
+ chunked = (new Date().valueOf() % 2) ? true : false,
+ size = 0,
+ count = 0,
+ bodyStream;
+
+ formData.setNodeChunkedEncoding(chunked);
+
+ formData.append('items[]', 'value0');
+ formData.append('items[]', 'value1');
+ formData.append('item', 'value2');
+ formData.append('item', 'value3');
+ formData.append('file1', new File(__dirname + '/../files/file2.txt'));
+ formData.append('emoticon', new File(__dirname + '/../files/smiley-cool.png'));
+ formData.append('avatar', new File(__dirname + '/../files/coolaj86-2010.jpg'));
+
+
+ // Uses 'x-www-form-urlencoded' if possible
+ bodyStream = formData.serialize('x-www-form-urlencoded');
+
+ // Check to see if contentType has switched to 'multipart/form-data'
+ console.log(formData.getContentType());
+
+ bodyStream.on('data', function (data) {
+ count += 1;
+ size += data.length;
+ //console.log(data.toString('binary'));
+ });
+ bodyStream.on('load', function (data) {
+ console.log(chunked ? 'chunked' : 'not-chunked', count, data.length, size);
+ //console.log(data.toString('binary'));
+ });
+ bodyStream.on('end', function () {
+ console.log(size);
+ });
+}());
View
@@ -0,0 +1 @@
+This is file 1
View
@@ -0,0 +1,3 @@
+This is file 2
+It has three lines
+And was created on OS X
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
@@ -0,0 +1,181 @@
+//
+// FormData
+//
+// http://hacks.mozilla.org/2010/05/formdata-interface-coming-to-firefox/
+//
+(function () {
+ "use strict";
+
+ require('remedial');
+ require('bufferjs');
+
+ var EventEmitter = require('events').EventEmitter,
+ Futures = require('futures'),
+ File = require('./file'),
+ FileReader = require('./file-reader');
+
+ function isFile(o) {
+ return (o instanceof File) ||
+ (o.name && (o.path || o.stream || o.buffer));
+ }
+
+ function FormData() {
+ var self = this,
+ fields = {};
+
+ self.nodeChunkedEncoding = false;
+
+ self.setNodeChunkedEncoding = function (val) {
+ self.nodeChunkedEncoding = val;
+ };
+
+ self.getContentType = function () {
+ return self.type;
+ };
+
+ self.append = function (key, val) {
+ var field = fields[key] = fields[key] || [],
+ err;
+
+ if (field.length > 0 && '[]' !== key.substr(key.length - 2)) {
+ err = new Error("Overwriting '" + key + "'. Use '" + key + "[] if you intend this to be treated as an array' ");
+ console.log(err.message);
+ field.pop();
+ }
+
+ field.push(val);
+ return err;
+ };
+
+ function toJSON() {
+ /*
+ files.forEach(function (file) {
+ var fr = new FileReader();
+ fr.addEventListener('load', join.deliverer());
+ fr.readAsText('base64');
+ });
+ */
+ }
+
+ function toContentDisposition(key, val) {
+ var emitter = new EventEmitter(),
+ text = '',
+ fr;
+
+ text += '\r\n--' + self.boundary;
+ text += "\r\nContent-Disposition: form-data; name=" + key.quote();
+
+ if (!isFile(val)) {
+ if ('string' !== typeof val) {
+ val = JSON.stringify(val);
+ }
+ process.nextTick(function () {
+ emitter.emit('data', new Buffer(text + "\r\n\r\n" + val));
+ emitter.emit('end');
+ });
+ } else {
+ fr = new FileReader();
+ fr.on('loadstart', function () {
+ text += "\r\nContent-Type: " + (val.type || 'application/binary') + "\r\n\r\n";
+ emitter.emit('data', new Buffer(text));
+ });
+ fr.on('data', function (data) {
+ emitter.emit('data', data);
+ });
+ fr.on('loadend', function () {
+ emitter.emit('end');
+ });
+ fr.setNodeChunkedEncoding(self.nodeChunkedEncoding);
+ fr.readAsArrayBuffer(val);
+ }
+ return emitter;
+ }
+
+ Array.prototype.forEachAsync = function (callback) {
+ var self = this,
+ sequence = Futures.sequence();
+
+ function handleItem(item, i, arr) {
+ sequence.then(function (next) {
+ callback(next, item, i, arr);
+ });
+ }
+
+ this.forEach(handleItem);
+
+ return sequence;
+ };
+
+ function toFormData() {
+ var emitter = new EventEmitter(),
+ buffers = [];
+
+ // TODO randomize
+ self.boundary = 'abcdefghiponmlkjqrstzyxwvu';
+
+ emitter.on('data', function (data) {
+ buffers.push(data);
+ });
+
+ Object.keys(fields).forEachAsync(function (next, key) {
+ fields[key].forEachAsync(function (next, item) {
+ var stream = toContentDisposition(key, item);
+ stream.on('data', function (data) {
+ emitter.emit('data', data);
+ });
+ stream.on('end', next);
+ })
+ .then(next);
+ })
+ .then(function (next) {
+ emitter.emit('ready');
+ next(); // does cleanup
+ });
+
+ emitter.on('ready', function () {
+ emitter.emit('load', Buffer.concat(buffers));
+ emitter.emit('end');
+ });
+
+ return emitter;
+ }
+
+ function toFormUrlEncoded() {
+ }
+
+ self.serialize = function (intendedType) {
+ self.type = intendedType = (intendedType || '').toLowerCase();
+
+ if ('multipart/form-data' !== self.type) {
+ Object.keys(fields).forEach(function (key) {
+ // TODO traverse entire tree
+ fields[key].forEach(function (item) {
+ if (isFile(item)) {
+ self.type = 'multipart/form-data';
+ }
+ });
+ });
+
+ if ('multipart/form-data' === self.type) {
+ console.log("ContentType changed `multipart/form-data`: Some of the upload items are `HTML5::FileAPI::File`s.");
+ }
+
+ return toFormData();
+ }
+
+ if (!self.type || 'application/x-www-form-urlencoded' === self.type) {
+ self.type = 'application/x-www-form-urlencoded';
+ return toFormUrlEncoded();
+ }
+
+ if ('application/json' === encoding.toLowerCase()) {
+ return toJSON();
+ }
+ };
+ }
+
+ module.exports = FormData;
+
+ if ('undefined' === typeof provide) { provide = function() {}; }
+ provide('file-api/form-data');
+}());

0 comments on commit 8278c3d

Please sign in to comment.