summaryrefslogtreecommitdiff
path: root/vendor/assets/javascripts/backbone-model-file-upload.js
blob: 273670766346ce4c006e71f9b345f3134ace6594 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//     Backbone.Model File Upload v0.1
//     by Joe Vu - joe.vu@homeslicesolutions.com
//     For all details and documentation:
//     https://github.com/homeslicesolutions/backbone-model-file-upload

!function(_, Backbone){
  // Clone the original Backbone.Model.prototype
  var backboneModelClone = _.clone( Backbone.Model.prototype );

  // Extending out
  _.extend(Backbone.Model.prototype, {

    // ! Default file attribute - can be overwritten
    fileAttribute: 'file',

    // @ Save - overwritten
    save: function(key, val, options) {

      // Variables
      var attrs, attributes = this.attributes;

      // 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;
      }

      // Validate & wait options - taken directly from original Backbone.Model.save
      options = _.extend({validate: true}, options);
      if (attrs && !options.wait) {
        if (!this.set(attrs, options)) return false;
      } else {
        if (!this._validate(attrs, options)) return false;
      }
      if (attrs && options.wait) {
        this.attributes = _.extend({}, attributes, attrs);
      }

      // Check for "formData" flag and check for if file exist.
      if ( options.formData === true 
           || options.formData !== false 
              && this.attributes[ this.fileAttribute ] 
              && this.attributes[ this.fileAttribute ] instanceof File ) {
        
        // Flatten Attributes reapplying File Object
        var formAttrs = _.clone( this.attributes ),
            fileAttr = this.attributes[ this.fileAttribute ];
        formAttrs = this._flatten( formAttrs );
        formAttrs[ this.fileAttribute ] = fileAttr;

        // Converting Attributes to Form Data
        var formData = new FormData();
        _.each( formAttrs, function( value, key ){
          formData.append( key, value );
        });

        // Set options for AJAX call
        options = options || {};
        options.data = formData;
        options.processData = false;
        options.contentType = false;

        // Apply custom XHR for processing status & listen to "progress"
        var that = this;
        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
      if (attrs && options.wait) this.attributes = attributes;

      // Continue to call the existing "save" method
      return backboneModelClone.save.call(this, attrs, options);
    },

    // _ FlattenObject gist by "penguinboy".  Thank You!
    // https://gist.github.com/penguinboy/762197
    _flatten: function( obj ) {
      var output = {};
      for (var i in obj) {
        if (!obj.hasOwnProperty(i)) continue;
        if (typeof obj[i] == 'object') {
          var flatObject = this._flatten(obj[i]);
          for (var x in flatObject) {
            if (!flatObject.hasOwnProperty(x)) continue;
            output[i + '.' + x] = flatObject[x];
          }
        } else {
          output[i] = obj[i];
        }
      }
      return output;

    },

    // _ Get the Progress of the uploading file
    _progressHandler: function( event ) {
      console.log(event);
      if (event.lengthComputable) {
        var percentComplete = event.loaded / event.total;
        console.log("triggering... " + percentComplete);
        this.trigger( 'progress', percentComplete );
      }
    }
  });
}(_, Backbone);