From 3a31989778f98efca734c7fbd0a9c66ae7c32f3a Mon Sep 17 00:00:00 2001 From: homeslicesolutions Date: Sat, 30 Aug 2014 12:08:56 -0700 Subject: [PATCH] Big fixes with arguments and save The current state was completely messed up with saving and all the different scenarios. This update brings back my main intent of the code and also does a separate merge of the attributes for the use of the FileObj. It also adds Blob support, and I finally written some tests. --- backbone-model-file-upload.js | 42 +++--- tests/backbone-model-file-upload.spec.js | 178 ++++++++++++++++++++--- tests/index.html | 13 +- tests/upload-test.php | 57 ++++++-- 4 files changed, 218 insertions(+), 72 deletions(-) diff --git a/backbone-model-file-upload.js b/backbone-model-file-upload.js index 88b23d3..dde41fb 100644 --- a/backbone-model-file-upload.js +++ b/backbone-model-file-upload.js @@ -1,4 +1,4 @@ -// Backbone.Model File Upload v0.3 +// Backbone.Model File Upload v0.4 // by Joe Vu - joe.vu@homeslicesolutions.com // For all details and documentation: // https://github.com/homeslicesolutions/backbone-model-file-upload @@ -20,7 +20,7 @@ var backboneModelClone = _.clone( Backbone.Model.prototype ); // Extending out - _.extend(Backbone.Model.prototype, { + _.extend(Backbone.Model.prototype, { // ! Default file attribute - can be overwritten fileAttribute: 'file', @@ -31,16 +31,11 @@ // Variables var attrs, attributes = this.attributes; - // Signature parsing - similar to original Backbone.Model.save - // Handle (key, value, options), ({key: value}, options), and (options) -style arguments. - if (typeof key === 'object') { - if (typeof val === void 0) { - attrs = attributes; - options = key; - } else { - attrs = key; - options = val; - } + // 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; + options = val; } else { (attrs = {})[key] = val; } @@ -52,19 +47,24 @@ } else { if (!this._validate(attrs, options)) return false; } + + // Merge data temporarily for formdata + var mergedAttrs = _.extend({}, attributes, attrs); + if (attrs && options.wait) { - this.attributes = _.extend({}, attributes, attrs); + this.attributes = mergedAttrs; } // Check for "formData" flag and check for if file exist. if ( options.formData === true || options.formData !== false - && attrs[ this.fileAttribute ] - && attrs[ this.fileAttribute ] instanceof File ) { + && mergedAttrs[ this.fileAttribute ] + && mergedAttrs[ this.fileAttribute ] instanceof File + || mergedAttrs[ this.fileAttribute ] instanceof Blob ) { // Flatten Attributes reapplying File Object - var formAttrs = _.clone( attrs ), - fileAttr = attrs[ this.fileAttribute ]; + var formAttrs = _.clone( mergedAttrs ), + fileAttr = mergedAttrs[ this.fileAttribute ]; formAttrs = this._flatten( formAttrs ); formAttrs[ this.fileAttribute ] = fileAttr; @@ -75,7 +75,6 @@ }); // Set options for AJAX call - options = options || {}; options.data = formData; options.processData = false; options.contentType = false; @@ -85,11 +84,10 @@ options.xhr = function() { var xhr = $.ajaxSettings.xhr(); xhr.upload.addEventListener('progress', function(){ - that._progressHandler.apply(that, arguments); }, false); return xhr; - } + } } // Resume back to original state @@ -97,7 +95,7 @@ // Continue to call the existing "save" method return backboneModelClone.save.call(this, attrs, options); - + }, // _ FlattenObject gist by "penguinboy". Thank You! @@ -119,7 +117,7 @@ return output; }, - + // _ Get the Progress of the uploading file _progressHandler: function( event ) { if (event.lengthComputable) { diff --git a/tests/backbone-model-file-upload.spec.js b/tests/backbone-model-file-upload.spec.js index 87915b9..f9c0ea4 100644 --- a/tests/backbone-model-file-upload.spec.js +++ b/tests/backbone-model-file-upload.spec.js @@ -1,29 +1,161 @@ !function(){ 'use strict' - var File = Backbone.Model.extend({ - url: 'upload-test.php', - fileAttribute: 'fileAttachment' + describe('Testing Backbone Model Plugin', function(){ + + var File = Backbone.Model.extend({ + url: 'upload-test.php', + fileAttribute: 'fileAttachment' + }); + + var fileModel; + var simulatedFileObj; + + beforeEach(function(){ + + simulatedFileObj = new Blob(['Hello World'], {type : 'text/html'}); + + fileModel = new File({ + from: 'sample@email.com', + subject: 'Hello, friend!', + body: 'Dear friend, Just saying hello! Love, Yours truly.', + nestedObject: { + nest: 'eggs' + } + }); + + }); + + it('should detect the file(blob) save successfully', function(done){ + + // Arrange + fileModel.set({fileAttachment: simulatedFileObj}); + + // Listen + fileModel.on('sync', function(model){ + + // Assert + expect(model.get('from')).toBe('sample@email.com'); + expect(model.get('fileAttachment').data).toBe('data:text/html;base64,PHN0cm9uZz5IZWxsbyBXb3JsZDwvc3Ryb25nPg=='); + + done(); + + }) + + // Act + fileModel.save(null); + + }); + + it ('should be able to be saved in as an argument of "save" as object', function(done){ + + // Listen + fileModel.on('sync', function(model){ + + // Assert + expect(model.get('from')).toBe('sample@email.com'); + expect(model.get('fileAttachment').data).toBe('data:text/html;base64,PHN0cm9uZz5IZWxsbyBXb3JsZDwvc3Ryb25nPg=='); + + done(); + + }); + + // Act + fileModel.save({fileAttachment: simulatedFileObj}); + + }); + + it ('should be able to be saved in as an argument of "save" as key/value argument', function(done){ + + // Listen + fileModel.on('sync', function(model){ + + // Assert + expect(model.get('from')).toBe('sample@email.com'); + expect(model.get('fileAttachment').data).toBe('data:text/html;base64,PHN0cm9uZz5IZWxsbyBXb3JsZDwvc3Ryb25nPg=='); + + done(); + + }); + + // Act + fileModel.save('fileAttachment', simulatedFileObj); + + }); + + it ('should be able to have "wait" and "validate" option', function(done){ + + // Listen + fileModel.on('sync', function(model){ + + // Assert + expect(model.get('from')).toBe('sample@email.com'); + expect(model.get('fileAttachment').data).toBe('data:text/html;base64,PHN0cm9uZz5IZWxsbyBXb3JsZDwvc3Ryb25nPg=='); + + done(); + + }); + + // Act + fileModel.save({fileAttachment: simulatedFileObj},{wait: true, validate: false}); + + }); + + it('should still have the option to save normally by setting it and save(null)', function(done){ + + // Listen + fileModel.on('sync', function(model){ + + // Assert + expect(model.get('from')).toBe('somethingelse@email.com'); + //expect(model.get('fileAttachment').data).toBe('data:text/html;base64,PHN0cm9uZz5IZWxsbyBXb3JsZDwvc3Ryb25nPg=='); + + done(); + + }); + + // Act + fileModel.set({from: 'somethingelse@email.com'}); + fileModel.save(null); + + }); + + it('should still have the option to save normally by save("from","yes")', function(done){ + + // Listen + fileModel.on('sync', function(model){ + + // Assert + expect(model.get('from')).toBe('yes'); + //expect(model.get('fileAttachment').data).toBe('data:text/html;base64,PHN0cm9uZz5IZWxsbyBXb3JsZDwvc3Ryb25nPg=='); + + done(); + + }); + + // Act + fileModel.save('from','yes'); + + }); + + it('should still have the option to save normally by save({from: "yes"})', function(done){ + + // Listen + fileModel.on('sync', function(model){ + + // Assert + expect(model.get('from')).toBe('yes'); + //expect(model.get('fileAttachment').data).toBe('data:text/html;base64,PHN0cm9uZz5IZWxsbyBXb3JsZDwvc3Ryb25nPg=='); + + done(); + + }); + + // Act + fileModel.save({from: "yes"}); + + }); + }); - var file = new File({ - from: 'sample@email.com', - subject: 'Hello, friend!', - body: 'Dear friend, Just saying hello! Love, Yours truly.', - nestedObject: { - nest: 'eggs' - } - }); - - $('#fileupload').on('change', function(e){ - var fileObj = $(this)[0].files[0]; - console.log(fileObj) - file.set('fileAttachment', fileObj); - file.save(); - }) - - //file.save(); - - - }(); \ No newline at end of file diff --git a/tests/index.html b/tests/index.html index 1b275ed..61c3f8a 100644 --- a/tests/index.html +++ b/tests/index.html @@ -11,22 +11,13 @@ - + - - - - - - - \ No newline at end of file diff --git a/tests/upload-test.php b/tests/upload-test.php index 1779542..a553a2a 100644 --- a/tests/upload-test.php +++ b/tests/upload-test.php @@ -1,23 +1,48 @@ $value) { + if (strpos(strtolower($name), 'content-type') > -1 + && strpos(strtolower($value), 'json') > -1) { + + $json = file_get_contents('php://input'); + $obj = json_decode($json); + + foreach ($obj as $key => $value) { + $passthrough[$key] = $value; + } + + break; + } +} + + +$model = array( + 'from' => $passthrough['from'], + 'subject' => $passthrough['subject'], + 'body' => $passthrough['body'], + 'nestedObject' => array( + 'nest' => $passthrough["nestedObject_nest"] + ) +); + $fileName = $_FILES['fileAttachment']['name']; $fileType = $_FILES['fileAttachment']['type']; -$fileContent = file_get_contents($_FILES['fileAttachment']['tmp_name']); -$dataUrl = 'data:' . $fileType . ';base64,' . base64_encode($fileContent); - -$json = json_encode(array( - 'fileAttachment' => array( +if ($_FILES['fileAttachment']['tmp_name'] != "") { + $fileContent = file_get_contents($_FILES['fileAttachment']['tmp_name']); + $dataUrl = 'data:' . $fileType . ';base64,' . base64_encode($fileContent); + + $model['fileAttachment'] = array( 'name' => $fileName, 'type' => $fileType, 'data' => $dataUrl - ), - 'from' => $_REQUEST['from'], - 'subject' => $_REQUEST['subject'], - 'body' => $_REQUEST['body'], - 'nestedObject' => array( - 'nest' => $_REQUEST["nestedObject.nest"] - ) -)); - -echo $json; -?> \ No newline at end of file + ); + +} +echo json_encode($model); +?> +