/**
* @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);
}
});
});