Merge pull request #13 from homeslicesolutions/Refactoring-the-class

Major refactor
1.0.0
Joe Vu 10 years ago
commit 243d8bb619

@ -4,7 +4,7 @@ module.exports = function(grunt) {
run_node: {
start: {
files: { src: [ 'tests/mock-file-server.js'] }
files: { src: [ 'test/mock-file-server.js'] }
}
},
@ -35,41 +35,50 @@ module.exports = function(grunt) {
},
jasmine: {
host: 'http://localhost:8888/',
options: {
specs: ['tests/*spec.js'],
//keepRunner: true,
vendor: [
"bower_components/Blob/Blob.js",
"bower_components/jquery/dist/jquery.min.js",
"bower_components/underscore/underscore-min.js",
"bower_components/backbone/backbone.js",
"backbone-model-file-upload.js"
]
}
},
amd: {
src: 'backbone-model-file-upload.js',
host: 'http://localhost:8888/',
options: {
specs: ['test/*spec.js'],
helpers: 'bower_components/Blob/Blob.js',
//keepRunner: true,
template: require('grunt-template-jasmine-requirejs'),
templateOptions: {
requireConfig: {
paths: {
"jquery": "bower_components/jquery/dist/jquery.min",
"underscore": "bower_components/underscore/underscore-min",
"backbone": "bower_components/backbone/backbone"
}
}
},
parallel: {
runTest: {
tasks: [{
grunt: true,
args: ['run:mockServer']
}, {
grunt: true,
args: ['jasmine']
}, {
cmd: 'node:kill'
}]
}
},
browserGlobal: {
src: 'backbone-model-file-upload.js',
host: 'http://localhost:8888/',
options: {
specs: ['test/*spec.js'],
//keepRunner: true,
vendor: [
"bower_components/Blob/Blob.js",
"bower_components/jquery/dist/jquery.min.js",
"bower_components/underscore/underscore-min.js",
"bower_components/backbone/backbone.js"
]
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.loadNpmTasks('grunt-run');
grunt.loadNpmTasks('grunt-parallel');
grunt.loadNpmTasks('grunt-run-node');
grunt.registerTask('test', ['run:installBower','run_node','jasmine','stop_node']);
grunt.registerTask('test', ['build','run_node','jasmine','stop_node']);
grunt.registerTask('build', ['run:installBower']);
grunt.registerTask('resetNodeWin', ['run:killAllNodeWindows']);
grunt.registerTask('resetNodeMac', ['run:killAllNodeMac']);

@ -1,8 +1,9 @@
Backbone.Model File Upload
==========================
![alt tag](https://travis-ci.org/homeslicesolutions/backbone-model-file-upload.svg?branch=master)
A concise, non-iframe, & pure XHR2/AJAX Backbone.model file upload. (Good for IE >= 10, FF, Chrome.)
![alt tag](https://travis-ci.org/homeslicesolutions/backbone-model-file-upload.svg?branch=master)
This plugin upgrades the current `save` method to be able to upload files using the HTML5's File API and FormData class.
@ -162,18 +163,20 @@ c Version v0.1
#### Version 0.5.2
- Add jQuery to the UMD dependency model
-
#### Version 0.5.3
- Added CommonJS support
### Dev/Installation
If you want to work on this plugin, test it, etc., it just needs an install of `node` and `grunt`.
```
npm i -d
```
To build
```
grunt build
```
To test
```
npm test
npm test OR grunt test
```
### Future plans
As I'm looking at this plugin and all the plugins, I'm not really extending out the Backbone class like how it should. Also people have been asking me to write a mixin version instead. Also there should be a CommonJS version as well. If any of you guys want to help please do. That would be great!

@ -1,4 +1,4 @@
// Backbone.Model File Upload v0.5.2
// Backbone.Model File Upload v0.5.3
// by Joe Vu - joe.vu@homeslicesolutions.com
// For all details and documentation:
// https://github.com/homeslicesolutions/backbone-model-file-upload
@ -7,21 +7,32 @@
// bildja - Dima Bildin - github.com/bildja
// Minjung - Alejandro - github.com/Minjung
(function (root, factory) {
(function(root, factory) {
// AMD
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['underscore', 'jquery', 'backbone'], factory);
define(['underscore', 'jquery', 'backbone'], function(_, $, Backbone){
factory(root, Backbone, _, $);
});
// NodeJS/CommonJS
} else if (typeof exports !== 'undefined') {
var _ = require('underscore'), $ = require('jquery'), Backbone = require('backbone');
factory(root, _, $, Backbone);
// Browser global
} else {
// Browser globals
factory(_, $, Backbone);
factory(root, root.Backbone, root._, root.$);
}
}(this, function(_, $, Backbone){
// Clone the original Backbone.Model.prototype
var backboneModelClone = _.clone( Backbone.Model.prototype );
}(this, function(root, Backbone, _, $) {
'use strict';
// Clone the original Backbone.Model.prototype as superClass
var _superClass = _.clone( Backbone.Model.prototype );
// Extending out
_.extend(Backbone.Model.prototype, {
var BackboneModelFileUpload = Backbone.Model.extend({
// ! Default file attribute - can be overwritten
fileAttribute: 'file',
@ -32,7 +43,7 @@
// Variables
var attrs, attributes = this.attributes;
// Signature parsing - taken directly from original Backbone.Model.save
// Signature parsing - taken directly from original Backbone.Model.save
// and it states: 'Handle both "key", value and {key: value} -style arguments.'
if (key == null || typeof key === 'object') {
attrs = key;
@ -57,16 +68,16 @@
}
// Check for "formData" flag and check for if file exist.
if ( options.formData === true
|| options.formData !== false
&& mergedAttrs[ this.fileAttribute ]
&& mergedAttrs[ this.fileAttribute ] instanceof File
|| mergedAttrs[ this.fileAttribute ] instanceof FileList
|| mergedAttrs[ this.fileAttribute ] instanceof Blob ) {
if ( options.formData === true
|| options.formData !== false
&& mergedAttrs[ this.fileAttribute ]
&& mergedAttrs[ this.fileAttribute ] instanceof File
|| mergedAttrs[ this.fileAttribute ] instanceof FileList
|| mergedAttrs[ this.fileAttribute ] instanceof Blob ) {
// Flatten Attributes reapplying File Object
var formAttrs = _.clone( mergedAttrs ),
fileAttr = mergedAttrs[ this.fileAttribute ];
fileAttr = mergedAttrs[ this.fileAttribute ];
formAttrs = this._flatten( formAttrs );
formAttrs[ this.fileAttribute ] = fileAttr;
@ -93,15 +104,15 @@
var xhr = $.ajaxSettings.xhr();
xhr.upload.addEventListener('progress', that._progressHandler.bind(that), false);
return xhr;
}
}
}
// Resume back to original state
if (attrs && options.wait) this.attributes = attributes;
// Continue to call the existing "save" method
return backboneModelClone.save.call(this, attrs, options);
return _superClass.save.call(this, attrs, options);
},
// _ FlattenObject gist by "penguinboy". Thank You!
@ -123,7 +134,7 @@
return output;
},
// _ Get the Progress of the uploading file
_progressHandler: function( event ) {
if (event.lengthComputable) {
@ -131,7 +142,9 @@
this.trigger( 'progress', percentComplete );
}
}
});
// Export out to override Backbone Model
Backbone.Model = BackboneModelFileUpload;
}));

@ -3,6 +3,7 @@
"dependencies": {
"jquery": "~2.1.1",
"backbone": "~1.1.2",
"Blob": "*"
"Blob": "*",
"requirejs": "~2.1.15"
}
}

@ -13,6 +13,7 @@
"grunt-contrib-jasmine": "^0.6.5",
"grunt-run": "^0.3.0",
"grunt-run-node": "^0.1.0",
"grunt-template-jasmine-requirejs": "^0.2.0",
"lodash": "~2.4.1"
},
"scripts": {

@ -14,6 +14,8 @@
fileAttribute: 'fileAttachment'
});
var fileModel;
var simulatedFileObj;
@ -136,7 +138,7 @@
// Act
fileModel.set({from: 'somethingelse@email.com'});
fileModel.save(null);
fileModel.save();
});

@ -0,0 +1,91 @@
'use strict';
var http = require('http'),
formidable = require('formidable'),
fs = require('fs'),
_ = require('lodash');
var options = {
host: 'localhost',
port: 8989
};
var server = http.createServer(function(req,res) {
console.log('POST Detected with incoming Form-data...');
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
console.log('POST Finished and publishing object back to browser...');
var headers = {};
headers['Content-Type'] = 'application/json';
headers["Access-Control-Allow-Origin"] = req.headers.origin;
headers["Access-Control-Allow-Methods"] = "POST, GET, PUT, DELETE, OPTIONS";
headers["Access-Control-Allow-Credentials"] = true;
headers["Access-Control-Max-Age"] = '86400'; // 24 hours
headers["Access-Control-Allow-Headers"] = "X-Requested-With, X-HTTP-Method-Override, Access-Control-Allow-Origin, Content-Type, Accept";
res.writeHead(200, headers);
var output = {};
_.extend(output, unflatten(fields));
if (_.isEmpty(files)) return res.end(JSON.stringify(output));
for (var i in files) {
if (files.hasOwnProperty(i)) {
output[i] = {};
fs.readFile(files[i].path, function (err, data) {
output[i].type = files[i].type;
output[i].size = files[i].size;
output[i].name = files[i].name;
output[i].lastModifiedDate = files[i].lastModifiedDate;
output[i].data = 'data:' + files[i].type + ';base64,' + data.toString('base64');
res.end(JSON.stringify(output));
});
}
}
});
});
var unflatten = function(path, obj, value) {
var key, child, output;
if (Object.prototype.toString.call(path) == '[object Object]') {
output = {};
for (key in path) {
if (path.hasOwnProperty(key)) {
unflatten(key.split('.'), output, path[key]);
}
}
return output;
}
key = path.shift();
if (!path.length) {
obj[key] = value;
return obj;
}
if ((child = obj[key]) == void 0) {
child = obj[key] = {};
}
unflatten(path, child, value);
return obj;
};
server.listen(options.port, options.host);
console.log("listening on " + options.host + ':' + options.port);

@ -1,122 +0,0 @@
'use strict';
var http = require('http'),
formidable = require('formidable'),
fs = require('fs'),
_ = require('lodash');
var options = {
host: 'localhost',
port: 8989
};
var unflatten = function(path, obj, value) {
var key, child, output;
if (Object.prototype.toString.call(path) == '[object Object]') {
output = {};
for (key in path) {
if (path.hasOwnProperty(key)) {
unflatten(key.split('.'), output, path[key]);
}
}
return output;
}
key = path.shift();
if (!path.length) {
obj[key] = value;
return obj;
}
if ((child = obj[key]) == void 0) {
child = obj[key] = {};
}
unflatten(path, child, value);
return obj;
};
var server = http.createServer(function(req,res) {
// Capture POST call
//if (req.method == 'POST' && req.headers['content-type'].match('application/json')) {
//
// console.log('POST Detected with incoming JSON...');
//
// var body = '';
// req.on('data', function (data) {
// body += data;
// console.log('POST Data: ' + data);
// });
//
// req.on('end', function () {
// console.log('POST Finished and publishing object back to browser...');
// res.writeHead(200, {'Content-Type': 'application/json'});
// res.end(body);
// });
//
//
//
//} else if ((req.method == 'POST' || req.method == 'OPTIONS') && req.headers['content-type'].match('multipart/form-data') > 0) {
console.log('POST Detected with incoming Form-data...');
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
console.log('POST Finished and publishing object back to browser...');
var headers = {};
headers['Content-Type'] = 'application/json';
headers["Access-Control-Allow-Origin"] = req.headers.origin;
headers["Access-Control-Allow-Methods"] = "POST, GET, PUT, DELETE, OPTIONS";
headers["Access-Control-Allow-Credentials"] = true;
headers["Access-Control-Max-Age"] = '86400'; // 24 hours
headers["Access-Control-Allow-Headers"] = "X-Requested-With, X-HTTP-Method-Override, Access-Control-Allow-Origin, Content-Type, Accept";
res.writeHead(200, headers);
var output = {};
_.extend(output, unflatten(fields));
if (_.isEmpty(files)) return res.end(JSON.stringify(output));
for (var i in files) {
if (files.hasOwnProperty(i)) {
output[i] = {};
fs.readFile(files[i].path, function (err, data) {
output[i].type = files[i].type;
output[i].size = files[i].size;
output[i].name = files[i].name;
output[i].lastModifiedDate = files[i].lastModifiedDate;
output[i].data = 'data:' + files[i].type + ';base64,' + data.toString('base64');
res.end(JSON.stringify(output));
});
}
}
});
//} else {
// console.log('GET Detected...');
// console.log('GET Returning OK');
// res.writeHead(200, {'Content-Type': 'text/html'})
// res.end('OK');
//}
});
server.listen(options.port, options.host);
console.log("listening on " + options.host + ':' + options.port);
Loading…
Cancel
Save