Source: layout/tabs.js

/**
 * @class ludo.Layout.Tabs
 *
 */
ludo.layout.Tabs = new Class({
    Extends: ludo.View,
    type: 'layout.Tabs',
    tabPos: 'left',
    lm: undefined,
    tabs: {},
    svgTexts: {},

    currentPos: -1,
    activeTab: undefined,
    currentZIndex: 3,
    activeTabId: undefined,

    tabParent: undefined,

    tabPositions: undefined,

    tabMenuEl: undefined,
    elLine: undefined,

    maxPos: undefined,

    maxSize: undefined,

    hiddenTabs: undefined,

    menu: undefined,

    tabTitles: undefined,
    alwaysInFront: true,

    __construct: function (config) {
        this.parent(config);
        this.setConfigParams(config, ['tabPos']);
        this.lm = config.lm;
        this.hiddenTabs = [];
        this.tabTitles = {};
    },
    ludoEvents: function () {
        this.parent();
        this.lm.addEvent('addChild', this.registerChild.bind(this));
        this.lm.addEvent('addChildRuntime', this.resizeTabs.bind(this));
        this.lm.addEvent('showChild', this.activateTabFor.bind(this));
        this.lm.addEvent('hideChild', this.hideTabFor.bind(this));
        this.lm.addEvent('removeChild', this.removeTabFor.bind(this));
        this.addEvent('resize', this.resizeTabs.bind(this));
    },

    __rendered: function () {
        this.parent();
        this.resizeTabs();
    },

    registerChild: function (layout, parent, child) {
        if (!this.lm.isTabs(child)) {
            this.createTabFor(child);
        }
    },

    resizeTabs: function () {


        this.showAllTabs();
        if (this.tabPositions == undefined) {
            this.tabPositions = {};
        }

        this.resizeTabsDom();

        if (this.tabPos === 'top' || this.tabPos === 'bottom')this.findHiddenTabs();
    },

    showAllTabs: function () {
        $.each(this.tabs, function (key) {
            this.tabs[key].show();

        }.bind(this));
    },

    resizeTabsDom: function () {
        var pos = 0;
        var size;

        $.each(this.tabs, function (key) {

            var node = this.tabs[key];
            if (this.tabPos === 'top' || this.tabPos === 'bottom') {
                size = node.outerWidth(true);
            } else {
                size = node.outerHeight(true);
            }
            this.tabPositions[key] = {
                pos: pos,
                size: size
            };
            node.css(this.getPosAttribute(), pos + 'px');

            pos += size;

            this.maxPos = pos;

        }.bind(this));
    },

    findHiddenTabs: function () {

        if (!this.tabParent)return;

        this.tabParent.css('left', 0);

        if (!this.haveTabsOutOfView()) {
            if (this.tabMenuEl)this.tabMenuEl.hide();
            return;
        }

        this.moveCurrentIntoView();

        this.hiddenTabs = [];

        var size = this.getBody().width();
        var menu = this.getMenuIcon();


        size -= menu.outerWidth(true);

        var pos = Math.abs(this.tabParent.position().left);

        $.each(this.tabPositions, function (id, position) {
            if (position.pos < pos || position.pos + position.size > pos + size) {
                this.hiddenTabs.push(id);

                if (position.pos >= pos) {
                    this.tabs[id].hide();
                }
            } else {
                this.tabs[id].show();
            }
        }.bind(this));

        menu.html(this.hiddenTabs.length);

        menu.css('visibility', this.hiddenTabs.length > 0 ? 'visible' : 'hidden');


        if (this.hiddenTabs.length > 0) {
            this.maxSize = size - menu.outerWidth(true);
            this.moveCurrentIntoView();
            this.getMenuIcon().show();
        }
        this.resizeTabsDom();
    },

    moveCurrentIntoView: function () {

        var size = this.getBody().width();
        var menu = this.getMenuIcon();
        size -= menu.outerWidth(true);
        var pos = Math.abs(this.tabParent.position().left);

        var tabPosition = this.tabPositions[this.activeTabId];

        var offsetStart = tabPosition.pos - pos;
        var offsetEnd = (tabPosition.pos + tabPosition.size) - (pos + size);

        if (offsetStart < 0) {
            this.tabParent.css('left', offsetStart);
        } else if (offsetEnd > 0) {
            var newPos = pos - offsetEnd;
            this.tabParent.css('left', newPos);
        }
    },

    haveTabsOutOfView: function () {
        return this.maxPos > this.getBody().width();
    },

    getMenuIcon: function () {
        if (this.tabMenuEl == undefined) {

            if (this.tabPos == 'left' || this.tabPos == 'right') {
                this.tabMenuEl = this.getBody();
                return this.tabMenuEl;
            }
            this.tabMenuEl = $('<div class="ludo-tab-expand-box ludo-tab-expand-box-' + this.tabPos + '"></div>');
            this.getBody().append(this.tabMenuEl);

            var s = this.getBody().outerHeight() - this.elLine.height();
            var k = this.tabPos == 'top' || this.tabPos == 'bottom' ? 'height' : 'width';
            this.tabMenuEl.css(k, s);

            if (this.tabPos == 'bottom') {
                this.tabMenuEl.css('top', this.elLine.outerHeight());
            }

            this.tabMenuEl.css('line-height', s + "px");

            this.tabMenuEl.on('click', this.toggleMenu.bind(this));
            this.tabMenuEl.css('visibility', 'hidden');

            this.tabMenuEl.mouseenter(this.enterMenuIcon.bind(this));
            this.tabMenuEl.mouseleave(this.leaveMenuIcon.bind(this));

        }
        return this.tabMenuEl;
    },

    enterMenuIcon: function (e) {
        $(e.target).addClass('ludo-tab-expand-box-' + this.tabPos + '-over');
    },

    leaveMenuIcon: function (e) {
        $(e.target).removeClass('ludo-tab-expand-box-' + this.tabPos + '-over');
    },

    getMenu: function () {
        if (this.menu == undefined) {
            var layout = {
                type: 'menu',
                orientation: 'vertical',
                alignLeft: this.tabMenuEl,
                height: 'wrap',
                width: 'wrap'
            };

            if (this.tabPos == 'top') {
                layout.below = this.tabMenuEl
            } else {
                layout.above = this.tabMenuEl;
            }
            this.menu = new ludo.menu.Menu({
                renderTo: document.body,
                hidden: true,
                alwaysInFront: true,
                layout: layout,
                listeners: {
                    'click': function (item) {
                        if (item.action && this.tabs[item.action] != undefined) {
                            ludo.$(item.action).show();
                            this.menu.hide();
                            this.resizeTabs();
                        }
                    }.bind(this)
                }

            });
            this.menu.getEl().mousedown(ludo.util.cancelEvent);
            ;
            ludo.EffectObject.on('start', this.hideMenu.bind(this));
            $(document.documentElement).mousedown(this.domClick.bind(this));

        }
        return this.menu;
    },

    menuShown: false,

    domClick: function (e) {
        if (this.menu == undefined)return;
        if (e.target == this.tabMenuEl[0])return;
        this.hideMenu();
    },

    autoHide: function () {
        this.hideMenu();
    },

    hideMenu: function () {
        this.getMenu().hide();
    },


    toggleMenu: function () {
        var menu = this.getMenu();

        if (menu.isHidden()) {
            this.showMenu();
        } else {
            this.menu.hide();
        }
    },

    showMenu: function () {
        this.menuShown = true;
        var menu = this.getMenu();

        menu.show();

        menu.disposeAllChildren();
        $.each(this.hiddenTabs, function (index, id) {
            menu.addChild({
                label: this.tabTitles[id],
                action: id
            });
        }.bind(this));

        this.menu.getLayout().resize();
    },

    createTabFor: function (child) {

        if (this.tabParent == undefined) {
            this.tabParent = $('<div style="position:absolute" class="ludo-tab-layout-parent-for-tabs ludo-tab-layout-parent-for-tabs-' + this.tabPos + '"></div>');
            if (this.tabPos == 'top' || this.tabPos == 'bottom') {
                this.tabParent.css({
                    height: this.getBody().height(),
                    width: 10000
                });
            } else {
                this.tabParent.css({
                    width: this.getBody().width(),
                    height: 10000
                });
            }

            this.getBody().append(this.tabParent);
        }
        var node;
        if (this.tabPos === 'top' || this.tabPos == 'bottom') {
            node = this.getPlainTabFor(child);
        } else {
            node = this.getSVGTabFor(child);
        }

        node.on('click', child.show.bind(child, false));
        this.tabParent.append(node);
        if (child.layout.closable) {
            this.addCloseButton(node, child);
        }
        node.css(this.getPosAttribute(), this.currentPos);
        node.addClass("ludo-tab-strip-tab");
        node.addClass('ludo-tab-strip-tab-' + this.tabPos);
        this.tabs[child.getId()] = node;

        if (!child.isHidden())this.activateTabFor(child);

    },

    addCloseButton: function (node, child) {
        var el = $('<div>');
        el.addClass('ludo-tab-close ludo-tab-close-' + this.tabPos);
        el.mouseenter(this.enterCloseButton.bind(this));
        el.mouseleave(this.leaveCloseButton.bind(this));
        el.attr('id', 'tab-close-' + child.id);
        el.on('click', this.removeChild.bind(this));
        node.append(el);
        var p;
        switch (this.tabPos) {
            case 'top':
            case 'bottom':
                p = node.css('padding-right');
                node.css('paddingRight', (parseInt(p) + el.outerWidth()));
                break;
            case 'right':
                p = node.css('padding-right');
                node.css('paddingBottom', (parseInt(p) + el.outerHeight()));
                break;
            case 'left':
                p = node.css('padding-right');
                node.css('paddingTop', (parseInt(p) + el.outerHeight()));
                break;
        }
    },

    removeChild: function (e) {
        var id = e.target.id.replace('tab-close-', '');
        ludo.get(id).remove();
        return false;
    },

    removeTabFor: function (child) {
        this.tabs[child.getId()].remove();
        if (this.svgTexts[child.getId()]) {
            this.svgTexts[child.getId()].remove();
            delete this.svgTexts[child.getId()];
        }
        delete this.tabs[child.getId()];
        this.tabPositions[child.getId()] = undefined;

        this.resizeTabs();
    },

    enterCloseButton: function (e) {
        $(e.target).addClass('ludo-tab-close-' + this.tabPos + '-over');
    },

    leaveCloseButton: function (e) {
        $(e.target).removeClass('ludo-tab-close-' + this.tabPos + '-over');
    },

    getPosAttribute: function () {
        if (!this.posAttribute) {
            switch (this.tabPos) {
                case 'top':
                case 'bottom':
                    this.posAttribute = 'left';
                    break;
                case 'left':
                case 'right':
                    this.posAttribute = 'top';
                    break;
            }
        }
        return this.posAttribute;
    },


    getPlainTabFor: function (child) {
        var el = $('<div>');
        this.getBody().append(el);
        el.className = 'ludo-tab-strip-tab ludo-tab-strip-tab-' + this.tabPos;
        el.html('<div class="ludo-tab-strip-tab-bg"></div><span style="z-index:2">' + this.getTitleFor(child) + '</span>');
        return el;
    },

    getSVGTabFor: function (child) {
        var el = $('<div><div class="ludo-tab-strip-tab-bg"></div></div>');
        this.getBody().append(el);

        var svgEl = $('<div style="z-index:2;position:relative">');
        el.append(svgEl);
        var box = new ludo.layout.TextBox({
            renderTo: svgEl,
            width: 1000, height: 1000,
            className: 'ludo-tab-strip-tab-txt-svg',
            classNameOver: 'ludo-tab-strip-tab-txt-svg-active',
            text: this.getTitleFor(child),
            rotation: this.getRotation()
        });
        var size = box.getSize();
        svgEl.css({
            'width': size.x, height: size.y
        });

        this.svgTexts[child.getId()] = box;
        return el;
    },

    getRotation: function () {
        if (this.rotation === undefined) {
            switch (this.tabPos) {
                case 'left' :
                    this.rotation = 270;
                    break;
                case 'right' :
                    this.rotation = 90;
                    break;
                case 'bottom' :
                    this.rotation = 180;
                    break;
                default :
                    this.rotation = 0;
                    break;
            }
        }
        return this.rotation;
    },

    getTitleFor: function (child) {
        var title = (child.title || child.layout.title || child.getTitle());
        this.tabTitles[child.id] = title;
        return title;
    },

    hideTabFor: function () {
        if (this.activeTab != undefined) {
            this.activeTab.removeClass('ludo-tab-strip-tab-active');
            this.svgTextMethod(this.activeTabId, 'leave');

        }
    },

    svgTextMethod: function (childId, method) {
        var t = this.svgTexts[childId];
        if (t != undefined && t[method] != undefined) {
            t[method]();
        }
    },

    activateTabFor: function (child) {
        this.hideTabFor();

        if (this.tabs[child.id] !== undefined) {

            this.svgTextMethod(child.id, 'enter');

            this.tabs[child.id].addClass('ludo-tab-strip-tab-active');
            this.activeTab = this.tabs[child.id];
            this.activeTab.css('zIndex', this.currentZIndex);
            this.currentZIndex++;

            this.activeTabId = child.id;

            if (this.hiddenTabs.length > 0) {
                this.resizeTabs();
            }
        }

    },

    ludoDOM: function () {
        this.parent();
        this.getEl().addClass('ludo-tab-strip');
        this.getEl().addClass('ludo-tab-strip-' + this.tabPos);

        var el = $('<div>');
        el.addClass('ludo-tab-strip-line');
        this.elLine = el;
        this.getBody().append(el);

        this.getMenuIcon();
    },

    getTabFor: function (child) {
        return this.tabs[child.id]
    },

    getChangedViewport: function () {
        var value;
        if (this.tabPos === 'top' || this.tabPos === 'bottom') {
            value = this.getEl().outerHeight(true);
        } else {
            value = this.getEl().outerWidth(true);
        }
        return {
            key: this.tabPos, value: value
        };
    },
    getCount: function () {
        return Object.keys(this.tabs).length;
    },

    resize: function (size) {
        this.parent(size);
    }
});