Source: widgets/button.js

/**
 * @fileOverview Requirejs module containing the antie.widgets.Button class.
 * @preserve Copyright (c) 2013-present British Broadcasting Corporation. All rights reserved.
 * @license See https://github.com/fmtvp/tal/blob/master/LICENSE for full licence
 */

define(
    'antie/widgets/button',
    [
        'antie/widgets/container',
        'antie/events/focusdelayevent',
        'antie/events/keyevent',
        'antie/events/selectevent'
    ],
    function(Container, FocusDelayEvent, KeyEvent, SelectEvent) {
        'use strict';

        /**
         * The Button widget class represents a container widget which can receive focus and be selected/activated by the user.
         * @name antie.widgets.Button
         * @class
         * @extends antie.widgets.Container
         * @requires antie.events.FocusDelayEvent
         * @requires antie.events.KeyEvent
         * @requires antie.events.SelectEvent
         * @param {String} [id] The unique ID of the widget. If excluded, a temporary internal ID will be used (but not included in any output).
         * @param {boolean} [animationEnabled] If true, a focus delay will be set before displaying the button
         */
        return Container.extend(/** @lends antie.widgets.Button.prototype */ {
            /**
             * @constructor
             * @ignore
             */
            init: function init (id, animationEnabled) {
                //TODO refactor this to set focusDelay explicitly rather than using animationEnabled with fixed focusDelay
                init.base.call(this, id);
                this.addClass('button');
                this.addClass('buttonBlurred');

                this._focusDelayHandle = null;
                this._disabled = false;

                /* Reduce the focusDelayTimeout for devices that don't have animation enabled */
                if(typeof animationEnabled === 'boolean' && !animationEnabled) {
                    this._focusDelayTimeout = 500;
                } else {
                    this._focusDelayTimeout = 1500;
                }

                /* if the ENTER key is pressed, translate into into a SelectEvent on this button */
                var self = this;
                this.addEventListener('keydown', function(e) {
                    if(e.keyCode === KeyEvent.VK_ENTER) {
                        self.select();
                        e.stopPropagation();
                    }
                });
            },
            /**
             * Renders the widget and any child widgets to device-specific output.
             * @param {antie.devices.Device} device The device to render to.
             * @returns A device-specific object that represents the widget as displayed on the device (in a browser, a DOMElement);
             */
            render: function render (device) {
                this.outputElement = device.createButton(this.id, this.getClasses(), '#');
                for(var i=0; i<this._childWidgetOrder.length; i++) {
                    device.appendChildElement(this.outputElement, this._childWidgetOrder[i].render(device));
                }
                return this.outputElement;
            },
            /**
             * Checks to see if a widget is focusable.
             * @see antie.widgets.Button
             * @returns {Boolean} True if the button is enabled.
             */
            isFocusable: function isFocusable () {
                // a widget can receive focus if it or any of it's descendants are Buttons
                // We're a button, so we are
                return !this._disabled;
            },
            /**
             * Set the button to be disabled and therefore not focusable. Adds buttonDisabled class.
             * @param {Boolean} disabled True if the button is to be disabled.
             */
            setDisabled: function setDisabled (disabled) {
                this._disabled = disabled;
                if(disabled) {
                    this.addClass('buttonDisabled');
                } else {
                    this.removeClass('buttonDisabled');
                }
            },
            /**
             * Gives this button focus by setting active children back up the widget tree.
             * @see antie.widgets.Button
             * @param {Boolean} [force] Pass <code>true</code> to force focus to a disabled button.
             * @returns Boolean true if focus has been moved to the button. Otherwise returns false.
             */
            focus: function focus (force) {
                var origDisabled = this._disabled;
                if(force) {
                    this._disabled = false;
                }

                var focusChanged = true;
                var w = this;
                while(w.parentWidget) {
                    if(!w.parentWidget.setActiveChildWidget(w)) {
                        focusChanged = false;
                    }
                    w = w.parentWidget;
                }

                this._disabled = origDisabled;

                return focusChanged;
            },
            select: function select () {
                this.bubbleEvent(new SelectEvent(this));
            },
            /**
             * Flags the active child as focussed or blurred.
             * @param {Boolean} focus True if the active child is to be focussed, False if the active child is to be blurred.
             * @private
             */
            _setActiveChildFocussed: function _setActiveChildFocussed (focus) {
                if(this._focusDelayHandle) {
                    clearTimeout(this._focusDelayHandle);
                }
                if(focus) {
                    this.removeClass('buttonBlurred');
                    this.addClass('buttonFocussed');

                    var self = this;
                    // Fire a focus delay event if this button has had focus for more than x-seconds.
                    this._focusDelayHandle = setTimeout(function() {
                        self.bubbleEvent(new FocusDelayEvent(self));
                    }, this._focusDelayTimeout);

                    this.getCurrentApplication()._setFocussedWidget(this);
                } else {
                    this.removeClass('buttonFocussed');
                    this.addClass('buttonBlurred');
                }
            },
            removeFocus: function removeFocus () {
                removeFocus.base.call(this);
                this.removeClass('buttonFocussed');
                this.addClass('buttonBlurred');
            }
        });
    }
);