js/src/plugins/file-upload.plugin.js

Summary

Maintainability
A
0 mins
Test Coverage
import $ from 'external/jquery';
import atk from 'atk';
import AtkPlugin from './atk.plugin';

export default class AtkFileUploadPlugin extends AtkPlugin {
    main() {
        this.textInput = this.$el.find('input[type="text"]');
        this.hiddenInput = this.$el.find('input[type="hidden"]');

        this.fileInput = this.$el.find('input[type="file"]');
        this.action = this.$el.find('#' + this.settings.action);
        this.actionContent = this.action.html();

        this.bar = this.$el.find('.progress');
        this.setEventHandler();
        this.setInitialState();
    }

    /**
     * Setup field initial state.
     */
    setInitialState() {
        // set progress bar
        this.bar.progress({
            text: {
                percent: '{percent}%',
                active: '{percent}%',
            },
        }).hide();

        this.$el.data().fileId = this.settings.file.id;
        this.hiddenInput.val(this.settings.file.id);
        this.textInput.val(this.settings.file.name);
        this.textInput.data('isTouch', false);
        if (this.settings.file.id) {
            this.setState('delete');
        }
    }

    /**
     * Update input value.
     */
    updateField(fileId, fileName) {
        this.$el.data().fileId = fileId;
        this.hiddenInput.val(fileId);

        if (fileName === '' || fileName === undefined || fileName === null) {
            this.textInput.val(fileId);
        } else {
            this.textInput.val(fileName);
        }
    }

    /**
     * Add event handler to input element.
     */
    setEventHandler() {
        this.textInput.on('click', (e) => {
            if (!e.target.value) {
                this.fileInput.click();
            }
        });

        // add event handler to action button
        this.action.on('click', (e) => {
            if (!this.textInput.val()) {
                this.fileInput.click();
            } else {
                // When upload is complete a JS action can be send to set an ID
                // to the uploaded file via the jQuery data property.
                // Check if that ID exist and send it with
                // delete callback, If not, default to file name.
                let id = this.$el.data().fileId;
                if (id === '' || id === undefined || id === null) {
                    id = this.textInput.val();
                }
                this.doFileDelete(id);
            }
        });

        // add event handler to file input
        this.fileInput.on('change', (e) => {
            if (e.target.files.length > 0) {
                this.textInput.val(e.target.files[0].name);
                this.doFileUpload(e.target.files);
            }
        });
    }

    /**
     * Set the action button HTML content.
     * Set the input text content.
     */
    setState(mode) {
        switch (mode) {
            case 'delete': {
                this.action.html(this.getEraseContent);
                setTimeout(() => {
                    this.bar.progress('reset');
                    this.bar.hide('fade');
                }, 1000);

                break;
            }
            case 'upload': {
                this.action.html(this.actionContent);
                this.textInput.val('');
                this.fileInput.val('');
                this.hiddenInput.val('');
                this.$el.data().fileId = null;

                break;
            }
        }
    }

    /**
     * Do the actual file uploading process.
     *
     * @param {FileList} files
     */
    doFileUpload(files) {
        // if submit button id is set, then disable submit during upload
        if (this.settings.submit) {
            $('#' + this.settings.submit).addClass('disabled');
        }

        // setup task on upload completion
        const completeCb = (response, content) => {
            if (response.success) {
                this.bar.progress('set label', this.settings.completeLabel);
                this.setState('delete');
            }

            if (this.settings.submit) {
                $('#' + this.settings.submit).removeClass('disabled');
            }
        };

        // setup progress bar update via xhr
        const xhrCb = () => {
            const xhr = new window.XMLHttpRequest();
            xhr.upload.addEventListener('progress', (event) => {
                if (event.lengthComputable) {
                    const percentComplete = event.loaded / event.total;
                    this.bar.progress('set percent', Number.parseInt(percentComplete * 100, 10));
                }
            }, false);

            return xhr;
        };

        this.bar.show();
        atk.uploadService.uploadFiles(
            files,
            this.$el,
            { fUploadAction: 'upload' },
            this.settings.url,
            completeCb,
            xhrCb
        );
    }

    /**
     * Callback server for file delete.
     */
    doFileDelete(fileId) {
        this.$el.api({
            on: 'now',
            url: this.settings.url,
            data: { fUploadAction: 'delete', fUploadId: fileId },
            method: 'POST',
            obj: this.$el,
            onComplete: (response, content) => {
                if (response.success) {
                    this.setState('upload');
                }
            },
        });
    }

    /**
     * Return the HTML content for erase action button.
     *
     * @returns {string}
     */
    getEraseContent() {
        return '<i class="red remove icon"></i>';
    }
}

AtkFileUploadPlugin.DEFAULTS = {
    url: null,
    file: { id: null, name: null },
    urlOptions: {},
    action: null,
    completeLabel: '100%',
    submit: null,
};