Skip to content

Instantly share code, notes, and snippets.

@ksprwhite
Last active October 10, 2015 12:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ksprwhite/a720bcf74dbd55ec3ab8 to your computer and use it in GitHub Desktop.
Save ksprwhite/a720bcf74dbd55ec3ab8 to your computer and use it in GitHub Desktop.
var FileUploader = (function($global) {
var defaults = {
'wrapperStyle': '',
'url': null,
'chooseText': 'Seleccionar archivos'
},
context = null;
function Uploader(element, settings) {
context = this;
this.settings = settings;
this.defaults = defaults;
this.init(this.element = element);
}
Uploader.prototype = {
formData: new FormData(),
additionalFields: {},
form: false,
submitted: false,
wrapper: null,
callbacks: {
'progress': null,
'error': null,
'complete': null,
'success': null,
'choose': null
},
/* inicializamos */
init: function(element) {
var parent;
this.instance.element = element;
/* check parent form */
parent = element;
do {
parent = parent.parentNode;
if (parent.tagName == 'FORM') {
this.form = parent;
break;
}
} while (parent);
if (this.form) {
this.form.onsubmit = function() {
context.upload();
return false;
};
}
/* check multiple attribute */
if (!this.element.multiple) {
this.element.multiple = true;
}
this.prepareSettings();
this.setWrapper();
},
prepareSettings: function() {
for (var _default in this.defaults) {
if (!(_default in this.settings)) {
this.settings[_default] = this.defaults[_default];
}
}
for (var setting in this.settings) {
var value = this.settings[setting];
switch (setting) {
case 'additional':
// check aditional inputs
if (Object.prototype.toString.call(value) == '[object Object]') {
for (var key in this.settings.additional) {
if (!this.settings.additional.hasOwnProperty(key)) {
continue;
}
this.additionalFields[key] = value;
}
} else if (Object.prototype.toString.call(value) == '[object String]' &&
document.querySelector(value)) {
[].forEach.call(document.querySelector(value).children, function(element, index) {
if (element === context.element ||
element.tagName !== 'INPUT' ||
!element.getAttribute('name')) {
return;
}
context.additionalFields[element.getAttribute('name')] = element.value;
});
}
break;
case 'url':
if (!value && this.form && this.form.getAttribute('action')) {
this.settings[setting] = this.form.getAttribute('action');
} else if (!value) {
this.settings[settings] = '';
}
break;
}
}
},
reset: function() {
// clear input value
this.element.value = '';
if (this.element.value) {
this.element.type = 'text';
this.element.type = 'file';
}
this.element.disabled = false;
// clear additional inputs
this.additionalFields = {};
// reset formData
this.formData = new FormData();
// enable submit
this.submitted = false;
// set defaults and settings
this.prepareSettings();
// reset wrapper
this.setWrapper();
},
upload: function() {
if (!this.element.files.length && !this.additionalFields.length)
return false;
if (this.submitted) {
return false;
} else {
this.wrapper.className = [this.wrapper.className, 'disabled'].join(' ');
this.element.disabled = true;
this.submitted = true;
}
// add files
[].forEach.call(this.element.files, function(file, index) {
context.formData.append(context.element.getAttribute('name'), file);
});
// add extra fields
Object.keys(this.additionalFields).forEach(function(key) {
context.formData.append(key, context.additionalFields[key]);
});
// create XMLHttpRequest object
var xhr = new XMLHttpRequest();
// handle progress
xhr.upload.addEventListener('progress', function(event) {
if (event.lengthComputable) {
var progress = (event.loaded / event.total) * 100;
context.performCallback('progress', [progress, event]);
}
}, false);
// handle complete callback
xhr.upload.addEventListener('load', function(event) {
context.performCallback('complete', [event]);
}, false);
// handle error
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status !== 200) {
context.performCallback('error', [xhr]);
} else {
context.performCallback('success', [xhr]);
}
}
};
// open and send data
xhr.open('POST', this.settings.url, true);
xhr.send(this.formData);
},
setCallback: function(callback, handler) {
if (!(callback in this.callbacks))
return false;
this.callbacks[callback] = handler;
},
performCallback: function(callback, args) {
if (this.callbacks[callback]) {
this.callbacks[callback].apply(this.instance, args);
}
},
setWrapper: function() {
var parent = this.form ? this.form : this.element.parentNode,
wrapper = document.createElement('div'),
button = document.createElement('a'),
info = document.createElement('div');
if (this.wrapper) {
this.element.parentNode.removeChild(this.wrapper);
}
this.wrapper = wrapper;
this.element.style.display = 'none';
button.className = 'button';
button.innerHTML = this.settings.chooseText;
button.href = '#';
info.className = 'info';
this.wrapper.className = this.settings.wrapperStyle;
this.wrapper.appendChild(button);
this.wrapper.appendChild(info);
button.addEventListener('click', function(event) {
context.element.click();
event.preventDefault();
return false;
});
this.element.addEventListener('change', function() {
info.innerHTML = context.element.files.length + ' archivos selecionados';
context.performCallback('choose', [context.element.files]);
});
if (this.element.files.length) {
info.innerHTML = this.element.files.length + ' archivos selecionados';
}
parent.insertBefore(this.wrapper, this.element);
},
instance: {
element: null,
upload: function() {
context.upload();
return this;
},
reset: function() {
context.reset();
return this;
},
on: function(callback, handler) {
context.setCallback(callback, handler);
return this;
}
}
};
return (function(selector, settings) {
var element;
$global._UploaderInstances = $global._UploaderInstances || {};
if (!(element = document.querySelector(selector))) {
throw new Error(['[', selector, ']', ' element is undefined.'].join(''));
} else if (element.nodeName !== 'INPUT' || element.type !== 'file') {
throw new Error(['[', selector, ']', ' input must be file type.'].join(''));
} else if (!$global._UploaderInstances[element]) {
return $global._UploaderInstances[element] = new Uploader(element, settings).instance;
} else {
return $global._UploaderInstances[element];
}
});
}(window));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment