Last active
September 18, 2017 15:45
-
-
Save philfreo/3877309 to your computer and use it in GitHub Desktop.
Backbone-Forms File Upload Editor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// directly uploads to S3 | |
// See http://philfreo.com/blog/how-to-allow-direct-file-uploads-from-javascript-to-amazon-s3-signed-by-python/ | |
// See https://github.com/elasticsales/s3upload-coffee-javascript | |
editors.Filepicker = editors.Text.extend({ | |
tagName: 'div', | |
events: { | |
'change input[type=file]': 'uploadFile', | |
'click .remove': 'removeFile' | |
}, | |
initialize: function(options) { | |
_.bindAll(this, 'filepickerSuccess', 'filepickerError', 'filepickerProgress'); | |
editors.Text.prototype.initialize.call(this, options); | |
this.$input = $('<input type="hidden" name="'+this.key+'" />'); | |
this.$uploadInput = $('<input type="file" multiple="multiple" />'); | |
this.$loader = $('<p class="upload-status"><span class="loader"></span> Uploading…</p>'); | |
this.$error = $('<p class="upload-error error">Error</p>'); | |
this.$list = $('<ul class="file-list">'); | |
}, | |
// return an array of file dicts | |
getValue: function() { | |
var val = this.$input.val(); | |
return val ? JSON.parse(val) : []; | |
}, | |
setValue: function(value) { | |
var str, files = value; | |
if (_(value).isObject()) { | |
str = JSON.stringify(value); | |
} else { | |
files = value ? JSON.parse(value) : []; | |
} | |
this.$input.val(str); | |
this.updateList(files); | |
}, | |
render: function(options) { | |
editors.Text.prototype.render.apply(this, arguments); | |
this.$el.append(this.$input); | |
this.$el.append(this.$uploadInput); | |
this.$el.append(this.$loader.hide()); | |
this.$el.append(this.$error.hide()); | |
this.$el.append(this.$list); | |
return this; | |
}, | |
uploadFile: function() { | |
var s3upload = new S3Upload({ | |
file_dom_selector: this.$uploadInput, | |
s3_sign_put_url: '/sign_s3_put/', | |
onProgress: this.filepickerProgress, | |
onFinishS3Put: this.filepickerSuccess, | |
onError: this.filepickerError | |
}); | |
}, | |
filepickerSuccess: function(s3Url, file) { | |
console.log('File uploaded', s3Url); | |
this.$loader.hide(); | |
this.$error.hide(); | |
this.$uploadInput.val(''); | |
var newFiles = [{ | |
url: s3Url, | |
filename: file.name, | |
size: file.size, | |
content_type: file.type | |
}]; | |
console.log('File uploaded (processed)', newFiles); | |
this.setValue(this.getValue().concat(newFiles)); | |
}, | |
filepickerError: function(msg, file) { | |
console.debug('Filepicker error', msg); | |
this.$loader.hide(); | |
this.$error.show(); | |
}, | |
filepickerProgress: function(percent, message, publicUrl, file) { | |
//console.log('Filepicker progress', percent, message); | |
this.$loader.show(); | |
this.$error.hide(); | |
}, | |
updateList: function(files) { | |
// this code is currently duplicated as a handlebar helper (I wanted to let this | |
// backbone-forms field stand on its own) | |
var displayFilesize = function(bytes) { | |
// TODO improve this function | |
return Math.floor(bytes / 1024) + 'K'; | |
}; | |
this.$list.empty(); | |
_(files).each(function(file) { | |
var a = $('<a>', { | |
target: '_blank', | |
href: file.url, | |
text: file.filename + ' (' + file.content_type + ') ' + displayFilesize(file.size) | |
}); | |
var li = $('<li>').append(a); | |
li.append(a, ' ', $('<a href="#" class="remove"><i class="icon-remove"></i></a>').data('url', file.url)); | |
this.$list.append(li); | |
}, this); | |
this.$list[files.length ? 'show' : 'hide'](); | |
}, | |
removeFile: function(ev) { | |
if (ev) ev.preventDefault(); | |
var url = $(ev.currentTarget).data('url'); | |
var files = this.getValue(); | |
this.setValue(_(files).reject(function(one) { | |
return one.url === url; | |
})); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// this version uses the filepicker.io service | |
editors.FilepickerIO = editors.Text.extend({ | |
tagName: 'div', | |
events: { | |
'change input[type=file]': 'uploadFile', | |
'click .remove': 'removeFile' | |
}, | |
initialize: function(options) { | |
_.bindAll(this, 'filepickerSuccess', 'filepickerError', 'filepickerProgress'); | |
editors.Text.prototype.initialize.call(this, options); | |
this.$input = $('<input type="hidden" name="'+this.key+'" />'); | |
this.$uploadInput = $('<input type="file" multiple="multiple" />'); | |
this.$loader = $('<p class="upload-status"><span class="loader"></span> Uploading…</p>'); | |
this.$error = $('<p class="upload-error error">Error</p>'); | |
this.$list = $('<ul class="file-list">'); | |
}, | |
// return an array of file dicts | |
getValue: function() { | |
var val = this.$input.val(); | |
return val ? JSON.parse(val) : []; | |
}, | |
setValue: function(value) { | |
var str, files = value; | |
if (_(value).isObject()) { | |
str = JSON.stringify(value); | |
} else { | |
files = value ? JSON.parse(value) : []; | |
} | |
this.$input.val(str); | |
this.updateList(files); | |
}, | |
render: function(options) { | |
editors.Text.prototype.render.apply(this, arguments); | |
this.$el.append(this.$input); | |
this.$el.append(this.$uploadInput); | |
this.$el.append(this.$loader.hide()); | |
this.$el.append(this.$error.hide()); | |
this.$el.append(this.$list); | |
return this; | |
}, | |
uploadFile: function() { | |
filepicker = require('filepicker-io'); | |
// https://developers.filepicker.io/docs/web/#local-upload | |
filepicker.uploadFile(this.$uploadInput.get(0), this.filepickerSuccess, this.filepickerError, this.filepickerProgress); | |
}, | |
filepickerSuccess: function(files) { | |
console.log('Filepicker (raw)', files); | |
this.$loader.hide(); | |
this.$error.hide(); | |
this.$uploadInput.val(''); | |
// when uploading one file, it returns just an object | |
if (!_(files).isArray()) { files = [files]; } | |
// turn response array into a flatter array of objects | |
var newFiles = _(files).map(function(one) { | |
return { | |
url: one.url, | |
filename: one.data.filename, | |
s3_key: one.data.key, | |
size: one.data.size, | |
content_type: one.data.type | |
}; | |
}); | |
console.log('Filepicker (processed)', newFiles); | |
this.setValue(this.getValue().concat(newFiles)); | |
}, | |
filepickerError: function(msg) { | |
console.debug('Filepicker error', msg); | |
this.$loader.hide(); | |
this.$error.show(); | |
}, | |
filepickerProgress: function(percent) { | |
this.$loader.show(); | |
this.$error.hide(); | |
}, | |
updateList: function(files) { | |
// this code is currently duplicated as a handlebar helper (I wanted to let this | |
// backbone-forms field stand on its own) | |
var displayFilesize = function(bytes) { | |
// TODO improve this function | |
return Math.floor(bytes / 1024) + 'K'; | |
}; | |
this.$list.empty(); | |
_(files).each(function(file) { | |
var a = $('<a>', { | |
target: '_blank', | |
href: file.url, | |
text: file.filename + ' (' + file.content_type + ') ' + displayFilesize(file.size) | |
}); | |
var li = $('<li>').append(a); | |
li.append(a, ' ', $('<a href="#" class="remove"><i class="icon-remove"></i></a>').data('s3_key', file.s3_key)); | |
this.$list.append(li); | |
}, this); | |
this.$list[files.length ? 'show' : 'hide'](); | |
}, | |
removeFile: function(ev) { | |
if (ev) ev.preventDefault(); | |
var s3_key = $(ev.currentTarget).data('s3_key'); | |
var files = this.getValue(); | |
this.setValue(_(files).reject(function(one) { | |
return one.s3_key === s3_key; | |
})); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment