Source: widgets/loader.js

/**
 * @module application/widgets/loader
 * @description Loader widget
 * @author Lars Grevelink <lars.grevelink@24i.com>
 */
define('application/widgets/loader', [
    'antie/runtimecontext',
    'rofl/lib/utils',
    'rofl/widgets/button'
], function (
    RuntimeContext,
    Utils,
    Button
) {
    'use strict';

    var loaderContainerClass = 'loader-container';

    return Button.extend({

        /**
         * Initialises the loader widget.
         *
         * @param {string} [id] - Identifier of the widget.
         */
        init: function init (id) {
            init.base.call(this, id || 'loader');
            this._stopPropagationBound = Utils.bind(this._stopPropagation, this);

            this.addClass([loaderContainerClass]);
            this.addEventListener('keydown', this._stopPropagationBound);
            this.addEventListener('select', this._stopPropagationBound);
        },

        /**
         * Renders the loader and makes sure it's hidden by default.
         *
         * @param {Device} device - The currently active device.
         * @returns {HTMLElement} - The rendered element.
         */
        render: function render (device) {
            var outputElement = render.base.call(this, device);

            /*
             * Make sure we hide the loader by default to ensure
             * it's not focusable without actively telling the
             * application to do so.
             */
            this.hide({
                skipAnim: true
            });

            return outputElement;
        },

        /**
         * Stops the propagation for the given event.
         *
         * @param {Event} [evt] - Event triggered.
         */
        _stopPropagation: function (evt) {
            evt.stopPropagation();
        },

        /**
         * Shows the widget and sets the content.
         *
         * @param {Object} [options] - Options for the show transition.
         */
        show: function show (options) {
            var device = RuntimeContext.getDevice(),

                // Animation options
                duration = 2000,
                maxOffset = 502,
                changePerMs = 0.15,
                interval = 10,
                time = 0,
                element;

            // Make sure the output element is rendered
            if (!this.outputElement) {
                this.render(device);
            }

            // Clear the hide timeout
            if (this._hideTimeout) {
                window.clearTimeout(this._hideTimeout);
            }
            if (this._animation) {
                window.clearInterval(this._animation);
            }

            // Set the loading SVG
            this.outputElement.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" stroke-dasharray="5,5" class="svg-container"><circle cx="50" cy="50" r="40" fill="none" stroke-width="6" stroke-linecap="round" class="loader"></circle></svg>';

            // Start animating the loader
            element = device.getChildElementsByTagName(this.outputElement, 'svg')[0];
            this._animation = window.setInterval(function () {
                time += interval;
                element.style.strokeDashoffset = -(Math.round(maxOffset / duration * time)) + 'px';

                if (time <= duration / 2) {
                    element.style.strokeDasharray = Math.max(1, 150 - changePerMs * time) + 'px ,' + (100 + changePerMs * time) + 'px';
                } else {
                    element.style.strokeDasharray = 1 + changePerMs * (time - 1000) + 'px ,' + (250 - changePerMs * (time - 1000)) + 'px';
                }

                if (time >= duration) {
                    time = 0;
                }
            }, interval);

            // Show the widget
            show.base.call(this, options || {});
        },

        /**
         * Hides the widget and kills the content.
         *
         * @param {Object} [options] - Options for the hide transition.
         */
        hide: function hide (options) {
            options = options || {};

            // Hide the widget
            hide.base.call(this, options);

            // Clear the hide timeout
            if (this._hideTimeout) {
                window.clearTimeout(this._hideTimeout);
            }

            // Stop any running animation
            if (this._animation) {
                window.clearInterval(this._animation);
            }

            // Clear the hide timeout
            this._hideTimeout = window.setTimeout(Utils.bind(function () {
                this.outputElement.innerHTML = '';
            }, this), options.duration || 500);
        },

        /**
         * Adds the check on style attributes to validate whether the widget is focusable.
         *
         * @returns {boolean} - Whether the widget is focusable.
         */
        isFocusable: function isFocusable () {
            var styles;

            if (this.outputElement) {

                styles = this.outputElement.style;
                if (styles.visibility === 'hidden' || styles.opacity === 0 || styles.display === 'none') {
                    return false;
                }
            }

            return isFocusable.base.call(this);
        }
    });
});