Source: widgets/search/input.js

define('application/widgets/search/input', [
    'application/widgets/search/inputfield',
    'rofl/widgets/label',
    'rofl/widgets/verticallist',
    'application/widgets/search/multikbd',
    'application/managers/search',
    'application/widgets/search/lastsearched',
    'rofl/widgets/horizontallist',
    'rofl/lib/l10n',
    'application/widgets/pointerfocusablebutton',
    'rofl/lib/utils',
    'antie/runtimecontext',
    'rofl/widgets/container'
], function (
    InputField,
    Label,
    VerticalList,
    MultiKeyboard,
    SearchManager,
    LastSearched,
    HorizontalList,
    L10N,
    PointerFocusableButton,
    Utils,
    RuntimeContext,
    Container
) {
    'use strict';

    var Input,
        searchManager = SearchManager.getInstance(),
        application = RuntimeContext.getCurrentApplication(),
        configuration = application.getConfiguration(),
        searchLayout = application.getLayout().search,
        lastSearchedFromRight = searchLayout.lastSearched.fromRight,
        GRADIENT_CLASS = 'gradient-visible',
        MAX_LAST_SEARCHED_ITEMS = configuration.search.maxLastSearchedItems;

    Input = VerticalList.extend({

        /**
         * Initialises the input widget.
         *
         * @param {Object} config - The configuration.
         * @param {Object} config.inputField - The inputfield options.
         * @param {Object} [config.inputField.opts] - The options. Optional.
         */
        init: function init (config) {

            init.base.call(this);
            this.addClass('input-container');
            this._build(config);
            this.addEventListener('selecteditemchange',
                Utils.bind(this.onSelectedItemChange, this));
            this.addEventListener('focus', Utils.bind(this.onFocus, this));
        },

        /**
         * Builds the widget.
         *
         * @param {Object} config - The configuration.
         * @param {Object} config.inputField - The inputfield options.
         * @param {Object} [config.inputField.opts] - The options. Optional.
         * @private
         */
        _build: function (config) {
            this._buildInputField(config.inputField);
            this._buildKeyboard();
            this._buildLastSearch();
        },

        /**
         * Builds the input field.
         *
         * @param {Object} config - The inputfield options.
         * @param {Object} [config.opts] - The options. Optional.
         * @private
         */
        _buildInputField: function (config) {
            var inputField = this._inputField = new InputField(config);

            this.appendChildWidget(inputField);
        },

        /**
         * Build keyboard widget.
         *
         * @private
         */
        _buildKeyboard: function () {
            this._keyboard = new MultiKeyboard();

            this._inputField.setKeyboard(this._keyboard);
            this.appendChildWidget(this._keyboard);
        },

        /**
         * Builds the last searched items list.
         *
         * @private
         */
        _buildLastSearch: function () {
            var searchedItems = searchManager.getLastSearchedItems(),
                lastSearchedContainer = this._lastSearchedContainer = new Container('last-searched-container'),
                lastSearchedList = this._lastSearchedList = new VerticalList(),
                searchedItemsArray,
                lastsearched,
                i;

            lastSearchedList.addClass('last-searched-list');
            lastSearchedContainer.addClass('last-searched-container');

            if (searchedItems) {

                searchedItemsArray = searchedItems.split('~');
                searchedItemsArray = searchedItemsArray.reverse();

                // Limit the items to show.
                searchedItemsArray.splice(MAX_LAST_SEARCHED_ITEMS);

                for (i = 0; i < searchedItemsArray.length; i++) {
                    lastsearched = new LastSearched(searchedItemsArray[i]);
                    lastSearchedList.appendChildWidget(lastsearched);
                }

            }

            lastSearchedContainer.appendChildWidget(lastSearchedList);
            this.appendChildWidget(lastSearchedContainer);
        },

        /**
         * Builds clear button.
         *
         * @private
         */
        _buildClearButton: function () {
            var clearButton,
                clearLabel;

            clearButton = this._clearButton = new PointerFocusableButton('clearbutton');
            clearLabel = new Label({ text: '', classNames: ['icon', 'icon-delete-v2'] });

            clearButton.addClass('clear-button');
            clearButton.appendChildWidget(clearLabel);
        },

        /**
         * Returns value of the input.
         *
         * @returns {string} - Value of the inupt.
         */
        getValue: function () {
            return this._inputField.getValue();
        },

        /**
         * Returns inputfield object.
         *
         * @returns {Object} - Inputfield Object.
         */
        getInputField: function () {
            return this._inputField;
        },

        /**
         * Focus this widget.
         */
        focus: function () {
            this._keyboard.focus();
        },

        /**
         * Updates carousel object.
         */
        updateLastSearchedList: function () {
            var carousel = this._lastSearchedList,
                searchedItems = searchManager.getLastSearchedItems(),
                searchedItemsArray,
                lastsearched,
                i;

            if (this._canUpdateFocusIndex) {
                this._lastFocussedIndex = carousel.getActiveChildWidgetIndex();
                this._canUpdateFocusIndex = false;
            }
            carousel.removeChildWidgets();

            if (searchedItems) {
                carousel.removeChildWidgets();
                searchedItemsArray = searchedItems.split('~');
                searchedItemsArray = searchedItemsArray.reverse();

                // Limit the items to show.
                searchedItemsArray.splice(MAX_LAST_SEARCHED_ITEMS);

                for (i = 0; i < searchedItemsArray.length; i++) {
                    lastsearched = new LastSearched(searchedItemsArray[i]);
                    carousel.appendChildWidget(lastsearched);
                }
            }
        },

        /**
         * Returns the keyboard.
         *
         * @returns {Object} - The keyboard.
         */
        getKeyboard: function () {
            return this._keyboard;
        },

        /**
         * Reset input widget.
         */
        reset: function () {
            var keyboard = this._keyboard;

            keyboard.reset();
            keyboard._keyboard.focus();

            if (this._lastSearchedList.getChildWidgetCount()) {
                this._lastSearchedList.setActiveChildIndex(0);
            }
        },

        /**
         * Gets executed when item is focussed.
         *
         * @param {Object} e - The event data.
         */
        onFocus: function (e) {
            var target = e.target;

            if (target.hasClass('last-searched')) {
                this._lastIndex = target.parentWidget._selectedIndex;
            }
        },

        /**
         * Gets clear button.
         *
         * @returns {Object} Clear button.
         */
        getClearButton: function () {
            return this._clearButton;
        },

        /**
         * Gets executed when the selected item changes.
         *
         * @param {Object} e - The event data.
         */
        onSelectedItemChange: function (e) {
            var item = e.item,
                lastFocussedIndex = this._lastFocussedIndex || 0;

            if (item.hasClass('top-list') && item.hasClass('focus')) {
                this._lastSearchedList.getChildWidgetByIndex(lastFocussedIndex).focus();
                this._canUpdateFocusIndex = true;
            } else if (item.hasClass('last-searched')) {
                this._positionLastSearched(item, e.index);
            }
        },

        /**
         * Positions the last searched row.
         *
         * @param {Object} element - The element.
         * @param {number} index - The element index.
         * @private
         */
        _positionLastSearched: function (element, index) {
            var position = this._getNewElementPosition(element),
                lastSearchedContainer = this._lastSearchedContainer;

            if (position) {
                this._lastSearchedList.setStyleTo('left', position + 'px');

                if (index === 0) {
                    lastSearchedContainer.removeClass(GRADIENT_CLASS);
                } else {

                    if (!lastSearchedContainer.hasClass(GRADIENT_CLASS)) {
                        lastSearchedContainer.addClass(GRADIENT_CLASS);
                    }
                }
            }
        },

        /**
         * Returns the new element position.
         *
         * @param {Object} element - The element to get the position for.
         * @returns {number|null} - The new position or null.
         * @private
         */
        _getNewElementPosition: function (element) {
            var listElement = this._lastSearchedList.outputElement,
                currentPosition = parseInt((listElement.style.left || '0').replace('px', '')),
                containerPosition = this._lastSearchedContainer.outputElement.getBoundingClientRect(),
                position,
                newPosition,
                positionDifference;

            if (!element.outputElement) {
                return null;
            }

            position = element.outputElement.getBoundingClientRect();

            if (position.right >= lastSearchedFromRight) {

                // Row should move left.
                positionDifference = position.right - lastSearchedFromRight;
                newPosition = currentPosition - positionDifference;
                return newPosition;
            } else if (position.left < containerPosition.left) {

                // Row should move right.
                positionDifference = containerPosition.left - position.left;
                newPosition = currentPosition + positionDifference;
                return newPosition;
            }

            // Row should not move.
            return null;
        }
    });

    return Input;
});