UNPKG

lite-uploader

Version:

Lightweight nodeJS/jQuery File Uploader

287 lines (233 loc) 7.72 kB
(function (factory) { /* istanbul ignore else */ if (typeof module === "object" && typeof module.exports === "object") { module.exports = factory(global.$); } else { factory($); } }(function ($) { /* istanbul ignore next */ if ($ && $.fn) { $.fn.liteUploader = function (options) { return this.each(function () { options.ref = options.ref || $(this).attr("name"); var getFiles = function () { return $(this).get(0).files; }.bind(this); var onEvent = function (name, data) { $(this).trigger.bind($(this))(name, [data]); }.bind(this) $.data(this, "liteUploader", new LiteUploader(options, onEvent, getFiles)); }); }; } function LiteUploader (options, onEvent, getFiles) { this.options = this._applyDefaults(options); this._getFiles = getFiles || function () { return null}; this._triggerEvent = onEvent; this.xhrs = []; } LiteUploader.prototype = { _applyDefaults: function (options) { return Object.assign({ url: null, ref: null, method: "POST", rules: {}, params: {}, headers: {}, validators: [], singleFileUploads: false, withCredentials: false, beforeRequest: null, sendAsFormData: true, }, options); }, _init: function (files) { files = files || this._getFiles(); if (!files || !files.length) throw new Error("No files"); return Promise.all([this._validateOptions(), this._validateFiles(files)]) .then(function (allErrors) { var errors = allErrors[0]; if (!errors) errors = allErrors[1]; if (errors) { this._triggerEvent("lu:errors", {errors}); } else { this._triggerEvent("lu:start", {files}); this.xhrs = []; this._startUpload(files); } }.bind(this)); }, _startUpload: function (files) { const promises = this._splitFiles(files).map(function (fileSplit) { return this._buildXhrObject(fileSplit) .then(function(xhr) { return this._beforeRequest(fileSplit) .then(function (formData) { if (!this.options.sendAsFormData && this.options.singleFileUploads) return xhr.send(fileSplit[0]) return xhr.send(formData); }.bind(this)); }.bind(this)) }.bind(this)); return Promise.all(promises) }, _splitFiles: function (files) { if (this.options.singleFileUploads) { return Array.prototype.map.call(files, function (file) { return [file]; }); } else { return [files]; } }, _beforeRequest: function (files) { this._triggerEvent("lu:before", {files}); var formData = this._collateFormData(files); return this.options.beforeRequest ? this.options.beforeRequest(files, formData) : Promise.resolve(formData); }, _validateOptions: function () { var requiredOptions = ["url", "ref"]; var errors = requiredOptions.reduce(function (acc, option) { if (!this.options[option]) acc.push({ type: option + "Required" }); return acc; }.bind(this), []); if (!errors.length) return null; return [{ name: "_options", errors: errors }]; }, _allowedFileTypeValidator: function (rule, file) { var allowedTypes = rule.split(","); var isWildcardType = /([a-z]+)\/\*$/; if (allowedTypes.indexOf(file.type) !== -1) return; var allowed = allowedTypes.reduce(function(result, allowedType) { if (result) { return result; } else { var matches = allowedType.match(isWildcardType) || []; return matches[1] === file.type.split("/")[0]; } }, false); if (!allowed) { return { type: "type", rule: rule, given: file.type }; } }, _maxSizeValidator: function (rule, file) { if (file.size > rule) { return { type: "size", rule: rule, given: file.size }; } }, _validateFiles: function (files) { var promises = Array.prototype.map.call(files, function (file) { return this._validateFile(file); }.bind(this)); return Promise.all(promises) .then(function (fileErrors) { var allErrors = fileErrors.filter(function (file) { return file.errors.length; }); return (allErrors.length) ? allErrors : null; }); }, _validateFile: function (file) { var validatorMap = { "allowedFileTypes": this._allowedFileTypeValidator, "maxSize": this._maxSizeValidator }; var builtIn = Object.keys(this.options.rules).reduce(function (acc, key) { var rule = this.options.rules[key]; if (rule && validatorMap[key]) acc.push(validatorMap[key](rule, file)); return acc; }.bind(this), []); var custom = this.options.validators.map(function (fn) { return fn(file); }); return Promise.all(builtIn.concat(custom)) .then(function (fileErrors) { return { name: file.name, errors: fileErrors.filter(function (error) { return !!error; }) } }); }, _getFormDataObject: function() { return new FormData() }, _collateFormData: function (files) { var formData = this._getFormDataObject(); for (var key in this.options.params) { formData.append(key, this.options.params[key]); } Array.prototype.forEach.call(files, function (file) { formData.append(this.options.ref, file); }.bind(this)); return formData; }, _getXmlHttpRequestObject: function () { return new XMLHttpRequest(); }, _getScriptUrl: function(files) { if (typeof this.options.url === "function") return this.options.url(files) return Promise.resolve(this.options.url) }, _buildXhrObject: function (files) { return this._getScriptUrl(files) .then(function(url) { var xhr = this._getXmlHttpRequestObject(); xhr.open(this.options.method, url); if (this.options.withCredentials) xhr.withCredentials = true for (var key in this.options.headers) { xhr.setRequestHeader(key, this.options.headers[key]); } xhr.upload.addEventListener("progress", this._onXHRProgress.bind(this, files), false); xhr.onreadystatechange = function () { this._onXHRResponse(files, xhr); }.bind(this); this.xhrs.push(xhr); return xhr; }.bind(this)) }, _onXHRProgress: function (files, e) { if (!e.lengthComputable) return; this._triggerEvent("lu:progress", { files: files, percentage: Math.floor((e.loaded / e.total) * 100) }); }, _onXHRResponse: function (files, xhr) { if (xhr.readyState !== 4) return; this._triggerEvent("lu:finish"); if (xhr.status >= 200 && xhr.status < 300) { this._triggerEvent("lu:success", {files, response: xhr.responseText}); } else { this._triggerEvent("lu:fail", {xhr}); } }, /* Public Methods */ startUpload: function (files) { this._init(files); }, addParam: function (key, value) { this.options.params[key] = value; }, cancelUpload: function () { this._triggerEvent("lu:cancelled"); this.xhrs.forEach(function (xhr) { xhr.abort(); }); } }; return LiteUploader; }));