UNPKG

ng2-file-upload

Version:
1,132 lines (1,120 loc) • 41.9 kB
import * as i0 from '@angular/core'; import { EventEmitter, input, output, inject, ElementRef, HostListener, Directive, NgModule } from '@angular/core'; // reports an error that cannot be rethrown, so it still reaches window.onerror and Angular's ErrorHandler function reportLater(error) { setTimeout(() => { throw error; }); } // runs then() even when action() throws; if both throw, the first error is rethrown and the second reported later function runThen(action, then) { try { action(); } catch (e) { try { then(); } catch (later) { reportLater(later); } throw e; } then(); } class FileLikeObject { constructor(fileOrInput) { this.rawFile = fileOrInput; const fakePathOrObject = fileOrInput instanceof HTMLInputElement ? fileOrInput.value : fileOrInput; const postfix = typeof fakePathOrObject === 'string' ? 'FakePath' : 'Object'; const method = `_createFrom${postfix}`; this[method](fakePathOrObject); } _createFromFakePath(path) { this.lastModifiedDate = void 0; this.size = void 0; this.type = `like/${path.slice(path.lastIndexOf('.') + 1).toLowerCase()}`; this.name = path.slice(path.lastIndexOf('/') + path.lastIndexOf('\\') + 2); } _createFromObject(object) { this.size = object.size; this.type = object.type; this.name = object.name; } } class FileItem { constructor(uploader, some, options) { this.url = '/'; this.headers = []; this.withCredentials = true; this.formData = []; this.isReady = false; this.isUploading = false; this.isUploaded = false; this.isSuccess = false; this.isCancel = false; this.isError = false; this.progress = 0; this.uploader = uploader; this.some = some; this.options = options; this.file = new FileLikeObject(some); this._file = some; if (uploader.options) { this.method = uploader.options.method || 'POST'; this.alias = uploader.options.itemAlias || 'file'; } this.url = uploader.options.url; } upload() { try { this.uploader.uploadItem(this); } catch (e) { // a callback that threw before the request: fail the item so the queue moves on, then rethrow try { runThen(() => this.uploader._onErrorItem(this, '', 0, {}), () => this.uploader._onCompleteItem(this, '', 0, {})); } catch (later) { reportLater(later); } throw e; } } cancel() { this.uploader.cancelItem(this); } remove() { this.uploader.removeFromQueue(this); } onBeforeUpload() { return void 0; } onBuildForm(form) { return { form }; } onProgress(progress) { return { progress }; } onSuccess(response, status, headers) { return { response, status, headers }; } onError(response, status, headers) { return { response, status, headers }; } onCancel(response, status, headers) { return { response, status, headers }; } onComplete(response, status, headers) { return { response, status, headers }; } _onBeforeUpload() { this.isReady = true; this.isUploading = true; this.isUploaded = false; this.isSuccess = false; this.isCancel = false; this.isError = false; this.progress = 0; this.onBeforeUpload(); } _onBuildForm(form) { this.onBuildForm(form); } _onProgress(progress) { this.progress = progress; this.onProgress(progress); } _onSuccess(response, status, headers) { this.isReady = false; this.isUploading = false; this.isUploaded = true; this.isSuccess = true; this.isCancel = false; this.isError = false; this.progress = 100; this.index = undefined; this.onSuccess(response, status, headers); } _onError(response, status, headers) { this.isReady = false; this.isUploading = false; this.isUploaded = true; this.isSuccess = false; this.isCancel = false; this.isError = true; this.progress = 0; this.index = undefined; this.onError(response, status, headers); } _onCancel(response, status, headers) { this.isReady = false; this.isUploading = false; this.isUploaded = false; this.isSuccess = false; this.isCancel = true; this.isError = false; this.progress = 0; this.index = undefined; this.onCancel(response, status, headers); } _onComplete(response, status, headers) { this.onComplete(response, status, headers); if (this.uploader.options.removeAfterUpload) { this.remove(); } } _prepareToUploading() { this.index = this.index || ++this.uploader._nextIndex; this.isReady = true; } } class FileType { /* MS office */ // tslint:disable-next-line:variable-name static { this.mime_doc = [ 'application/msword', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'application/vnd.ms-word.document.macroEnabled.12', 'application/vnd.ms-word.template.macroEnabled.12' ]; } // tslint:disable-next-line:variable-name static { this.mime_xsl = [ 'application/vnd.ms-excel', 'application/vnd.ms-excel', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'application/vnd.ms-excel.sheet.macroEnabled.12', 'application/vnd.ms-excel.template.macroEnabled.12', 'application/vnd.ms-excel.addin.macroEnabled.12', 'application/vnd.ms-excel.sheet.binary.macroEnabled.12' ]; } // tslint:disable-next-line:variable-name static { this.mime_ppt = [ 'application/vnd.ms-powerpoint', 'application/vnd.ms-powerpoint', 'application/vnd.ms-powerpoint', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.openxmlformats-officedocument.presentationml.template', 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 'application/vnd.ms-powerpoint.addin.macroEnabled.12', 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' ]; } /* PSD */ // tslint:disable-next-line:variable-name static { this.mime_psd = [ 'image/photoshop', 'image/x-photoshop', 'image/psd', 'application/photoshop', 'application/psd', 'zz-application/zz-winassoc-psd' ]; } /* Compressed files */ // tslint:disable-next-line:variable-name static { this.mime_compress = [ 'application/x-gtar', 'application/x-gcompress', 'application/compress', 'application/x-tar', 'application/x-rar-compressed', 'application/octet-stream', 'application/x-zip-compressed', 'application/zip-compressed', 'application/x-7z-compressed', 'application/gzip', 'application/x-bzip2' ]; } static getMimeClass(file) { let mimeClass = 'application'; if (file?.type && this.mime_psd.indexOf(file.type) !== -1) { mimeClass = 'image'; } else if (file?.type?.match('image.*')) { mimeClass = 'image'; } else if (file?.type?.match('video.*')) { mimeClass = 'video'; } else if (file?.type?.match('audio.*')) { mimeClass = 'audio'; } else if (file?.type === 'application/pdf') { mimeClass = 'pdf'; } else if (file?.type && this.mime_compress.indexOf(file.type) !== -1) { mimeClass = 'compress'; } else if (file?.type && this.mime_doc.indexOf(file.type) !== -1) { mimeClass = 'doc'; } else if (file?.type && this.mime_xsl.indexOf(file.type) !== -1) { mimeClass = 'xls'; } else if (file?.type && this.mime_ppt.indexOf(file.type) !== -1) { mimeClass = 'ppt'; } if (mimeClass === 'application' && file?.name) { mimeClass = this.fileTypeDetection(file.name); } return mimeClass; } static fileTypeDetection(inputFilename) { const types = { jpg: 'image', jpeg: 'image', tif: 'image', psd: 'image', bmp: 'image', png: 'image', nef: 'image', tiff: 'image', cr2: 'image', dwg: 'image', cdr: 'image', ai: 'image', indd: 'image', pin: 'image', cdp: 'image', skp: 'image', stp: 'image', '3dm': 'image', mp3: 'audio', wav: 'audio', wma: 'audio', mod: 'audio', m4a: 'audio', compress: 'compress', zip: 'compress', rar: 'compress', '7z': 'compress', lz: 'compress', z01: 'compress', bz2: 'compress', gz: 'compress', pdf: 'pdf', xls: 'xls', xlsx: 'xls', ods: 'xls', mp4: 'video', avi: 'video', wmv: 'video', mpg: 'video', mts: 'video', flv: 'video', '3gp': 'video', vob: 'video', m4v: 'video', mpeg: 'video', m2ts: 'video', mov: 'video', doc: 'doc', docx: 'doc', eps: 'doc', txt: 'doc', odt: 'doc', rtf: 'doc', ppt: 'ppt', pptx: 'ppt', pps: 'ppt', ppsx: 'ppt', odp: 'ppt' }; const chunks = inputFilename.split('.'); if (chunks.length < 2) { return 'application'; } const extension = chunks[chunks.length - 1].toLowerCase(); if (types[extension] === undefined) { return 'application'; } else { return types[extension]; } } } function isFile(value) { return (File && value instanceof File); } class FileUploader { constructor(options) { this.isUploading = false; this.queue = []; this.progress = 0; this._nextIndex = 0; this.options = { autoUpload: false, isHTML5: true, filters: [], removeAfterUpload: false, disableMultipart: false, formatDataFunction: (item) => item._file, formatDataFunctionIsAsync: false, url: '' }; this.setOptions(options); this.response = new EventEmitter(); } setOptions(options) { this.options = Object.assign(this.options, options); this.authToken = this.options.authToken; this.authTokenHeader = this.options.authTokenHeader || 'Authorization'; this.autoUpload = this.options.autoUpload; // drop the built-in filters added by an earlier call before adding them again const builtIn = [this._queueLimitFilter, this._fileSizeFilter, this._fileTypeFilter, this._mimeTypeFilter]; this.options.filters = this.options.filters?.filter((filter) => !builtIn.includes(filter.fn)); this.options.filters?.unshift({ name: 'queueLimit', fn: this._queueLimitFilter }); if (this.options.maxFileSize) { this.options.filters?.unshift({ name: 'fileSize', fn: this._fileSizeFilter }); } if (this.options.allowedFileType) { this.options.filters?.unshift({ name: 'fileType', fn: this._fileTypeFilter }); } if (this.options.allowedMimeType) { this.options.filters?.unshift({ name: 'mimeType', fn: this._mimeTypeFilter }); } for (let i = 0; i < this.queue.length; i++) { this.queue[i].url = this.options.url; } } addToQueue(files, _options, filters) { let options = _options; const list = Array.from(files); const arrayOfFilters = this._getFilters(filters); const count = this.queue.length; const addedFileItems = []; list.map((some) => { if (!options) { options = this.options; } const temp = new FileLikeObject(some); if (this._isValidFile(temp, arrayOfFilters, options)) { const fileItem = new FileItem(this, some, options); addedFileItems.push(fileItem); this.queue.push(fileItem); this._onAfterAddingFile(fileItem); } else { if (typeof this._failFilterIndex === 'number' && this._failFilterIndex >= 0) { const filter = arrayOfFilters[this._failFilterIndex]; this._onWhenAddingFileFailed(temp, filter, options); } } }); if (this.queue.length !== count) { this._onAfterAddingAll(addedFileItems); this.progress = this._getTotalProgress(); } this._render(); if (this.options.autoUpload) { this.uploadAll(); } } removeFromQueue(value) { const index = this.getIndexOfItem(value); const item = this.queue[index]; if (item.isUploading) { item.cancel(); } this.queue.splice(index, 1); this.progress = this._getTotalProgress(); } clearQueue() { while (this.queue.length) { this.queue[0].remove(); } this.progress = 0; } uploadItem(value) { const index = this.getIndexOfItem(value); const item = this.queue[index]; const transport = this.options.isHTML5 ? '_xhrTransport' : '_iframeTransport'; item._prepareToUploading(); if (this.isUploading) { return; } this.isUploading = true; this[transport](item); } cancelItem(value) { const index = this.getIndexOfItem(value); const item = this.queue[index]; if (!item) { return; } if (item.isUploading) { const prop = this.options.isHTML5 ? item._xhr : item._form; prop.abort(); return; } // a waiting item is taken out of the queue without sending a request if (item.isReady) { this._onCancelItem(item, '', 0, {}); item._onComplete('', 0, {}); this.onCompleteItem(item, '', 0, {}); this._render(); } } uploadAll() { const items = this.getNotUploadedItems().filter((item) => !item.isUploading); if (!items.length) { return; } items.map((item) => item._prepareToUploading()); items[0].upload(); } cancelAll() { // waiting items first, so cancelling the uploading item does not start the next one const items = this.getNotUploadedItems(); items.filter((item) => !item.isUploading).forEach((item) => item.cancel()); items.filter((item) => item.isUploading).forEach((item) => item.cancel()); } isFile(value) { return isFile(value); } isFileLikeObject(value) { return value instanceof FileLikeObject; } getIndexOfItem(value) { return typeof value === 'number' ? value : this.queue.indexOf(value); } getNotUploadedItems() { return this.queue.filter((item) => !item.isUploaded); } getReadyItems() { return this.queue .filter((item) => (item.isReady && !item.isUploading)) .sort((item1, item2) => item1.index - item2.index); } onAfterAddingAll(fileItems) { return { fileItems }; } onBuildItemForm(fileItem, form) { return { fileItem, form }; } onAfterAddingFile(fileItem) { return { fileItem }; } onWhenAddingFileFailed(item, filter, options) { return { item, filter, options }; } onBeforeUploadItem(fileItem) { return { fileItem }; } onProgressItem(fileItem, progress) { return { fileItem, progress }; } onProgressAll(progress) { return { progress }; } onSuccessItem(item, response, status, headers) { return { item, response, status, headers }; } onErrorItem(item, response, status, headers) { return { item, response, status, headers }; } onCancelItem(item, response, status, headers) { return { item, response, status, headers }; } onCompleteItem(item, response, status, headers) { return { item, response, status, headers }; } onCompleteAll() { return void 0; } _mimeTypeFilter(item) { if (!this.options.allowedMimeType) { return true; } // browsers report an empty type for extensions the OS does not know if (!item?.type) { return this.options.allowEmptyMimeType !== false; } return this.options.allowedMimeType.indexOf(item.type) !== -1; } _fileSizeFilter(item) { return !(this.options.maxFileSize && item.size > this.options.maxFileSize); } _fileTypeFilter(item) { return !(this.options.allowedFileType && this.options.allowedFileType.indexOf(FileType.getMimeClass(item)) === -1); } _onErrorItem(item, response, status, headers) { item._onError(response, status, headers); this.onErrorItem(item, response, status, headers); } _onCompleteItem(item, response, status, headers) { // the queue moves on even when an app callback throws runThen(() => { item._onComplete(response, status, headers); this.onCompleteItem(item, response, status, headers); }, () => this._uploadNext()); } _finishItem(item, method, response, status, headers) { runThen(() => this[method](item, response, status, headers), () => this._onCompleteItem(item, response, status, headers)); } _uploadNext() { const nextItem = this.getReadyItems()[0]; this.isUploading = false; if (nextItem) { nextItem.upload(); return; } this.progress = this._getTotalProgress(); this.onCompleteAll(); this._render(); } _headersGetter(parsedHeaders) { return (name) => { if (name) { return parsedHeaders[name.toLowerCase()] || undefined; } return parsedHeaders; }; } _xhrTransport(item) { const xhr = item._xhr = new XMLHttpRequest(); let sendable; this._onBeforeUploadItem(item); if (typeof item._file.size !== 'number') { throw new TypeError('The file specified is no longer valid'); } if (!this.options.disableMultipart) { sendable = this._buildFormData(item, item._file); } else { if (this.options.formatDataFunction) { sendable = this.options.formatDataFunction(item); } } xhr.upload.onprogress = (event) => { const progress = Math.round(event.lengthComputable ? event.loaded * 100 / event.total : 0); this._onProgressItem(item, progress); }; xhr.onload = () => { const headers = this._parseHeaders(xhr.getAllResponseHeaders()); const response = this._transformResponse(xhr.response); this._finishItem(item, this._isSuccessCode(xhr.status) ? '_onSuccessItem' : '_onErrorItem', response, xhr.status, headers); }; xhr.onerror = () => { const headers = this._parseHeaders(xhr.getAllResponseHeaders()); const response = this._transformResponse(xhr.response); this._finishItem(item, '_onErrorItem', response, xhr.status, headers); }; xhr.onabort = () => { const headers = this._parseHeaders(xhr.getAllResponseHeaders()); const response = this._transformResponse(xhr.response); this._finishItem(item, '_onCancelItem', response, xhr.status, headers); }; this._openRequest(xhr, item); if (this.options.formatDataFunctionIsAsync) { sendable.then((result) => xhr.send(JSON.stringify(result))); } else { xhr.send(sendable); } this._render(); } _buildFormData(item, file, extraParameters = {}) { const sendable = new FormData(); this._onBuildItemForm(item, sendable); Object.keys(extraParameters).forEach((key) => sendable.append(key, extraParameters[key])); const appendFile = () => sendable.append(item.alias, file, item.file.name); if (!this.options.parametersBeforeFiles) { appendFile(); } // For AWS, Additional Parameters must come BEFORE Files if (this.options.additionalParameter !== undefined) { Object.keys(this.options.additionalParameter).forEach((key) => { let paramVal = this.options.additionalParameter?.[key]; // Allow an additional parameter to include the filename if (typeof paramVal === 'string' && paramVal.indexOf('{{file_name}}') >= 0 && item.file?.name) { paramVal = paramVal.replace('{{file_name}}', item.file.name); } sendable.append(key, paramVal); }); } if (appendFile && this.options.parametersBeforeFiles) { appendFile(); } return sendable; } _openRequest(xhr, item) { // tslint:disable-next-line:no-this-assignment // eslint-disable-next-line @typescript-eslint/no-this-alias const that = this; if (item.method && item.url) { xhr.open(item.method, item.url, true); } xhr.withCredentials = item.withCredentials; if (this.options.headers) { for (const header of this.options.headers) { xhr.setRequestHeader(header.name, header.value); } } if (item.headers.length) { for (const header of item.headers) { xhr.setRequestHeader(header.name, header.value); } } if (this.authToken && this.authTokenHeader) { xhr.setRequestHeader(this.authTokenHeader, this.authToken); } xhr.onreadystatechange = function () { if (xhr.readyState == XMLHttpRequest.DONE) { that.response.emit(xhr.responseText); } }; } _getTotalProgress(value = 0) { if (this.options.removeAfterUpload) { return value; } const notUploaded = this.getNotUploadedItems().length; const uploaded = notUploaded ? this.queue.length - notUploaded : this.queue.length; const ratio = 100 / this.queue.length; const current = value * ratio / 100; return Math.round(uploaded * ratio + current); } _getFilters(filters) { if (!filters) { return this.options?.filters || []; } if (Array.isArray(filters)) { return filters; } if (typeof filters === 'string') { const names = filters.match(/[^\s,]+/g); return this.options?.filters || [] .filter((filter) => names?.indexOf(filter.name) !== -1); } return this.options?.filters || []; } _render() { return void 0; } _queueLimitFilter() { return this.options.queueLimit === undefined || this.queue.length < this.options.queueLimit; } _isValidFile(file, filters, options) { this._failFilterIndex = -1; return !filters.length ? true : filters.every((filter) => { if (typeof this._failFilterIndex === 'number') { this._failFilterIndex++; } return filter.fn.call(this, file, options); }); } _isSuccessCode(status) { return (status >= 200 && status < 300) || status === 304; } _transformResponse(response) { return response; } _parseHeaders(headers) { const parsed = {}; let key; let val; let i; if (!headers) { return parsed; } headers.split('\n').map((line) => { i = line.indexOf(':'); key = line.slice(0, i).trim().toLowerCase(); val = line.slice(i + 1).trim(); if (key) { parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; } }); return parsed; } _onWhenAddingFileFailed(item, filter, options) { this.onWhenAddingFileFailed(item, filter, options); } _onAfterAddingFile(item) { this.onAfterAddingFile(item); } _onAfterAddingAll(items) { this.onAfterAddingAll(items); } _onBeforeUploadItem(item) { item._onBeforeUpload(); this.onBeforeUploadItem(item); } _onBuildItemForm(item, form) { item._onBuildForm(form); this.onBuildItemForm(item, form); } _onProgressItem(item, progress) { const total = this._getTotalProgress(progress); this.progress = total; item._onProgress(progress); this.onProgressItem(item, progress); this.onProgressAll(total); this._render(); } _onSuccessItem(item, response, status, headers) { item._onSuccess(response, status, headers); this.onSuccessItem(item, response, status, headers); } _onCancelItem(item, response, status, headers) { item._onCancel(response, status, headers); this.onCancelItem(item, response, status, headers); } } class ChunkedFileUploader extends FileUploader { constructor(options) { super(options); this._chunks = new WeakMap(); } setOptions(options) { super.setOptions(options); // chunk hooks may retarget item.method along with item.url, so reset both for (const item of this.queue) { item.method = this.options.method || 'POST'; } } getChunk(item) { return this._chunks.get(item)?.chunk; } resumeItem(item) { if (item.isUploading || this.getIndexOfItem(item) < 0) { return; } this._setResumePoint(item); item.upload(); } resumeAll() { const items = this.queue.filter(item => !item.isSuccess && !item.isUploading); if (!items.length) { return; } items.forEach(item => { this._setResumePoint(item); item._prepareToUploading(); }); items[0].upload(); } _onErrorItem(item, response, status, headers) { item._onError(response, status, headers); this._keepProgress(item); this.onErrorItem(item, response, status, headers); } cancelItem(value) { const item = this.queue[this.getIndexOfItem(value)]; const state = item && this._chunks.get(item); if (state && item.isUploading) { state.cancelled = true; item._xhr?.abort(); return; } super.cancelItem(value); } onBeforeUploadChunk(item, chunk) { return { item, chunk }; } onSuccessChunk(item, chunk, response, status, headers) { return { item, chunk, response, status, headers }; } onErrorChunk(item, chunk, response, status, headers) { return { item, chunk, response, status, headers }; } _xhrTransport(item) { const chunkSize = Math.floor(this.options.chunkSize || 0); if (chunkSize <= 0) { this._chunks.delete(item); return super._xhrTransport(item); } const resume = this._chunks.get(item)?.resume; this._chunks.set(item, { chunk: resume }); this._onBeforeUploadItem(item); if (typeof item._file.size !== 'number') { throw new TypeError('The file specified is no longer valid'); } const total = Math.max(1, Math.ceil(item._file.size / chunkSize)); this._sendChunk(item, resume || this._sliceChunk(item, 0, 0, chunkSize, total)); } _onCancelItem(item, response, status, headers) { item._onCancel(response, status, headers); this._keepProgress(item); // an item cancelled while waiting to resume starts over on upload(), like any cancelled item const state = this._chunks.get(item); if (state) { state.resume = undefined; } this.onCancelItem(item, response, status, headers); } // a paused or failed chunked item counts with the progress it keeps, not as 0 or as done _getTotalProgress(value = 0) { if (this.options.removeAfterUpload || !this.queue.length) { return super._getTotalProgress(value); } const total = this.queue.reduce((sum, item) => { if (item.isUploading) { return sum + value; } if (this._chunks.has(item) && !item.isSuccess) { return sum + item.progress; } return sum + (item.isUploaded ? 100 : 0); }, 0); return Math.round(total / this.queue.length); } _setResumePoint(item) { const state = this._chunks.get(item); if (state?.chunk) { state.resume = state.chunk; } } // a failed or cancelled item resumes from its current chunk, so its progress stays at that chunk's start _keepProgress(item) { const chunk = this._chunks.get(item)?.chunk; if (chunk && item._file.size) { item.progress = Math.round(chunk.start * 100 / item._file.size); } } _sliceChunk(item, index, start, length, total) { const end = Math.min(start + length, item._file.size); return { index, total, start, end, blob: item._file.slice(start, end, item._file.type) }; } _sendChunk(item, chunk) { const state = this._chunks.get(item); const xhr = item._xhr = new XMLHttpRequest(); state.chunk = chunk; this.onBeforeUploadChunk(item, chunk); const sendable = this.options.disableMultipart ? chunk.blob : this._buildFormData(item, chunk.blob, { [this.options.chunkIndexParam || 'chunkIndex']: chunk.index.toString(), [this.options.totalChunksParam || 'totalChunks']: chunk.total.toString() }); if (state.cancelled) { this._finishItem(item, '_onCancelItem', '', 0, {}); return; } xhr.upload.onprogress = (event) => { const sent = event.lengthComputable ? event.loaded / event.total * (chunk.end - chunk.start) : 0; this._onProgressItem(item, item._file.size ? Math.round((chunk.start + sent) * 100 / item._file.size) : 0); }; xhr.onload = () => this._onChunkDone(item, chunk, xhr, this._isSuccessCode(xhr.status)); xhr.onerror = () => this._onChunkDone(item, chunk, xhr, false); xhr.onabort = () => { state.cancelled = true; this._onChunkDone(item, chunk, xhr, false); }; this._openRequest(xhr, item); const hasContentRange = [...(this.options.headers || []), ...item.headers].some(header => header.name.toLowerCase() === 'content-range'); if (this.options.disableMultipart && !hasContentRange) { const range = chunk.end > chunk.start ? `${chunk.start}-${chunk.end - 1}` : '*'; xhr.setRequestHeader('Content-Range', `bytes ${range}/${item._file.size}`); } xhr.send(sendable); this._render(); } _onChunkDone(item, chunk, xhr, isSuccess) { const state = this._chunks.get(item); const headers = this._parseHeaders(xhr.getAllResponseHeaders()); const response = this._transformResponse(xhr.response); // cancel() from a hook aborts the finished request, which resets xhr.status to 0 const status = xhr.status; if (state.cancelled) { this._finishItem(item, '_onCancelItem', response, status, headers); return; } if (!isSuccess) { runThen(() => this.onErrorChunk(item, chunk, response, status, headers), () => this._finishItem(item, '_onErrorItem', response, status, headers)); return; } this._runChunkHook(item, () => this.onSuccessChunk(item, chunk, response, status, headers)); if (chunk.index + 1 >= chunk.total) { state.chunk = undefined; this._finishItem(item, '_onSuccessItem', response, status, headers); return; } const next = this._sliceChunk(item, chunk.index + 1, chunk.end, chunk.end - chunk.start, chunk.total); if (state.cancelled) { state.chunk = next; this._finishItem(item, '_onCancelItem', response, status, headers); return; } this._runChunkHook(item, () => this._sendChunk(item, next)); } // hooks after the first chunk run in XHR callbacks: fail the item so the queue moves on, then rethrow _runChunkHook(item, hook) { try { hook(); } catch (e) { if (item.isUploading) { try { this._finishItem(item, '_onErrorItem', '', 0, {}); } catch (later) { reportLater(later); } } throw e; } } } class FileSelectDirective { constructor() { this.uploader = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "uploader" }] : /* istanbul ignore next */ [])); // eslint-disable-next-line @angular-eslint/no-output-on-prefix this.onFileSelected = output(); this.element = inject(ElementRef); } getOptions() { return this.uploader()?.options; } getFilters() { return ''; } isEmptyAfterSelection() { return !!this.element.nativeElement.attributes.multiple; } // a selection no longer in the queue would stop the same file from firing change again, so clear it before the picker opens onClick() { const file = this.element.nativeElement.files?.[0]; if (file && !this.uploader()?.queue.some(item => item._file === file)) { this.element.nativeElement.value = ''; } } onChange() { const files = this.element.nativeElement.files; const options = this.getOptions(); const filters = this.getFilters(); this.uploader()?.addToQueue(files, options, filters); this.onFileSelected.emit(files); if (this.isEmptyAfterSelection()) { this.element.nativeElement.value = ''; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.8", ngImport: i0, type: FileSelectDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.8", type: FileSelectDirective, isStandalone: true, selector: "[ng2FileSelect]", inputs: { uploader: { classPropertyName: "uploader", publicName: "uploader", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onFileSelected: "onFileSelected" }, host: { listeners: { "click": "onClick()", "change": "onChange()" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.8", ngImport: i0, type: FileSelectDirective, decorators: [{ type: Directive, args: [{ selector: '[ng2FileSelect]' }] }], propDecorators: { uploader: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploader", required: false }] }], onFileSelected: [{ type: i0.Output, args: ["onFileSelected"] }], onClick: [{ type: HostListener, args: ['click'] }], onChange: [{ type: HostListener, args: ['change'] }] } }); class FileDropDirective { constructor() { this.uploader = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "uploader" }] : /* istanbul ignore next */ [])); this.fileOver = output(); // eslint-disable-next-line @angular-eslint/no-output-on-prefix this.onFileDrop = output(); this.element = inject(ElementRef); } getOptions() { return this.uploader()?.options; } getFilters() { return ''; } onDrop(event) { const transfer = this._getTransfer(event); if (!transfer) { return; } const options = this.getOptions(); const filters = this.getFilters(); this._preventAndStop(event); if (options) { this.uploader()?.addToQueue(transfer.files, options, filters); } this.fileOver.emit(false); this.onFileDrop.emit(transfer.files); } onDragOver(event) { const transfer = this._getTransfer(event); if (!this._haveFiles(transfer.types)) { return; } transfer.dropEffect = 'copy'; this._preventAndStop(event); this.fileOver.emit(true); } onDragLeave(event) { if (this.element) { if (event.currentTarget === this.element[0]) { return; } } this._preventAndStop(event); this.fileOver.emit(false); } _getTransfer(event) { return event.dataTransfer ? event.dataTransfer : event.originalEvent.dataTransfer; // jQuery fix; } _preventAndStop(event) { event.preventDefault(); event.stopPropagation(); } _haveFiles(types) { if (!types) { return false; } if (types.indexOf) { return types.indexOf('Files') !== -1; } if (types.contains) { return types.contains('Files'); } return false; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.8", ngImport: i0, type: FileDropDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.8", type: FileDropDirective, isStandalone: true, selector: "[ng2FileDrop]", inputs: { uploader: { classPropertyName: "uploader", publicName: "uploader", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fileOver: "fileOver", onFileDrop: "onFileDrop" }, host: { listeners: { "drop": "onDrop($event)", "dragover": "onDragOver($event)", "dragleave": "onDragLeave($event)" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.8", ngImport: i0, type: FileDropDirective, decorators: [{ type: Directive, args: [{ selector: '[ng2FileDrop]', }] }], propDecorators: { uploader: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploader", required: false }] }], fileOver: [{ type: i0.Output, args: ["fileOver"] }], onFileDrop: [{ type: i0.Output, args: ["onFileDrop"] }], onDrop: [{ type: HostListener, args: ['drop', ['$event']] }], onDragOver: [{ type: HostListener, args: ['dragover', ['$event']] }], onDragLeave: [{ type: HostListener, args: ['dragleave', ['$event']] }] } }); class FileUploadModule { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.8", ngImport: i0, type: FileUploadModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.1.8", ngImport: i0, type: FileUploadModule, imports: [FileSelectDirective, FileDropDirective], exports: [FileSelectDirective, FileDropDirective] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.1.8", ngImport: i0, type: FileUploadModule }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.8", ngImport: i0, type: FileUploadModule, decorators: [{ type: NgModule, args: [{ imports: [ FileSelectDirective, FileDropDirective, ], exports: [ FileSelectDirective, FileDropDirective, ], }] }] }); /** * Generated bundle index. Do not edit. */ export { ChunkedFileUploader, FileDropDirective, FileItem, FileLikeObject, FileSelectDirective, FileUploadModule, FileUploader }; //# sourceMappingURL=ng2-file-upload.mjs.map