Source: widgets/horizontallist.js

/**
 * @fileOverview Requirejs module containing the antie.widgets.HorizontalList 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/horizontallist',
    [
        'antie/widgets/list',
        'antie/events/keyevent'
    ],
    function(List, KeyEvent) {
        'use strict';

        /**
         * The HorizontalList widget is a container widget that supports spatial navigation between items using {@link KeyEvent.VK_LEFT} and {@link KeyEvent.VK_RIGHT}.
         * @name antie.widgets.HorizontalList
         * @class
         * @extends antie.widgets.List
         * @requires antie.events.KeyEvent
         * @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 {antie.Formatter} [itemFormatter] A formatter class used on each data item to generate the list item child widgets.
         * @param {antie.DataSource|Array} [dataSource] An array of data to be used to generate the list items, or an aysnchronous data source.
         */
        var HorizontalList = List.extend(/** @lends antie.widgets.HorizontalList.prototype */ {
            /**
             * @constructor
             * @ignore
             */
            init: function init (id, itemFormatter, dataSource) {
                // we need to wrap our contents in a mask to support animation
                this._maskElement = null;

                init.base.call(this, id, itemFormatter, dataSource);
                this.addClass('horizontallist');

                var self = this;
                this.addEventListener('keydown', function(e) {
                    self._onKeyDown(e);
                });
            },

            /**
             * Set whether to support wrapping within the list.
             * @param {Integer} wrapMode    Pass <code>HorizontalList.WRAP_MODE_NONE</code> for no wrapping.
             *                              Pass <code>HorizontalList.WRAP_MODE_NONE</code> to allow navigation to wrap.
             */
            setWrapMode: function setWrapMode (wrapMode) {
                this._wrapMode = wrapMode;
            },

            /**
             * Key handler for horizontal lists. Processes KeyEvent.VK_LEFT and KeyEvent.VK_RIGHT keys and stops propagation
             * if the keypress is handled. Otherwise allows the event to be bubbled up to the parent widget to allow
             * spatial navigation out of the list.
             * @param {antie.events.KeyEvent} evt The key event.
             */
            _onKeyDown: function _onKeyDown (evt) {
                if(evt.keyCode !== KeyEvent.VK_LEFT && evt.keyCode !== KeyEvent.VK_RIGHT) {
                    return;
                }

                var _newSelectedIndex = this._selectedIndex;
                var _newSelectedWidget = null;
                do {
                    if(evt.keyCode === KeyEvent.VK_LEFT) {
                        _newSelectedIndex--;
                    } else if(evt.keyCode === KeyEvent.VK_RIGHT) {
                        _newSelectedIndex++;
                    }

                    //force the index to wrap correctly
                    if(this._wrapMode === HorizontalList.WRAP_MODE_WRAP ) {
                        _newSelectedIndex = ( _newSelectedIndex + this._childWidgetOrder.length ) % this._childWidgetOrder.length;
                    } else if(_newSelectedIndex < 0 || _newSelectedIndex >= this._childWidgetOrder.length) {

                        break;
                    }
                    var _widget = this._childWidgetOrder[_newSelectedIndex];
                    if(_widget.isFocusable()) {
                        _newSelectedWidget = _widget;
                        break;
                    }
                } while(true);

                if(_newSelectedWidget) {
                    this.setActiveChildWidget(_newSelectedWidget);
                    evt.stopPropagation();
                }

                return (_newSelectedWidget !== null);
            }
        });

        HorizontalList.WRAP_MODE_NONE = 0;
        HorizontalList.WRAP_MODE_WRAP = 1;

        return HorizontalList;
    }
);