API Docs for: 1.0.0
Show:

File: Resources/public/js/views/ez-barview.js

/*
 * Copyright (C) eZ Systems AS. All rights reserved.
 * For full copyright and license information view LICENSE file distributed with this source code.
 */
YUI.add('ez-barview', function (Y) {
    "use strict";
    /**
     * Provides the base Bar view class
     *
     * @module ez-barview
     */
    Y.namespace('eZ');

    var BAR_VIEW_NAME = 'barView',
        IS_HIDDEN_CLASS = "is-hidden",
        ACTIVE_MENU_CLASS = ".active-actions",
        VIEW_MORE_MENU_CLASS = ".view-more-actions",
        VIEW_MORE_BUTTON_CLASS = ".view-more-button";

    /**
     * The base bar view
     *
     * @namespace eZ
     * @class BarView
     * @constructor
     * @extends eZ.TemplateBasedView
     */
    Y.eZ.BarView = Y.Base.create(BAR_VIEW_NAME, Y.eZ.TemplateBasedView, [], {
        events: {
            '.view-more-button': {
                'tap': '_toggleViewMore'
            }
        },

        /**
         * Overrides the default implementation from Y.eZ.TemplateBasedView so
         * that all bar views use the same template
         *
         * @method _getName
         * @protected
         * @return String
         */
        _getName: function () {
            return BAR_VIEW_NAME;
        },

        /**
         * Initializer is called upon view's init
         * Creating actions lookup object, misc init workflow
         *
         * @method initializer
         */
        initializer: function () {
            var that = this;

            this._sortActions();
            Y.Array.each(this.get('actionsList'), function (action) {
                action.addTarget(that);
            });
            this.after('activeChange', function (e) {
                Y.Array.each(this.get('actionsList'), function (actionView) {
                    actionView.set('active', e.newVal);
                });
                if ( e.newVal ) {
                    this._handleHeightUpdate();
                }
            });
        },

        /**
         * Renders the edit action bar (non-height-responsive version).
         *
         * @method render
         * @return {eZ.EditActionBarView} the view itself
         */
        render: function () {
            var container = this.get('container'),
                activeMenu,
                viewMoreTrigger;

            container.setHTML(this.template({
                viewMoreText: this.get('viewMoreText')
            }));

            this._attachedViewEvents.push(Y.on("windowresize", Y.bind(this._handleHeightUpdate, this)));

            activeMenu = container.one(ACTIVE_MENU_CLASS);
            viewMoreTrigger = container.one(VIEW_MORE_BUTTON_CLASS);
            viewMoreTrigger.addClass(IS_HIDDEN_CLASS);

            Y.Array.each(this.get('actionsList'), function (actionView) {
                activeMenu.append(actionView.render().get('container'));
            });

            return this;
        },

        /**
         * Add the action to the main actions list
         *
         * @method addAction
         * @param action {eZ.ButtonActionView} instance of an action view
         */
        addAction: function (action) {
            var actionsList = this.get('actionsList');

            action.addTarget(this);
            actionsList.push(action);
            this.set('actionsList', actionsList);
            this._sortActions();
        },

        /**
         * Remove an action from the main actions list
         *
         * @method removeAction
         * @param actionId {String} Unique action identifier
         * @return {boolean} True, if action was found and removed, false otherwise
         */
        removeAction: function (actionId) {
            var actionsList = this.get('actionsList'),
                index,
                length;

            for (index = 0, length = actionsList.length; index < length; index++) {
                if (actionsList[index].get('actionId') == actionId) {
                    actionsList[index].removeTarget(this);
                    actionsList.splice(index, 1);
                    return true;
                }
            }

            return false;
        },

        /**
         * Get an action view with the actionId
         *
         * @method getAction
         * @param actionId {String}
         * @return {eZ.ButtonActionView}
         */
        getAction: function (actionId) {
            var actionsList = this.get('actionsList');

            return Y.Array.find(actionsList, function (t) {
                return t.get('actionId') === actionId;
            });
        },

        /**
         * Handles container height update by rearranging the actions between
         * menus. We are filling-in the active menu, until the container becomes
         * higher than the available height, after that we are filling the
         * hidden menu.
         *
         * @method _handleHeightUpdate
         * @protected
         * @param {Object} e event facade of the resize event
         */
        _handleHeightUpdate: function (e) {
            var container = this.get('container'),
                winHeight = container.get('winHeight'),
                containerPositionY = container.getY(),
                scrolled = container.get('docScrollY'),
                availHeight = winHeight - (containerPositionY - scrolled);

            if (this._getHeight() > availHeight) {
                // push actions into view more menu until the main menu is not
                // overflowed any more or we are out of actions in the main menu
                while (this._getHeight() > availHeight && this._hasActiveActions()) {
                    this._pushLastActionToViewMore();
                }

            } else {
                // Do we have to pull some actions from view more menu back to active menu?
                if (this._hasViewMoreActions()) {
                    // pull actions from view more menu until the main menu is
                    // overflowed or we are out of actions in view more menu
                    while ( this._hasViewMoreActions() && (this._getHeight() + this._getFirstViewMoreActionHeight() <= availHeight) ) {
                        this._pullFirstActionFromViewMore();
                    }
                }
            }
        },

        /**
         * Push last action from the ACTIVE_MENU_CLASS menu to the VIEW_MORE_MENU_CLASS menu
         *
         * @method _pushLastActionToViewMore
         * @private
         */
        _pushLastActionToViewMore: function () {
            var container = this.get('container'),
                activeMenu = container.one(ACTIVE_MENU_CLASS),
                viewMoreMenu = container.one(VIEW_MORE_MENU_CLASS),
                actionViewNode = activeMenu.get('children').slice(-1).item(0);

            if (actionViewNode) {
                actionViewNode.remove();
                viewMoreMenu.append(actionViewNode);
                this._checkViewMoreTrigger();
            }
        },

        /**
         * Pull first available action from the VIEW_MORE_MENU_CLASS menu to the ACTIVE_MENU_CLASS menu
         *
         * @method _pullFirstActionFromViewMore
         * @private
         */
        _pullFirstActionFromViewMore: function () {
            var container = this.get('container'),
                activeMenu = container.one(ACTIVE_MENU_CLASS),
                viewMoreMenu = container.one(VIEW_MORE_MENU_CLASS),
                actionViewNode = viewMoreMenu.get('children').slice(-1).item(0);

            if (actionViewNode) {
                actionViewNode.remove();
                activeMenu.append(actionViewNode);
                this._checkViewMoreTrigger();
            }
        },

        /**
         * Check do we need to show "View More" link. Do it, if needed.
         *
         * @method _checkViewMoreTrigger
         * @private
         */
        _checkViewMoreTrigger: function () {
            var container = this.get('container'),
                viewMoreTrigger = container.one(VIEW_MORE_BUTTON_CLASS),
                viewMoreMenu = container.one(VIEW_MORE_MENU_CLASS);

            if (viewMoreMenu.get('children').isEmpty()) {
                viewMoreTrigger.addClass(IS_HIDDEN_CLASS);
            } else {
                viewMoreTrigger.removeClass(IS_HIDDEN_CLASS);
            }
        },

        /**
         * Returns scroll height of the action bar view container
         *
         * @method _getHeight
         * @return {Int} Scroll height of the action bar view container
         * @private
         */
        _getHeight: function () {
            return this.get('container').get('scrollHeight');
        },

        /**
         * Returns scroll height of the first action in VIEW_MORE_MENU_CLASS menu
         *
         * @method _getFirstViewMoreActionHeight
         * @return {Int} Scroll height of the action
         * @private
         */
        _getFirstViewMoreActionHeight: function () {
            return this.get('container').one(VIEW_MORE_MENU_CLASS).get('children').slice(-1).item(0).get('scrollHeight');
        },

        /**
         * Indicates if there are some actions in ACTIVE_MENU_CLASS menu
         *
         * @method _hasActiveActions
         * @return {boolean} true if there are some actions in ACTIVE_MENU_CLASS menu, false otherwise.
         * @private
         */
        _hasActiveActions: function () {
            return !this.get('container').one(ACTIVE_MENU_CLASS).get('children').isEmpty();
        },

        /**
         * Indicates if there are some actions in VIEW_MORE_MENU_CLASS menu
         *
         * @method _hasViewMoreActions
         * @return {boolean} true if there are some actions in VIEW_MORE_MENU_CLASS menu, false otherwise.
         * @private
         */
        _hasViewMoreActions: function () {
            return !this.get('container').one(VIEW_MORE_MENU_CLASS).get('children').isEmpty();
        },

        /**
         * Sorts the actions list by priority
         *
         * @method _sortActions
         * @protected
         */
        _sortActions: function () {
            var actionsList = this.get('actionsList');

            actionsList.sort(function (a, b) {
                return b.get('priority') - a.get('priority');
            });
            this.set('actionsList', actionsList);
        },

        destructor: function () {
            Y.Array.each(this.get('actionsList'), function (view) {
                view.removeTarget(this);
                view.destroy();
            }, this);
        },

        /**
         * Event event handler for clicks on "View More" link
         *
         * @method _toggleViewMore
         * @protected
         * @param {Object} e event facade of the click event
         */
        _toggleViewMore: function (e) {
            var container = this.get('container'),
                viewMoreTrigger = container.one(VIEW_MORE_BUTTON_CLASS),
                viewMoreActionsNode = container.one(VIEW_MORE_MENU_CLASS);

            viewMoreActionsNode.toggleClass(IS_HIDDEN_CLASS);

            if (!viewMoreActionsNode.hasClass(IS_HIDDEN_CLASS)) {
                viewMoreTrigger.setHTML(this.get('viewLessText'));
            } else {
                viewMoreTrigger.setHTML(this.get('viewMoreText'));
            }
        }

    }, {
        ATTRS: {
            /**
             * The actions list
             *
             * @attribute actionsList
             * @type Array
             * @default []
             */
            actionsList: {
                value: []
            },

            /**
             * Text on the "View more" action (shown when second part of the menu is hidden)
             *
             * @attribute viewMoreText
             * @default "View more"
             */
            viewMoreText: {
                value: "View more"
            },

            /**
             * Text on the "View less" action (shown when second part of the menu is shown)
             *
             * @attribute viewLessText
             * @default "View less"
             */
            viewLessText: {
                value: "View less"
            },

            /**
             * Content which is currently loaded in content edit view
             *
             * @attribute content
             * @type Y.eZ.Content
             * @default {}
             * @required
             */
            content: {
                value: {},
                setter: function (val, name) {
                    Y.Array.each(this.get('actionsList'), function (actionView) {
                        actionView.set('content', val);
                    });

                    return val;
                }
            }
        }
    });
});