import * as tslib_1 from "tslib";
import { Component, Input, Output, EventEmitter, ChangeDetectorRef, forwardRef, ViewEncapsulation, ChangeDetectionStrategy, ViewChild, ElementRef } from '@angular/core';
import { OnChange } from '../core/decorators';
import { DomSanitizer } from '@angular/platform-browser';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
/*
 * Provider Expression that allows component to register as a ControlValueAccessor.
 * This allows it to support [(ngModel)].
 * @docs-private
 */
var UPLOADER_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(function () { return UploaderComponent; }),
    multi: true
};
var UploaderComponent = /** @class */ (function () {
    function UploaderComponent(sanitizer, cdRef) {
        this.sanitizer = sanitizer;
        this.cdRef = cdRef;
        /**
         * 上传模式包括图片上传和文件上传
         * @default 'image'
         */
        this.mode = 'image';
        /**
         * 可上传的文件类型
         * @default 'image/*'
         */
        this.accept = 'image/*';
        /**
         * 上传进度包括百分比和进度条
         * @default 'text'
         */
        this.progressMode = 'text';
        /**
         * 已上传文件列表
         */
        this._files = [];
        /**
         * 布局方式，'horizontal' | 'vertical'
         * @default 'horizontal'
         */
        this.direction = 'horizontal';
        /**
         * 文件数量不符合要求的提示信息
         * @default '文件的数量超过限制'
         */
        this.invalidFileCountMessage = '文件的数量超过限制';
        /**
         * 文件大小不符合要求的提示信息
         * @default '文件的大小不符合要求'
         */
        this.invalidFileSizeMessage = '{0}: 文件的大小不符合要求';
        /**
         * 文件类型不符合要求的提示信息
         * @default '文件的类型不符合要求'
         */
        this.invalidFileTypeMessage = '{0}: 文件的类型不符合要求';
        /**
         * 上传失败的提示信息
         * @default '上传失败'
         */
        this.uploadFailedMessage = '上传失败';
        /**
         * 上传是否是禁用状态
         * @default false
         */
        this.disabled = false;
        this.onBeforeUpload = new EventEmitter();
        this.onBeforeSend = new EventEmitter();
        this.onSuccess = new EventEmitter();
        this.onError = new EventEmitter();
        this.onClear = new EventEmitter();
        this.onRemove = new EventEmitter();
        this.onSelect = new EventEmitter();
        this.onProgress = new EventEmitter();
        /**
         * 重新上传中被替换的文件
         */
        this._replacingFile = null;
        this._method = 'POST';
    }
    Object.defineProperty(UploaderComponent.prototype, "files", {
        get: function () {
            return this._files;
        },
        set: function (data) {
            this._files = data;
            this._files.forEach(function (file) {
                file.state = 'success';
            });
        },
        enumerable: true,
        configurable: true
    });
    UploaderComponent.prototype.ngOnInit = function () {
        this.validateFileCount([]);
    };
    /**
     * 选择待上传的文件
     *
     * @param {object} event - 文件选择事件
     * @docs-private
     */
    UploaderComponent.prototype.onFileSelect = function (event) {
        this.msgs = [];
        var files = event.dataTransfer ? event.dataTransfer.files : event.target.files;
        this.validateFiles(files);
    };
    /**
     * 文件验证通过后上传
     *
     * @param {object} files - 待上传文件
     */
    UploaderComponent.prototype.validateFiles = function (files) {
        var _this = this;
        if (this.validateFileCount(files)) {
            for (var i = 0; i < files.length; i++) {
                var file = files[i];
                if (this.validate(file)) {
                    // 预览图片
                    if (this.isImage(file)) {
                        file.url = this.sanitizer
                            .bypassSecurityTrustUrl((window.URL.createObjectURL(file)));
                    }
                    file.state = 'toBeUpload';
                    if (this._replacingFile) {
                        this.files.splice(this.files.indexOf(this._replacingFile), 1, file);
                        this._replacingFile = null;
                    }
                    else {
                        this.files.push(file);
                    }
                }
                else {
                    this.validateFileCount([]);
                }
            }
        }
        this._updateFormModel();
        this.files.forEach(function (file) {
            if (file.state === 'toBeUpload') {
                _this.upload(file);
            }
        });
    };
    /**
     * 清空上传Input所持有的文件，避免上传同一文件不触发change事件
     *
     * @param {object} files - 待上传文件
     */
    UploaderComponent.prototype.clearInputElement = function () {
        if (this.uploadInput && this.uploadInput.nativeElement) {
            this.uploadInput.nativeElement.value = '';
        }
    };
    /**
     * 上传文件
     *
     * @param {object} files - 待上传文件
     */
    UploaderComponent.prototype.upload = function (file) {
        var _this = this;
        file.state = 'uploading';
        this.clearInputElement();
        var xhr = new XMLHttpRequest();
        file.xhr = xhr;
        this.onBeforeUpload.emit({
            'xhr': xhr,
            'file': file
        });
        var formData = new FormData();
        formData.append('file', file, file.name);
        xhr.upload.addEventListener('progress', function (e) {
            if (e.lengthComputable) {
                file.progress = Math.round((e.loaded * 100) / e.total);
                _this.cdRef.markForCheck();
            }
            _this.onProgress.emit({ originalEvent: e, progress: file.progress });
        }, false);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                file.progress = 0;
                if (xhr.status >= 200 && xhr.status < 300) {
                    _this.onSuccess.emit({ xhr: xhr, file: file });
                    file.state = 'success';
                    file.xhr = null;
                    // 调用回调函数
                }
                else {
                    _this.onError.emit({ xhr: xhr, file: file });
                    file.state = 'error';
                    _this.cdRef.markForCheck();
                    file.xhr = null;
                }
            }
        };
        xhr.open(this._method, this.url, true);
        this.onBeforeSend.emit({
            'xhr': xhr,
            'file': file
        });
        xhr.withCredentials = this.withCredentials;
        xhr.send(formData);
    };
    /**
     * 验证文件的类型、大小
     *
     * @param {object} files - 待上传文件
     */
    UploaderComponent.prototype.validate = function (file) {
        if (this.accept && !this.isFileTypeValid(file)) {
            this.msgs.push({
                severity: 'error',
                message: this.invalidFileTypeMessage.replace('{0}', file.name),
            });
            return false;
        }
        if (this.maxFileSize && file.size > this.maxFileSize) {
            this.msgs.push({
                severity: 'error',
                message: this.invalidFileSizeMessage.replace('{0}', file.name),
            });
            return false;
        }
        return true;
    };
    /**
     * 验证文件的数量是否超过上限
     *
     * @param {object} files - 待上传文件
     * @return {boolean} 是否通过验证
     */
    UploaderComponent.prototype.validateFileCount = function (files) {
        // 上传的文件数量超过上限，显示错误提示
        if (this.files.length + files.length > this.maxFileCount) {
            this.msgs.push({
                severity: 'error',
                message: this.invalidFileCountMessage,
            });
            return false;
        }
        // 上传的文件数量达到上限，置灰上传按钮
        else if (this.files.length + files.length === +this.maxFileCount) {
            this.setDisabledState(true);
        }
        // 否则，点亮上传按钮
        else {
            this.setDisabledState(false);
        }
        return true;
    };
    /**
     * 验证文件的类型是否符合要求
     *
     * @param {object} file - 待上传文件
     * @return {boolean} 是否通过验证
     */
    UploaderComponent.prototype.isFileTypeValid = function (file) {
        var acceptableTypes = this.accept.split(',');
        for (var _i = 0, acceptableTypes_1 = acceptableTypes; _i < acceptableTypes_1.length; _i++) {
            var type = acceptableTypes_1[_i];
            type = type.trim();
            var acceptable = this.isWildcard(type) ?
                this.getTypeClass(file.type) === this.getTypeClass(type) :
                file.type === type || this.getFileExtension(file) === type;
            if (acceptable) {
                return true;
            }
        }
        return false;
    };
    /**
     * 文件类型是否包含通配符
     *
     * @param {string} fileType - 文件类型
     * @return {boolean} 是否通过验证
     */
    UploaderComponent.prototype.isWildcard = function (fileType) {
        return fileType.indexOf('*') !== -1;
    };
    /**
     * 获取文件的第一类型
     *
     * @param {string} fileType - 文件类型
     * @return {string} 文件的第一类型，如image、file
     */
    UploaderComponent.prototype.getTypeClass = function (fileType) {
        return fileType.substring(0, fileType.indexOf('/'));
    };
    /**
     * 获取文件名后缀
     *
     * @param {string} file - 待上传文件
     * @return {string} 文件名后缀
     */
    UploaderComponent.prototype.getFileExtension = function (file) {
        return '.' + file.name.split('.').pop();
    };
    /**
     * 将文件原始大小的字节数格式化成可读性更好的单位数
     *
     * @param {number} bytes - 字节数
     * @return {string} 带有单位的文件大小
     */
    UploaderComponent.prototype.formatSize = function (bytes) {
        if (bytes === 0) {
            return '0 B';
        }
        var k = 1000, dm = 3, sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
    };
    /**
     * 是否为图片
     *
     * @param {object} file - 待上传的文件
     * @return {boolean} 是否为图片
     */
    UploaderComponent.prototype.isImage = function (file) {
        return /^image\//.test(file.type);
    };
    /**
     * 取消上传中的文件
     *
     * @param {object} file - 上传中的文件
     * @docs-private
     */
    UploaderComponent.prototype.onCancelFile = function (file) {
        if (file.xhr) {
            file.xhr.abort();
        }
        this.onRemoveFile(file);
    };
    /**
     * 删除已上传的文件
     *
     * @param {object} file - 已上传的文件
     * @docs-private
     */
    UploaderComponent.prototype.onRemoveFile = function (file) {
        this.files.splice(this.files.indexOf(file), 1);
        this._updateFormModel();
        this.validateFileCount([]);
        this.onRemove.emit({ file: file });
    };
    /**
     * 上传失败后重新上传当前文件
     *
     * @param {object} file - 上传失败的文件
     * @docs-private
     */
    UploaderComponent.prototype.onReuploadFile = function (file) {
        this.upload(file);
    };
    /**
     * 上次成功后重新选择其他文件上传
     *
     * @param {object} file - 待上传的文件
     * @docs-private
     */
    UploaderComponent.prototype.onReplaceFile = function (file) {
        this._replacingFile = file;
    };
    /**
     * Sets the model value. Implemented as part of ControlValueAccessor.
     * @param value Value to be set to the model.
     */
    UploaderComponent.prototype.writeValue = function (value) {
        var _this = this;
        if (value) {
            this.files = value;
            // when init, set children toggle button state
            this.files.forEach(function (file) {
                _this.validateFiles(file);
            });
        }
    };
    /**
     * Registers a callback that will be triggered when the value has changed.
     * Implemented as part of ControlValueAccessor.
     * @param fn On change callback function.
     */
    UploaderComponent.prototype.registerOnChange = function (fn) {
        this._onModelChange = fn;
    };
    /**
     * Registers a callback that will be triggered when the control has been touched.
     * Implemented as part of ControlValueAccessor.
     * @param fn On touch callback function.
     */
    UploaderComponent.prototype.registerOnTouched = function (fn) {
        this._onTouch = fn;
    };
    /**
     * Toggles the disabled state of the component. Implemented as part of ControlValueAccessor.
     * @param isDisabled Whether the component should be disabled.
     */
    UploaderComponent.prototype.setDisabledState = function (isDisabled) {
        this.disabled = isDisabled;
        this.cdRef.markForCheck();
    };
    /**
     * 用于Form中的数据接口
     */
    UploaderComponent.prototype._updateFormModel = function () {
        if (this._onModelChange) {
            this._onModelChange(this.files);
        }
        if (this._onTouch) {
            this._onTouch(this.files);
        }
    };
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String)
    ], UploaderComponent.prototype, "url", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String)
    ], UploaderComponent.prototype, "mode", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String)
    ], UploaderComponent.prototype, "accept", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String)
    ], UploaderComponent.prototype, "progressMode", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Object),
        tslib_1.__metadata("design:paramtypes", [Object])
    ], UploaderComponent.prototype, "files", null);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String)
    ], UploaderComponent.prototype, "direction", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Number)
    ], UploaderComponent.prototype, "maxFileSize", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Number)
    ], UploaderComponent.prototype, "maxFileCount", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String)
    ], UploaderComponent.prototype, "invalidFileCountMessage", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String)
    ], UploaderComponent.prototype, "invalidFileSizeMessage", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String)
    ], UploaderComponent.prototype, "invalidFileTypeMessage", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String)
    ], UploaderComponent.prototype, "uploadFailedMessage", void 0);
    tslib_1.__decorate([
        OnChange(true),
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], UploaderComponent.prototype, "disabled", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], UploaderComponent.prototype, "withCredentials", void 0);
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], UploaderComponent.prototype, "onBeforeUpload", void 0);
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], UploaderComponent.prototype, "onBeforeSend", void 0);
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], UploaderComponent.prototype, "onSuccess", void 0);
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], UploaderComponent.prototype, "onError", void 0);
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], UploaderComponent.prototype, "onClear", void 0);
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], UploaderComponent.prototype, "onRemove", void 0);
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], UploaderComponent.prototype, "onSelect", void 0);
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], UploaderComponent.prototype, "onProgress", void 0);
    tslib_1.__decorate([
        ViewChild('uploadInput', { static: false }),
        tslib_1.__metadata("design:type", ElementRef)
    ], UploaderComponent.prototype, "uploadInput", void 0);
    UploaderComponent = tslib_1.__decorate([
        Component({
            selector: 'nb-uploader',
            template: "<ng-content></ng-content><ng-container *ngIf=\"mode==='image'\"><div class=\"nb-uploader-image\"><ul *ngIf=\"files.length\" class=\"image-list\"><li *ngFor=\"let file of files\" class=\"image-item\"><div class=\"img-progress-start\" [class.hidden]=\"file.state!=='uploading'\"><span *ngIf=\"progressMode==='text'\">{{ file.progress || '正在上传中' }}<label [class.hidden]=\"!file.progress\">%</label> </span><span *ngIf=\"progressMode==='bar'\"><nb-progress-bar [value]=\"file.progress\"></nb-progress-bar></span><button type=\"button\" nb-button theme=\"default\" (click)=\"onCancelFile(file)\">取消</button></div><div class=\"img-progress-error\" [class.hidden]=\"file.state!=='error'\"><span class=\"progress-error-msg\">{{ uploadFailedMessage }}</span> <button type=\"button\" nb-button theme=\"default\" (click)=\"onReuploadFile(file)\">重试</button> <i class=\"iconfont icon-close\" title=\"Close\" (click)=\"onRemoveFile(file)\"></i></div><div class=\"img-progress-finish\" [class.hidden]=\"file.state!=='success'\"><span class=\"photo_bigger_mark\"><img [src]=\"file.url\"></span><div class=\"photo-control\"><label for=\"fileInput\" class=\"edit_mark\" (click)=\"onReplaceFile(file)\">重新上传</label> <i class=\"iconfont icon-close\" title=\"Close\" (click)=\"onRemoveFile(file)\"></i></div></div></li></ul><div class=\"image-add\" *ngIf=\"!disabled\"><span class=\"image-add-icon\">+</span> <input id=\"fileInput\" (change)=\"onFileSelect($event)\" #uploadInput type=\"file\" multiple=\"multiple\" name=\"myFile\" class=\"fileinput\" accept=\"{{ accept }}\"></div><ul class=\"nb-uploader-msg\"><li *ngFor=\"let msg of msgs\"><span class=\"ui-messages-summary\">{{ msg.message }}</span></li></ul></div></ng-container><ng-container *ngIf=\"mode==='file'\"><div class=\"nb-uploader-file\"><div class=\"file-add\" [class.file-add-disabled]=\"disabled\"><button type=\"button\" nb-button theme=\"default\" [disabled]=\"disabled\"><i class=\"fa fa-upload\"></i> 文件上传</button> <input (change)=\"onFileSelect($event)\" #uploadInput type=\"file\" multiple=\"multiple\" name=\"myFile\" class=\"fileinput\" accept=\"{{ accept }}\"></div><ul class=\"nb-uploader-msg\"><li *ngFor=\"let msg of msgs\"><span class=\"ui-messages-summary\">{{ msg.message }}</span></li></ul><ul *ngIf=\"files.length\" class=\"file-list\"><li *ngFor=\"let file of files\" class=\"file-item\"><div class=\"file-progress-start\" [class.hidden]=\"file.state!=='uploading'\"><div *ngIf=\"progressMode==='text'\">{{ file.progress || '正在上传中' }}<label [class.hidden]=\"!file.progress\">%</label></div><div *ngIf=\"progressMode==='bar'\" class=\"file-progress-bar\"><nb-progress-bar [value]=\"file.progress\"></nb-progress-bar></div><i class=\"iconfont icon-close\" title=\"Close\" (click)=\"onCancelFile(file)\"></i></div><div class=\"file-progress-error\" [class.hidden]=\"file.state!=='error'\"><span class=\"iconfont icon-paperclip\"></span> <span class=\"file-name\" title=\"{{ file.name }}\">{{ file.name }}</span> <span class=\"progress-error-msg\">上传失败</span> <span class=\"progress-error-reupload\" (click)=\"onReuploadFile(file)\"><i class=\"iconfont icon-redo\"></i> 重试 </span><i class=\"iconfont icon-close\" title=\"Close\" (click)=\"onRemoveFile(file)\"></i></div><div class=\"file-progress-finish\" [class.hidden]=\"file.state!=='success'\"><span class=\"iconfont icon-paperclip\"></span> <span class=\"file-name\" title=\"{{ file.name }}\">{{ file.name }}</span> <i class=\"iconfont icon-close\" title=\"Close\" (click)=\"onRemoveFile(file)\"></i></div></li></ul></div></ng-container>",
            encapsulation: ViewEncapsulation.None,
            changeDetection: ChangeDetectionStrategy.OnPush,
            preserveWhitespaces: false,
            host: {
                'class': 'nb-widget nb-uploader',
                '[class.nb-uploader-horizontal]': 'direction==="horizontal"',
                '[class.nb-uploader-vertical]': 'direction==="vertical"'
            },
            exportAs: 'nbUploader'
        }),
        tslib_1.__metadata("design:paramtypes", [DomSanitizer,
            ChangeDetectorRef])
    ], UploaderComponent);
    return UploaderComponent;
}());
export { UploaderComponent };
//# sourceMappingURL=uploader.js.map