Home Reference Source

src/modules/loader.js

import {Feature} from '../feature';
import {createElm, createText, elm, removeElm} from '../dom';
import {EMPTY_FN} from '../types';
import {root} from '../root';
import {NONE} from '../const';
import {defaultsStr, defaultsFn} from '../settings';

const EVENTS = [
    'before-filtering',
    'before-populating-filter',
    'before-page-change',
    'before-clearing-filters',
    'before-page-length-change',
    'before-reset-page',
    'before-reset-page-length',
    'before-loading-extensions',
    'before-loading-themes'
];

/**
 * Activity indicator
 *
 * @export
 * @class Loader
 * @extends {Feature}
 */
export class Loader extends Feature {

    /**
     * Creates an instance of Loader.
     *
     * @param {TableFilter} tf TableFilter instance
     */
    constructor(tf) {
        super(tf, 'loader');

        let f = this.config.loader || {};

        /**
         * ID of custom container element
         * @type {String}
         */
        this.targetId = defaultsStr(f.target_id, null);

        /**
         * Loader container DOM element
         * @type {DOMElement}
         */
        this.cont = null;

        /**
         * Text displayed when indicator is visible
         * @type {String}
         */
        this.text = defaultsStr(f.text, 'Loading...');

        /**
         * Custom HTML injected in Loader's container element
         * @type {String}
         */
        this.html = defaultsStr(f.html, null);

        /**
         * Css class for Loader's container element
         * @type {String}
         */
        this.cssClass = defaultsStr(f.css_class, 'loader');

        /**
         * Close delay in milliseconds
         * @type {Number}
         */
        this.closeDelay = 250;

        /**
         * Callback fired when loader is displayed
         * @type {Function}
         */
        this.onShow = defaultsFn(f.on_show_loader, EMPTY_FN);

        /**
         * Callback fired when loader is closed
         * @type {Function}
         */
        this.onHide = defaultsFn(f.on_hide_loader, EMPTY_FN);
    }

    /**
     * Initializes Loader instance
     */
    init() {
        if (this.initialized) {
            return;
        }

        let tf = this.tf;
        let emitter = this.emitter;

        let containerDiv = createElm('div');
        containerDiv.className = this.cssClass;

        let targetEl = !this.targetId ?
            tf.dom().parentNode : elm(this.targetId);
        if (!this.targetId) {
            targetEl.insertBefore(containerDiv, tf.dom());
        } else {
            targetEl.appendChild(containerDiv);
        }
        this.cont = containerDiv;
        if (!this.html) {
            this.cont.appendChild(createText(this.text));
        } else {
            this.cont.innerHTML = this.html;
        }

        this.show(NONE);

        // Subscribe to events
        emitter.on(EVENTS, () => this.show(''));
        emitter.on(EVENTS, () => this.show(NONE));

        /** @inherited */
        this.initialized = true;
    }

    /**
     * Shows or hides activity indicator
     * @param {String} Two possible values: '' or 'none'
     */
    show(p) {
        if (!this.isEnabled()) {
            return;
        }

        let displayLoader = () => {
            if (!this.cont) {
                return;
            }
            if (p !== NONE) {
                this.onShow(this);
            }
            this.cont.style.display = p;
            if (p === NONE) {
                this.onHide(this);
            }
        };

        let t = p === NONE ? this.closeDelay : 1;
        root.setTimeout(displayLoader, t);
    }

    /**
     * Removes feature
     */
    destroy() {
        if (!this.initialized) {
            return;
        }

        let emitter = this.emitter;

        removeElm(this.cont);
        this.cont = null;

        // Unsubscribe to events
        emitter.off(EVENTS, () => this.show(''));
        emitter.off(EVENTS, () => this.show(NONE));

        this.initialized = false;
    }
}