UNPKG

ais-uploader

Version:

Simple uploader library for Angular 2+

1,033 lines (1,022 loc) 33.1 kB
import { last, map } from 'rxjs/operators'; import { __awaiter } from 'tslib'; import { Injectable, Pipe, NgModule, ComponentFactoryResolver, Directive, ElementRef, HostListener, Input, Renderer2, ViewContainerRef, defineInjectable, EventEmitter, Component, ViewEncapsulation, Inject, PLATFORM_ID, Output, ViewChild } from '@angular/core'; import { BehaviorSubject, Subscription } from 'rxjs'; import { isPlatformServer, CommonModule } from '@angular/common'; import { HttpClient, HttpEventType, HttpClientModule } from '@angular/common/http'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class AisUploaderService { /** * @param {?} http */ constructor(http) { this.http = http; this._uploadingProgress$ = new BehaviorSubject(0); } /** * @return {?} */ get uploadingProgress() { return this._uploadingProgress$.asObservable(); } /** * @param {?} file * @param {?} config * @return {?} */ upload(file, config) { if (!file) return null; this._uploadingProgress$.next(0); /** @type {?} */ const formData = new FormData(); formData.append('file', file); return this.http.post(`${config.apiUrl}`, formData, { headers: config.headers, responseType: config.responseType, reportProgress: true, observe: 'events', }).pipe(map(event => this.getEventMessage(event)), last()); } /** * @param {?} files * @param {?} config * @return {?} */ uploadMultiple(files, config) { if (!files || !files.length) return null; /** @type {?} */ const formData = new FormData(); for (const file of files) { formData.append('file', file); } return this.http.post(`${config.apiUrl}`, formData, { headers: config.headers, responseType: config.responseType, reportProgress: true, observe: 'events', }).pipe(map(event => this.getEventMessage(event)), last()); } /** * @param {?} event * @return {?} */ getEventMessage(event) { if (event.type) { switch (event.type) { case HttpEventType.UploadProgress: /** @type {?} */ const percentDone = Math.round(100 * event.loaded / event.total); this.showProgress(percentDone); return; case HttpEventType.Response: this.resetProgress(); return event.body; default: return 0; } } } /** * @param {?} persentage * @return {?} */ showProgress(persentage) { this._uploadingProgress$.next(persentage); } /** * @return {?} */ resetProgress() { } } AisUploaderService.decorators = [ { type: Injectable } ]; /** @nocollapse */ AisUploaderService.ctorParameters = () => [ { type: HttpClient } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @enum {string} */ const DocumentFileType = { DOCX: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', DOC: 'application/msword', PDF: 'application/pdf', XLSX: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', XLS: 'application/vnd.ms-excel', PPTX: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', PPT: 'application/vnd.ms-powerpoint', TXT: 'text/plain', MP4: 'video/mp4', AVI: 'video/avi', MPEG: 'video/mpeg', FLV: 'video/x-flv', WEBM: 'video/webm', OGG: 'video/ogg', MOV: 'video/quicktime', MSCSV: 'application/vnd.ms-excel', CSV: 'text/csv', JPEG: 'image/jpeg', JPG: 'image/jpeg', PNG: 'image/png', WEBP: 'image/webp', WBMP: 'image/vnd.wap.wbmp', SVG: 'image/svg+xml', TIFF: 'image/tiff', GIF: 'image/gif', MP3: 'audio/mpeg', WAV: 'audio/wav', XWAV: 'audio/x-wav', }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class UploaderTypesPipe { constructor() { this.DocumentFileType = DocumentFileType; } /** * @param {?} value * @param {?=} isAccept * @return {?} */ transform(value, isAccept = false) { if (!value) { return; } /** @type {?} */ const keys = []; for (const key of value) { for (const i in this.DocumentFileType) { if (key === this.DocumentFileType[i]) { if (keys.includes(`.${i.toLowerCase()}`) || keys.includes(i.toLowerCase())) { continue; } keys.push(isAccept ? `.${i.toLowerCase()}` : i.toLowerCase()); } } } return keys.join(', '); } } UploaderTypesPipe.decorators = [ { type: Injectable, args: [{ providedIn: 'root', },] }, { type: Pipe, args: [{ name: 'uploaderTypes', },] } ]; /** @nocollapse */ UploaderTypesPipe.ngInjectableDef = defineInjectable({ factory: function UploaderTypesPipe_Factory() { return new UploaderTypesPipe(); }, token: UploaderTypesPipe, providedIn: "root" }); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class AisUploaderComponent { /** * @param {?} platformId * @param {?} uploderService * @param {?} uploaderEnum */ constructor(platformId, uploderService, uploaderEnum) { this.uploderService = uploderService; this.uploaderEnum = uploaderEnum; this.onChange = new EventEmitter(); this.onProgress = new EventEmitter(); this.onError = new EventEmitter(); this._allowedExtensions = []; if (isPlatformServer(platformId) || typeof ((/** @type {?} */ (window))).MouseEvent === 'function') { return; } /** * @param {?} event * @param {?} params * @return {?} */ function CustomEvent(event, params) { params = params || { bubbles: false, cancelable: false, detail: undefined }; /** @type {?} */ const evt = document.createEvent('CustomEvent'); evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); return evt; } CustomEvent.prototype = ((/** @type {?} */ (window))).Event.prototype; ((/** @type {?} */ (window))).CustomEvent = CustomEvent; } /** * @return {?} */ ngOnInit() { this._generateAllowedExtensions(); } /** * @return {?} */ ngOnDestroy() { if (this._progressSub$) { this._progressSub$.unsubscribe(); } if (this._uploaderSub$) { this._uploaderSub$.unsubscribe(); } } // return file name or arr file names if isMultiple: true /** * @return {?} */ get fileName() { if (this.config.isMultiple) { return this.filesNames; } if (!this._file) { return ''; } return this._file.name; } // return arr file names if isMultiple: true /** * @return {?} */ get filesNames() { if (!this._files || !this._files.length) { return undefined; } return this._files.map(file => file.name); } // return info message /** * @return {?} */ get tooltipMessage() { if (!this.config.supportedFormats.length) { return `Max. size is ${this.config.maxSize ? this.config.maxSize : 'unlimited'} Mb`; } /** @type {?} */ const formats = this.uploaderEnum.transform(this.config.supportedFormats); return `Supported formats are ${formats || 'All formats'}. Max. size is ${this.config.maxSize ? this.config.maxSize : 'unlimited'} Mb`; } // open selction window /** * @return {?} */ select() { /** @type {?} */ let event; if (typeof ((/** @type {?} */ (window))).MouseEvent === 'function') { event = new MouseEvent('click', {}); } else { event = new CustomEvent('click'); } this.fileSelector.nativeElement.dispatchEvent(event); } // remove file from files arr /** * @param {?} i * @return {?} */ delete(i) { this._files.splice(i, 1); } // remove file or all files /** * @param {?=} emit * @return {?} */ clear(emit = true) { this._file = undefined; this._files = undefined; this.fileSelector.nativeElement.value = ''; if (emit) { this.onChange.emit(''); } } /** * @param {?} event * @return {?} */ loadFile(event) { return __awaiter(this, void 0, void 0, function* () { if (this.config.isMultiple) { /** @type {?} */ const _files = event.target.files; for (const file of _files) { /** @type {?} */ const isValid = yield this.validate(file); if (!isValid) { return; } } this._files = [..._files]; } else { /** @type {?} */ const _file = event.target.files[0]; if (!_file) { return; } /** @type {?} */ const isValid = yield this.validate(_file); if (!isValid) { return; } this._file = _file; } if (this.config.isAutoupload) { try { /** @type {?} */ const res = yield this.upload(); this.onChange.emit(res); } catch (e) { this.onError.emit(e); } return; } if (!this.config.isPreviewDisabled) { this._makePreview(); return; } this.onChange.emit(this._file.name); }); } // upload to server /** * @return {?} */ upload() { if (this._progressSub$) { this._progressSub$.unsubscribe(); } this._onLoaderSub(); if (!this.config.isMultiple) { return new Promise(resolve => { this._uploaderSub$ = this.uploderService.upload(this._file, this.config).subscribe(res => { this.onProgress.emit(0); this.uploadingProgress = 0; resolve(res); }, err => { this.onError.emit('Uploading error'); this.preventUploading(); resolve(undefined); }); }); } else { return new Promise(resolve => { this._uploaderSub$ = this.uploderService.uploadMultiple(this._files, this.config).subscribe(res => { this.onProgress.emit(0); this.uploadingProgress = 0; resolve(res); }, err => { this.onError.emit('Uploading error'); this.preventUploading(); resolve(undefined); }); }); } } // cancel current uploading /** * @return {?} */ preventUploading() { if (!this.uploadingProgress) { return; } if (this._progressSub$) { this._progressSub$.unsubscribe(); } if (this._uploaderSub$) { this._uploaderSub$.unsubscribe(); } this.onProgress.emit(0); this.uploadingProgress = 0; this.clear(); } /** * @private * @param {?} file * @return {?} */ validate(file) { return __awaiter(this, void 0, void 0, function* () { if (!file || !this.config) { this.throwError('Uploader error'); return false; } if (this.config.maxSize && file.size / 1024 / 1024 > this.config.maxSize) { this.throwError(`File size should be no more than ${this.config.maxSize}Mb`); return false; } if (this.config.supportedFormats.length && file.type && !this.config.supportedFormats.includes(file.type)) { this.throwError(`Unsupported file format`); return false; } if (this.config.supportedFormats.length && !file.type && !this._validateFormat(file.name)) { this.throwError(`Unsupported file format`); return false; } if (this.config.imageResolution) { /** @type {?} */ let resolution = this.config.imageResolution.split('x'); /** @type {?} */ let img; if (!resolution || !resolution.length || resolution.length !== 2) { this.throwError(`Unsupported resolution format`); return false; } resolution = resolution.map(r => Math.ceil(Number(r))).filter(n => !!n); if (!resolution || !resolution.length || resolution.length !== 2) { this.throwError(`Unsupported resolution format`); return false; } try { img = yield this._addImageProcess(file); } catch (e) { console.warn('Resolution property allows only for Images - Error Skipped'); return true; } if (img.width !== resolution[0] || img.height !== resolution[1]) { this.throwError(`Unsupported resolution format! Correct resolution is ${this.config.imageResolution}`); return false; } } return true; }); } /** * @private * @param {?} file * @return {?} */ _addImageProcess(file) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { // @ts-ignore /** @type {?} */ const _URL = window.URL || window.webkitURL; /** @type {?} */ const img = new Image(); /** @type {?} */ const objectUrl = _URL.createObjectURL(file); img.onload = () => { _URL.revokeObjectURL(objectUrl); return resolve({ height: img.height, width: img.width }); }; img.onerror = reject; img.src = objectUrl; }); }); } /** * @private * @return {?} */ _onLoaderSub() { this._progressSub$ = this.uploderService.uploadingProgress .subscribe(progress => { this.onProgress.emit(progress); this.uploadingProgress = progress; }); } /** * @private * @return {?} */ _makePreview() { if (!this.config.isMultiple) { /** @type {?} */ const reader = new FileReader(); reader.onload = (event) => { this.onChange.emit(event.target.result); }; reader.readAsDataURL(this._file); } else { /** @type {?} */ const dataArr = []; for (let i = 0; i < this._files.length; i++) { /** @type {?} */ const reader = new FileReader(); reader.onload = (event) => { dataArr[i] = (event.target.result); }; reader.readAsDataURL(this._files[i]); if (i == this._files.length - 1) { this.onChange.emit(dataArr); } } } } /** * @private * @param {?} name * @return {?} */ _validateFormat(name) { if (!this.config.supportedFormats.length) { return true; } /** @type {?} */ const extension = name.split('.').pop().toLowerCase(); return this._allowedExtensions.some(s => s === extension); } /** * @private * @param {?} message * @return {?} */ throwError(message) { this.onError.emit(message); this.fileSelector.nativeElement.value = ''; } // tslint:disable-next-line:cyclomatic-complexity /** * @private * @return {?} */ _generateAllowedExtensions() { if (!this.config || !this.config.supportedFormats) { return; } for (const format of this.config.supportedFormats) { switch (format) { case DocumentFileType.DOC: this._allowedExtensions.push('doc'); break; case DocumentFileType.DOCX: this._allowedExtensions.push('docx'); break; case DocumentFileType.XLSX: this._allowedExtensions.push('xlsx'); break; case DocumentFileType.XLS: this._allowedExtensions.push('xls'); break; case DocumentFileType.PDF: this._allowedExtensions.push('pdf'); break; case DocumentFileType.TXT: this._allowedExtensions.push('txt'); break; case DocumentFileType.PPTX: this._allowedExtensions.push('pptx'); break; case DocumentFileType.PPT: this._allowedExtensions.push('ppt'); break; case DocumentFileType.AVI: this._allowedExtensions.push('avi'); break; case DocumentFileType.FLV: this._allowedExtensions.push('flv'); break; case DocumentFileType.MP4: this._allowedExtensions.push('mp4'); break; case DocumentFileType.MPEG: this._allowedExtensions.push('mpeg'); break; case DocumentFileType.OGG: this._allowedExtensions.push('ogg'); break; case DocumentFileType.WEBM: this._allowedExtensions.push('webm'); break; case DocumentFileType.JPEG: this._allowedExtensions.push('jpeg'); break; case DocumentFileType.JPG: this._allowedExtensions.push('jpg'); break; case DocumentFileType.PNG: this._allowedExtensions.push('png'); break; case DocumentFileType.TIFF: this._allowedExtensions.push('tiff'); break; case DocumentFileType.WBMP: this._allowedExtensions.push('wbmp'); break; case DocumentFileType.WEBP: this._allowedExtensions.push('webp'); break; case DocumentFileType.SVG: this._allowedExtensions.push('svg'); break; case DocumentFileType.GIF: this._allowedExtensions.push('gif'); break; case DocumentFileType.CSV: this._allowedExtensions.push('csv'); break; case DocumentFileType.MSCSV: this._allowedExtensions.push('csv'); break; case DocumentFileType.MOV: this._allowedExtensions.push('mov'); break; case DocumentFileType.MP3: this._allowedExtensions.push('mp3'); break; case DocumentFileType.WAV: this._allowedExtensions.push('wav'); break; case DocumentFileType.XWAV: this._allowedExtensions.push('wav'); break; default: break; } } } } AisUploaderComponent.decorators = [ { type: Component, args: [{ selector: 'ais-uploader', template: "<input id=\"uploader_lib\" #fileSelector type=\"file\" [accept]=\"config?.supportedFormats | uploaderTypes:true\"\r\n (change)=\"loadFile($event)\">\r\n", encapsulation: ViewEncapsulation.None, styles: ["#uploader_lib{display:none}@keyframes blink{0%,100%{box-shadow:0 2px 2px rgba(0,0,0,.24),0 0 2px rgba(0,0,0,.12)}50%{box-shadow:none}}@-webkit-keyframes blink{0%,100%{box-shadow:0 2px 2px rgba(0,0,0,.24),0 0 2px rgba(0,0,0,.12)}50%{box-shadow:0 0 0}}.blink{-webkit-animation:3s linear infinite blink;animation:3s linear infinite blink}"] }] } ]; /** @nocollapse */ AisUploaderComponent.ctorParameters = () => [ { type: String, decorators: [{ type: Inject, args: [PLATFORM_ID,] }] }, { type: AisUploaderService }, { type: UploaderTypesPipe } ]; AisUploaderComponent.propDecorators = { onChange: [{ type: Output }], onProgress: [{ type: Output }], onError: [{ type: Output }], fileSelector: [{ type: ViewChild, args: ['fileSelector',] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class UploaderConfig { /** * @param {?} apiUrl * @param {?=} formats * @param {?=} isMultiple * @param {?=} isDropAllowed * @param {?=} maxSize * @param {?=} isAutoupload * @param {?=} isPreviewDisabled */ constructor(apiUrl, formats = [], isMultiple = false, isDropAllowed = false, maxSize = 0, isAutoupload = false, isPreviewDisabled = false) { this.apiUrl = apiUrl; this.supportedFormats = formats; this.isPreviewDisabled = isPreviewDisabled; this.maxSize = maxSize; this.isAutoupload = isAutoupload; this.isMultiple = isMultiple; this.isDropAllowed = isDropAllowed; } } var UploaderResponseType; (function (UploaderResponseType) { UploaderResponseType.JSON = (/** @type {?} */ ('json')); UploaderResponseType.BUFFER = (/** @type {?} */ ('arraybuffer')); UploaderResponseType.BLOB = (/** @type {?} */ ('blob')); UploaderResponseType.TEXT = (/** @type {?} */ ('text')); })(UploaderResponseType || (UploaderResponseType = {})); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class UploaderDirective { /** * @param {?} factory * @param {?} ref * @param {?} viewContainerRef * @param {?} renderer */ constructor(factory, ref, viewContainerRef, renderer) { this.factory = factory; this.ref = ref; this.viewContainerRef = viewContainerRef; this.renderer = renderer; this.onChange = new EventEmitter(); this.onProgress = new EventEmitter(); this.onError = new EventEmitter(); this._sub$ = new Subscription(); } /** * @return {?} */ get config() { return this._config; } /** * @param {?} config * @return {?} */ set config(config) { if (config === this._config) { return; } this._config = config; } /** * @return {?} */ ngOnInit() { if (!this.config.apiUrl || !this.config.supportedFormats) { this.onError.emit('Incorrect config!'); return; } this._setBtnContainer(); } /** * @return {?} */ ngOnDestroy() { this.viewContainerRef.clear(); if (this._clickFn) { this._clickFn(); } } // uploader instance /** * @return {?} */ get instance() { if (!this._btnContainer) { return; } return this._btnContainer.instance; } // selected file name /** * @return {?} */ get fileName() { if (!this.instance) { return; } return this.instance.fileName; } // info message /** * @return {?} */ get tooltipMessage() { if (!this.instance) { return; } return this.instance.tooltipMessage; } // get progress /** * @return {?} */ get progress() { if (!this.instance) { return; } return this.instance.uploadingProgress; } // delete file from uploader /** * @param {?} index * @return {?} */ delete(index) { if (!this.instance) { return; } this.instance.delete(index); } // remove file from uploader /** * @param {?=} emit * @return {?} */ clear(emit = true) { if (!this.instance) { return; } this.instance.clear(emit); } // cancel uploading /** * @return {?} */ preventUploading() { if (!this.instance) { return; } this.instance.preventUploading(); } // start uploading /** * @return {?} */ upload() { if (!this.instance) { return; } return this.instance.upload(); } // DROP /** * @param {?} event * @return {?} */ onDragOver(event) { /** @type {?} */ const transfer = this._getTransfer(event); if (!this._haveFiles(transfer.types)) { return; } transfer.dropEffect = 'copy'; this._preventAndStop(event); } /** * @param {?} event * @return {?} */ onDragLeave(event) { if (((/** @type {?} */ (this))).element) { if (event.currentTarget === ((/** @type {?} */ (this))).element[0]) { return; } } this._preventAndStop(event); } /** * @param {?} event * @return {?} */ onDrop(event) { if (!this.config.isDropAllowed) { return; } /** @type {?} */ const transfer = this._getTransfer(event); if (!transfer) { return; } this._preventAndStop(event); this.instance.loadFile({ target: { files: transfer.files } }); } /** * @protected * @param {?} event * @return {?} */ _getTransfer(event) { return event.dataTransfer ? event.dataTransfer : event.originalEvent.dataTransfer; // jQuery fix; } /** * @protected * @param {?} event * @return {?} */ _preventAndStop(event) { event.preventDefault(); event.stopPropagation(); } /** * @protected * @param {?} types * @return {?} */ _haveFiles(types) { if (!types) { return false; } if (types.indexOf) { return types.indexOf('Files') !== -1; } else if (types.contains) { return types.contains('Files'); } else { return false; } } // DROP END /** * @private * @return {?} */ _setBtnContainer() { /** @type {?} */ const componentFactory = this.factory.resolveComponentFactory(AisUploaderComponent); this._btnContainer = this.viewContainerRef.createComponent(componentFactory); this._btnContainer.instance.config = this.config; this._clickFn = this.renderer.listen(this.ref.nativeElement, 'click', (e) => this._handleClick(e)); this._sub$.add(this._btnContainer.instance.onChange.subscribe(data => this.onChange.emit(data))); this._sub$.add(this._btnContainer.instance.onError.subscribe(err => this.onError.emit(err))); this._sub$.add(this._btnContainer.instance.onProgress.subscribe(progress => this.onProgress.emit(progress))); } /** * @private * @param {?} event * @return {?} */ _handleClick(event) { /** @type {?} */ const attributes = Object.values(event.target.attributes).map(el => el['name']); if (!!attributes.find(attr => attr == 'clear') && this.fileName && !this.instance.uploadingProgress) { this.clear(); return; } if (!!attributes.find(attr => attr == 'stop') && this.fileName && this.instance.uploadingProgress) { this.preventUploading(); return; } this._btnContainer.instance.select(); } } UploaderDirective.decorators = [ { type: Directive, args: [{ selector: '[uploader]', exportAs: 'uploader', },] } ]; /** @nocollapse */ UploaderDirective.ctorParameters = () => [ { type: ComponentFactoryResolver }, { type: ElementRef }, { type: ViewContainerRef }, { type: Renderer2 } ]; UploaderDirective.propDecorators = { config: [{ type: Input, args: ['uploader',] }], onChange: [{ type: Output }], onProgress: [{ type: Output }], onError: [{ type: Output }], onDragOver: [{ type: HostListener, args: ['dragover', ['$event'],] }], onDragLeave: [{ type: HostListener, args: ['dragleave', ['$event'],] }], onDrop: [{ type: HostListener, args: ['drop', ['$event'],] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class AisUploaderModule { /** * @return {?} */ static forRoot() { return { ngModule: AisUploaderModule, providers: [], }; } } AisUploaderModule.decorators = [ { type: NgModule, args: [{ declarations: [ AisUploaderComponent, UploaderDirective, UploaderTypesPipe, ], imports: [ CommonModule, HttpClientModule, ], exports: [ AisUploaderComponent, UploaderDirective, ], providers: [ AisUploaderService, ], entryComponents: [ AisUploaderComponent, ], },] } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ export { AisUploaderService, AisUploaderComponent, AisUploaderModule, UploaderConfig, UploaderResponseType, DocumentFileType, UploaderDirective, UploaderTypesPipe as ɵa }; //# sourceMappingURL=ais-uploader.js.map