<commit_before>const SIZE_UNITS = {
    kB: 1024,
    MB: Math.pow(1024, 2),
    GB: Math.pow(1024, 3),
    TB: Math.pow(1024, 4),
};

export default {
    name: 'u-uploader',
    props: {
        url: String,
        dataType: { type: String, default: 'json' },
        data: { type: Object, default: () => ({}) },
        name: { type: String, default: 'file' },
        extensions: { type: [String, Array], default: '' },
        maxSize: { type: [String, Number], default: Infinity },
        disabled: { type: Boolean, default: false },
    },
    data() {
        return {
            contentType: 'multipart/form-data',
            sending: false,
            file: {},
            tempId: new Date().getTime(),
        };
    },
    methods: {
        /**
         * @method upload() 弹出文件对话框并且上传文件
         * @public
         * @return {void}
         */
        upload() {
            if (this.disabled || this.sending)
                return;

            this.$refs.file.value = '';
            this.$refs.file.click();
        },
        /**
         * @method checkExtensions(file) 检查扩展名
         * @private
         * @param  {File} file 文件对象
         * @return {boolean} 返回是否通过验证
         */
        checkExtensions(file) {
            if (!this.extensions)
                return true;

            const fileName = file.name;
            const extName = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase();

            let extensions = this.extensions;
            if (typeof extensions === 'string')
                extensions = extensions.split(',');

            if (extensions.includes(extName))
                return true;

            /**
             * @event error 上传错误时触发
             * @property {object} name ExtensionError
             * @property {object} message 错误信息
             * @property {object} extensions 可上传的扩展名
             */
            this.$emit('error', {
                name: 'ExtensionError',
                message: '只能上传' + extensions.join(', ') + '类型的文件！',
                extensions,
            }, this);

            return false;
        },
        /**
         * @method checkSize(file) 检查文件大小
         * @private
         * @param  {File} file 文件对象
         * @return {boolean} 返回是否通过验证
         */
        checkSize(file) {
            if (this.maxSize === Infinity)
                return true;

            let maxSize;
            if (!isNaN(this.maxSize))
                maxSize = +this.maxSize;
            else {
                const unit = this.maxSize.slice(-2);
                if (!SIZE_UNITS[unit])
                    throw new Error('Unknown unit!');

                maxSize = this.maxSize.slice(0, -2) * SIZE_UNITS[unit];
            }

            if (file.size <= maxSize)
                return true;

            /**
             * @event error 上传错误时触发
             * @property {object} name SizeError
             * @property {object} message 错误信息
             * @property {object} maxSize 可上传的最大文件大小
             * @property {object} size 当前文件大小
             */
            this.$emit('error', {
                name: 'SizeError',
                message: '文件大小超出限制！',
                maxSize: this.maxSize,
                size: file.size,
            }, this);

            return false;
        },
        /**
         * @method submit() 提交表单
         * @private
         * @return {void}
         */
        submit() {
            const file = this.$refs.file.files ? this.$refs.file.files[0] : {
                name: this.$refs.file.value,
                size: 0,
            };

            if (!file || !file.name || !this.checkExtensions(file) || !this.checkSize(file))
                return;

            const fileName = file.name;
            this.file = {
                name: fileName,
                extName: fileName.includes('.') ? fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase() : undefined,
                size: file.size,
            };

            /**
             * @event sending 发送前触发
             * @property {object} data 待发送的数据
             */
            let cancel = false;
            this.$emit('before-send', {
                data: this.data,
                file,
                files: this.$refs.file.files,
                preventDefault: () => cancel = true,
            }, this);
            if (cancel)
                return;

            this.sending = true;
            if (typeof FormData === 'undefined') // IE9 不支持 XHR2 相关功能
                this.$refs.form.submit();
            else {
                const xhr = new XMLHttpRequest();
                const formData = new FormData(this.$refs.form);

                xhr.open('POST', this.url);

                xhr.upload.onprogress = function (e) {
                    if (event.lengthComputable) {
                        /**
                        * @event progress 发送中触发
                        * @property {object} data 待发送的数据
                        */
                        this.$emit('progress', {
                            loaded: e.loaded,
                            total: e.total,
                        }, this);
                    }
                }.bind(this);

                xhr.onreadystatechange = () => {
                    if (xhr.readyState === 4) {
                        if (xhr.status === 200)
                            this.onLoad(xhr.responseText, xhr.responseXML);
                        else {
                            if (!this.sending)
                                return;
                            this.sending = false;
                            this.file = null;
                            this.$emit('error', {
                                name: 'ResponseError',
                                message: 'No responseText!',
                            }, this);
                        }
                    }
                };

                xhr.send(formData);
            }

            this.$emit('send', {
                data: this.data,
                file,
                files: this.$refs.file.files,
            }, this);
        },
        /**
         * @method onLoad() 接收数据回调
         * @private
         * @return {void}
         */
        onLoad(responseText, responseXML) {
            const $iframe = this.$refs.iframe;
            const file = this.file;

            if (!this.sending)
                return;
            this.sending = false;
            this.file = null;

            const xml = {};

            if (!!responseText || !!responseXML) {
                // ajax上传时数据处理
                xml.responseText = responseText;
                xml.responseXML = responseXML;
            } else {<commit_msg>:sparkles: Emit xhr & formData in<commit_after>const SIZE_UNITS = {
    kB: 1024,
    MB: Math.pow(1024, 2),
    GB: Math.pow(1024, 3),
    TB: Math.pow(1024, 4),
};

export default {
    name: 'u-uploader',
    props: {
        url: String,
        dataType: { type: String, default: 'json' },
        data: { type: Object, default: () => ({}) },
        name: { type: String, default: 'file' },
        extensions: { type: [String, Array], default: '' },
        maxSize: { type: [String, Number], default: Infinity },
        disabled: { type: Boolean, default: false },
    },
    data() {
        return {
            contentType: 'multipart/form-data',
            sending: false,
            file: {},
            tempId: new Date().getTime(),
        };
    },
    methods: {
        /**
         * @method upload() 弹出文件对话框并且上传文件
         * @public
         * @return {void}
         */
        upload() {
            if (this.disabled || this.sending)
                return;

            this.$refs.file.value = '';
            this.$refs.file.click();
        },
        /**
         * @method checkExtensions(file) 检查扩展名
         * @private
         * @param  {File} file 文件对象
         * @return {boolean} 返回是否通过验证
         */
        checkExtensions(file) {
            if (!this.extensions)
                return true;

            const fileName = file.name;
            const extName = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase();

            let extensions = this.extensions;
            if (typeof extensions === 'string')
                extensions = extensions.split(',');

            if (extensions.includes(extName))
                return true;

            /**
             * @event error 上传错误时触发
             * @property {object} name ExtensionError
             * @property {object} message 错误信息
             * @property {object} extensions 可上传的扩展名
             */
            this.$emit('error', {
                name: 'ExtensionError',
                message: '只能上传' + extensions.join(', ') + '类型的文件！',
                extensions,
            }, this);

            return false;
        },
        /**
         * @method checkSize(file) 检查文件大小
         * @private
         * @param  {File} file 文件对象
         * @return {boolean} 返回是否通过验证
         */
        checkSize(file) {
            if (this.maxSize === Infinity)
                return true;

            let maxSize;
            if (!isNaN(this.maxSize))
                maxSize = +this.maxSize;
            else {
                const unit = this.maxSize.slice(-2);
                if (!SIZE_UNITS[unit])
                    throw new Error('Unknown unit!');

                maxSize = this.maxSize.slice(0, -2) * SIZE_UNITS[unit];
            }

            if (file.size <= maxSize)
                return true;

            /**
             * @event error 上传错误时触发
             * @property {object} name SizeError
             * @property {object} message 错误信息
             * @property {object} maxSize 可上传的最大文件大小
             * @property {object} size 当前文件大小
             */
            this.$emit('error', {
                name: 'SizeError',
                message: '文件大小超出限制！',
                maxSize: this.maxSize,
                size: file.size,
            }, this);

            return false;
        },
        /**
         * @method submit() 提交表单
         * @private
         * @return {void}
         */
        submit() {
            const file = this.$refs.file.files ? this.$refs.file.files[0] : {
                name: this.$refs.file.value,
                size: 0,
            };

            if (!file || !file.name || !this.checkExtensions(file) || !this.checkSize(file))
                return;

            const fileName = file.name;
            this.file = {
                name: fileName,
                extName: fileName.includes('.') ? fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase() : undefined,
                size: file.size,
            };

            if (typeof FormData === 'undefined') { // IE9 不支持 XHR2 相关功能
                /**
                 * @event sending 发送前触发
                 * @property {object} data 待发送的数据
                 */
                let cancel = false;
                this.$emit('before-send', {
                    data: this.data,
                    file,
                    files: this.$refs.file.files,
                    preventDefault: () => cancel = true,
                }, this);
                if (cancel)
                    return;

                this.sending = true;
                this.$refs.form.submit();
            } else {
                const xhr = new XMLHttpRequest();
                const formData = new FormData(this.$refs.form);

                xhr.open('POST', this.url);
                xhr.upload.onprogress = function (e) {
                    if (event.lengthComputable) {
                        /**
                        * @event progress 发送中触发
                        * @property {object} data 待发送的数据
                        */
                        this.$emit('progress', {
                            loaded: e.loaded,
                            total: e.total,
                        }, this);
                    }
                }.bind(this);

                xhr.onreadystatechange = () => {
                    if (xhr.readyState === 4) {
                        if (xhr.status === 200)
                            this.onLoad(xhr.responseText, xhr.responseXML);
                        else {
                            if (!this.sending)
                                return;
                            this.sending = false;
                            this.file = null;
                            this.$emit('error', {
                                name: 'ResponseError',
                                message: 'No responseText!',
                            }, this);
                        }
                    }
                };

                /**
                 * @event sending 发送前触发
                 * @property {object} data 待发送的数据
                 */
                let cancel = false;
                this.$emit('before-send', {
                    data: this.data,
                    file,
                    files: this.$refs.file.files,
                    xhr,
                    formData,
                    preventDefault: () => cancel = true,
                }, this);
                if (cancel)
                    return;

                this.sending = true;
                xhr.send(formData);
            }

            this.$emit('send', {
                data: this.data,
                file,
                files: this.$refs.file.files,
            }, this);
        },
        /**
         * @method onLoad() 接收数据回调
         * @private
         * @return {void}
         */
        onLoad(responseText, responseXML) {
            const $iframe = this.$refs.iframe;
            const file = this.file;

            if (!this.sending)
                return;
            this.sending = false;
            this.file = null;

            const xml = {};

            if (!!responseText || !!responseXML) {
                // ajax上传时数据处理
                xml.responseText = responseText;
                xml.responseXML = responseXML;
            } else {
{'fraction_non_alphanumeric': 0.1178925035360679, 'fraction_numerical': 0.004526166902404526, 'mean_word_length': 1.0393712143063167, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 6}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4010866', 'n_tokens_mistral': 3932, 'n_tokens_neox': 3772, 'n_words': 777}