Skip to content

Instantly share code, notes, and snippets.

@blia
Created April 1, 2016 15:14
Show Gist options
  • Save blia/ec42096f778fcf601da1bb35167be436 to your computer and use it in GitHub Desktop.
Save blia/ec42096f778fcf601da1bb35167be436 to your computer and use it in GitHub Desktop.
var Uploader = Ember.Object.extend(Ember.Evented, {
url: null,
paramNamespace: null,
paramName: 'file',
isUploading: false,
/**
* ajax request type (method), by default it will be POST
*
* @property type
*/
type: 'POST',
/**
* Start upload of files and extra data
*
* @param {object|array} files One file object or one array of files object
* @param {array} extra
* @return {object} jquery promise from ajax object
*/
upload: function(files, extra) {
extra = extra || {};
var data = this.setupFormData(files, extra);
var url = get(this, 'url');
var type = get(this, 'type');
var self = this;
set(this, 'isUploading', true);
return this.ajax(url, data, type);
},
setupFormData: function(files, extra) {
var formData = new FormData();
for (var prop in extra) {
if (extra.hasOwnProperty(prop)) {
formData.append(this.toNamespacedParam(prop), extra[prop]);
}
}
// if is a array of files ...
if (Ember.isArray(files)) {
var paramName;
for (var i = files.length - 1; i >= 0; i--) {
paramName = this.toNamespacedParam(this.paramName) + '[' + i + ']';
formData.append(paramName , files[i]);
}
} else {
// if has only one file object ...
formData.append(this.toNamespacedParam(this.paramName), files);
}
return formData;
},
toNamespacedParam: function(name) {
if (this.paramNamespace) {
return this.paramNamespace + '[' + name + ']';
}
return name;
},
didUpload: function(data) {
set(this, 'isUploading', false);
this.trigger('didUpload', data);
return data;
},
didError: function(jqXHR, textStatus, errorThrown) {
set(this, 'isUploading', false);
// Borrowed from Ember Data
var isObject = jqXHR !== null && typeof jqXHR === 'object';
if (isObject) {
jqXHR.then = null;
if (!jqXHR.errorThrown) {
if (typeof errorThrown === 'string') {
jqXHR.errorThrown = new Error(errorThrown);
} else {
jqXHR.errorThrown = errorThrown;
}
}
}
this.trigger('didError', jqXHR, textStatus, errorThrown);
return jqXHR;
},
didProgress: function(e) {
e.percent = e.loaded / e.total * 100;
this.trigger('progress', e);
},
abort: function() {
set(this, 'isUploading', false);
this.trigger('isAborting');
},
ajax: function(url, params, method) {
var self = this;
var settings = {
url: url,
type: method || 'POST',
contentType: false,
processData: false,
xhr: function() {
var xhr = Ember.$.ajaxSettings.xhr();
xhr.upload.onprogress = function(e) {
self.didProgress(e);
};
self.one('isAborting', function() { xhr.abort(); });
return xhr;
},
data: params
};
return this._ajax(settings);
},
_ajax: function(settings) {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
settings.success = function(data) {
Ember.run(null, resolve, self.didUpload(data));
};
settings.error = function(jqXHR, responseText, errorThrown) {
Ember.run(null, reject, self.didError(jqXHR, responseText, errorThrown));
};
Ember.$.ajax(settings);
});
}
});
/**
* @namespace Uploader
*/
export var eventManager = Ember.Object.extend({
_elements: Ember.$(),
dragEnter: function (evt, view) {
if (this.get('_elements.length') === 0) {
this.uploader.trigger('filesDragStart', evt, view)
}
if (view.$().hasClass('uploader-zone')) {
Ember.$('.uploader-zone').removeClass('active')
view.$().addClass('active')
} else {
Ember.$('.uploader-zone').removeClass('active')
}
this.set('_elements', this.get('_elements').add(evt.target))
},
dragLeave: function (evt, view) {
this.set('_elements', this.get('_elements').not(evt.target))
if (this.get('_elements.length') === 0) {
this.uploader.trigger('filesDragStop', evt, view)
Ember.$('.uploader-zone').removeClass('active')
}
},
dragOver: function (evt, view) {
evt.stopPropagation();
evt.preventDefault();
},
drop: function (evt, view) {
this.set('_elements', Ember.$())
evt.stopPropagation();
evt.preventDefault();
this.uploader.trigger('filesDrop', evt, view)
}
});
/**
* Uploader service helps you to uplad any files in your Ember app
*
* @class Service
* @memberof Uploader
*/
export var Service = Ember.Object.extend(Ember.Evented, {
forbiddenUpload: true,
filesTypeUnacceptable: false,
filesSizeUnacceptable: false,
readyToUpload: false,
uploading: false,
limitError: false,
dataTransfer: null,
_handlers: 0,
uploadProgress: 0,
processing: false,
noHandlers: Ember.computed.not('_handlers'),
files: [],
rejected: Ember.computed.any('forbiddenUpload', 'noHandlers', 'filesTypeUnacceptable', 'filesSizeUnacceptable'),
/**
* Check user permissions to upload files
*
* @method userCan
* @memberof Service
*/
userCan: function () {
this.set('forbiddenUpload', false)
return true
},
handlersExist: function () {
return !this.get('noHandlers')
},
_filesDragStartHandler: function (evt, view) {
this.trigger('show')
if (this.userCan()) {
if (this.handlersExist()) {
this.trigger('dragStart')
}
}
},
_filesDragStopHandler: function (evt, view) {
this.trigger('hide')
this.set('forbiddenUpload', false)
this.set('filesTypeUnacceptable', false)
this.set('limitError', false)
this.set('filesSizeUnacceptable', false)
},
_filesDropHandler: function (evt, view) {
this.set('dataTransfer', evt.dataTransfer)
if (typeof view.handleFiles === 'function') {
view.handleFiles(this.get('dataTransfer.files'))
} else {
// missed drop
1;
}
},
_showHandler: function () {
this.set('readyToUpload', true)
},
_hideHandler: function () {
this.set('readyToUpload', false)
},
_initEventHandlers: function () {
this.on('filesDragStart', this._filesDragStartHandler)
this.on('filesDragStop', this._filesDragStopHandler)
this.on('filesDrop', this._filesDropHandler)
this.on('show', this._showHandler)
this.on('hide', this._hideHandler)
},
init: function () {
this._initEventHandlers()
},
addHandler: function () {
this.set('_handlers', this.get('_handlers') + 1)
},
removeHandler: function () {
this.set('_handlers', this.get('_handlers') - 1)
},
uploadFiles: function (endPoint) {
var self = this
var uploader = Uploader.create({ url: endPoint })
this.set('uploading', true)
this.set('readyToUpload', false)
this.set('uploadProgress', 0)
uploader.on('progress', function (e) {
self.set('uploadProgress', Math.round(e.percent))
if (Math.round(e.percent) === 100) {
self.set('uploading', false)
self.set('processing', true)
}
})
uploader.on('didUpload', function (data) {
self.set('uploading', false)
self.set('processing', false)
})
return uploader.upload(this.get('files'))
}
});
/**
* Uploader Component adds dnd uploader view to your ember app.
*
* @class UploaderComponent
*/
export var Component = Ember.Component.extend({
tagName: 'section',
classNames: ['uploader-zone'],
classNameBindings: ['opened', 'error'],
opened: Ember.computed.alias('uploader.readyToUpload'),
error: Ember.computed.alias('uploader.rejected'),
active: false,
defaults: {
acceptTypes: "*",
multiple: true,
maximumFileSize: 0,
maximumSummarySize: 0,
filesLimit: 0,
autoUpload: true,
autoUploadSingle: true,
endPoint: ENV.cdnPrefix
},
/**
* Uploader options
*
* Default options:
* - acceptTypes: "*"
* - multiple: true
* - maximumFileSize: 0
* - maximumSummarySize: 0
* - filesLimit: 0
* - autoUpload: true
* - autoUploadSingle: true
* - endPoint: '/cdn/'
*
* @type {Array}
*/
options: [],
init: function () {
this._super()
this.uploader.addHandler()
},
willDestroy: function () {
this._super()
this.uploader.removeHandler()
},
finishUpload: function (data) {
var uploader = this.uploader
var options = _.defaults(this.get('options'), this.get('defaults'))
var file
uploader.set('files', [])
// console.log(data.length, this.uploader.get('files.length'));
this.uploader.trigger('hide')
this.sendAction('action', data)
},
handleFiles: function (FileList) {
var length = FileList.length
var uploader = this.uploader
var options = _.defaults(this.get('options'), this.get('defaults'))
var acceptedByType = []
var acceptedBySize = []
var files = []
// check for multiple
if (!options.multiple && length > 1) {
uploader.set('limit', 1)
uploader.set('limitError', true)
}
// check length
if (options.filesLimit !== 0 && length > options.filesLimit) {
uploader.set('limit', options.filesLimit)
uploader.set('limitError', true)
}
for (var i = 0; i < length; i++) {
var file = FileList.item(i)
// check types
if (options.acceptTypes !== '*') {
// RegExp
if (_.isRegExp(options.acceptTypes) && options.acceptTypes.test(file.type)) {
acceptedByType.push(file)
} else
// string
if (_.isString(options.acceptTypes) && options.acceptTypes === file.type) {
acceptedByType.push(file)
} else
// array
if (_.isArray(options.acceptTypes) && options.acceptTypes.indexOf(file.type) !== -1) {
acceptedByType.push(file)
} else {
file.badType = true
}
} else {
acceptedByType.push(file)
}
// check sizes
if (options.maximumFileSize !== 0) {
if (options.maximumFileSize < file.size) {
file.badSize = true
} else {
acceptedBySize.push(file)
}
} else {
acceptedBySize.push(file)
}
// TODO check comuted size
// images
// if (/image\/.*/.test(file.type)) {
// console.log('image', file);
// file.kind = 'image'
// var img = document.createElement("img")
// img.file = file
// var reader = new FileReader();
// reader.onload = (function(aImg) { return function(e) {
// aImg.src = e.target.result;
//
// }; })(img);
// reader.readAsDataURL(file);
// console.log("%O", img);
// }
file.checked = true
files.push(file)
}
if (acceptedByType.length === 0) {
uploader.set('filesTypeUnacceptable', true)
this.set('error', true)
return
}
if (acceptedBySize.length === 0) {
uploader.set('filesSizeUnacceptable', true)
this.set('error', true)
return
}
uploader.set('files', files)
if (length === 1 && options.autoUploadSingle) {
uploader.uploadFiles(options.endPoint)
.then(Ember.run.bind(this, 'finishUpload'))
} else if (options.autoUpload) {
uploader.uploadFiles(options.endPoint)
.then(Ember.run.bind(this, 'finishUpload'))
} else {
uploader.showFileList()
}
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment