Source: layout/accordion.js

/**
 * This layout will render children in an Accordion. For a demo, see
 * <a href="../demo/layout/accordion.php" onclick="var w=window.open(this.href);return false">accordion demo</a>
 * @class ludo.layout.Accordion
 * @param {object} layout Layout config properties
 * @param {String} layout.type Set "type" to "accordion" on a parent view to render children in an accordion layout.
 * @param {String} layout.easing Easing for the animation. Default: "swing". easing is used for jQuery.animate, so any easing properties you have
 * available in jQuery or jQuery plugins are available here.
 * @param {number} layout.duration Duration of accordion animation in milliseconds(1/1000s).
 * @param {String} layout.title Option for child views inside an accordion layout. This is the title for the clickable title bar for each child view.
 * @example
 var w = new ludo.Window({
        title:'Accordion layout',
        layout:{
            left:50, top:50,
            width:700, height:600,
            type:'accordion',
            easing: 'linear',
            duration: 300
        },
        children:[
            {
                title: 'Drawing', // Title for the accordion
                html: '<img src="../images/drawing.png" style="margin-right:5px;margin-bottom:5px;float:left">' +
                'This is a Charcoal drawing on Smooth Newsprint paper. <br>',
                css:{ // CSS styling for the view
                    padding:5,
                    'font-size' : '1.2em',
                    'overflow-y': 'auto'
                }
            },
            {
                title: 'Second Accordion View',// Title for the accordion
                html: 'Second Accordion',
                css:{
                    padding:5
                }
            },
            {
                title: 'Source Code',// Title for the accordion
                type:'SourceCodePreview',
                css:{
                    padding:5
                }
            }
        ]
    });
 */

ludo.layout.Accordion = new Class({
    Extends: ludo.layout.Base,
    easing: 'swing',
    titleEls: undefined,
    titleHeight: undefined,


    expandedView: undefined,
    expandedChild: undefined,
    expandedTitle: undefined,

    childIndex: 0,

    duration: 200,
    busy: false,

    onCreate: function () {
        this.parent();
        var l = this.view.layout;
        if (l.easing != undefined)this.easing = l.easing;
        if (l.duration != undefined)this.duration = l.duration;
        this.titleEls = [];

    },

    addChild: function (child, insertAt, pos) {
        if (child.layout == undefined)child.layout = {};
        child.layout.index = this.childIndex++;

        return this.parent(child, insertAt, pos);
    },

    addChildEvents:function(child){
        child.addEvent('show', this.onShowHide.bind(this));
        child.addEvent('hide', this.onShowHide.bind(this));
    },

    onShowHide:function(child){

        var id = child.id;

        var style = child.hidden ? "none": "";


        $('#accordion-title-' + id).css('display', style);
        $('#accordion-' + id).css('display', style);

        this.measureTitleHeight();
        this.resize();


        if(!child.hidden)this.expandChild(id);

        if(id == this.expandedChild){
            this.expandedChild = this.firstExpanded();
            this.expandChild(this.expandedChild );
        }

    },

    onNewChild:function(child){
        this.parent(child);
        if(this.hasBeenRendered()){
            this.measureTitleHeight();
            this.resize();
        }
    },

    getParentForNewChild: function (child) {
        var el = $('<div class="ludo-framed-view-titlebar ludo-accordion-titlebar"></div>');

        var title = $('<div class="ludo-framed-view-titlebar-title ludo-accordion-title"></div>');
        var expand = $('<div class="ludo-accordion-collapsed"></div>');
        expand.attr("id", "accordion-expand-" + child.id);
        el.attr("id", "accordion-title-" + child.id);
        el.attr("data-accordion-for", child.id);
        el.on('click', this.getExpandFn(child));
        el.attr("data-child-index", child.layout.index);
        el.append(expand);
        el.append(title);
        if (child.title) {
            title.html(child.title);
        }
        this.view.getBody().append(el);

        this.titleEls.push(el);

        var container = $('<div class="ludo-accordion-container" style="height:0"></div>');
        container.attr("id", "accordion-" + child.id);
        this.view.getBody().append(container);

        if (child.hidden) {
            el.css('display', 'none');
            container.css('display', 'none');
        }
        return container;
    },

    getExpandFn: function (child) {
        return function () {
            this.expandChild(child.id);
        }.bind(this);
    },

    expandChild: function (id) {
        if (this.busy)return;

        var view = $("#accordion-" + id);
        var expand = $("#accordion-expand" + id);

        if (id == this.expandedChild)return;
        var h = this.viewport.height - this.titleHeight;

        if (this.expandedView) {
            var el = this.expandedView;
            var fn = this.onAnimationComplete.bind(this);
            var d = this.duration;
            var e = this.easing;


            view.height(0);
            $(function () {
                el.animate({height: 0, easing: e}, d, fn);
                view.animate({height: h, easing: e}, d);
            });
            this.busy = true;
        } else {
            view.animate({height: h}, 200);
        }
        this.expandedView = view;
        this.expandedChild = id;
        this.toggleTitle(id);
    },

    titleNextOfOpened: undefined,
    expandIcon: undefined,

    toggleTitle: function (id) {
        if (this.expandIcon) {
            this.expandIcon.removeClass('ludo-accordion-expanded');

        }
        this.expandIcon = $('#accordion-expand-' + id);
        this.expandIcon.addClass('ludo-accordion-expanded');
        this.expandedTitle = $("#accordion-title-" + id);
        if (this.titleNextOfOpened) {
            this.titleNextOfOpened.removeClass('ludo-accordion-titlebar-below-expanded');
        }
        var index = parseInt(this.expandedTitle.attr("data-child-index"));
        var next = this.nextVisible(index);

        if (next >= 0) {
            this.titleNextOfOpened = this.titleEls[next];
            this.titleNextOfOpened.addClass('ludo-accordion-titlebar-below-expanded');
        }

    },

    nextVisible: function (index) {
        for (var i = index + 1; i < this.view.children.length; i++) {
            if (!this.view.children[i].hidden) {
                return i;
            }
        }
        return -1;
    },

    measureTitleHeight: function () {
        this.titleHeight = 0;
        for (var i = 0; i < this.titleEls.length; i++) {
            if (!this.view.children[i].hidden) {
                this.titleHeight += this.titleEls[i].outerHeight();

            }
        }
    },

    onAnimationComplete: function () {
        this.busy = false;
    },

    beforeFirstResize: function () {
        this.measureTitleHeight();
        this.expandedChild = this.firstExpanded().id;
        this.expandedView = $('#accordion-' + this.expandedChild);
        this.expandedView.css('height', this.viewport.height - this.titleHeight);
        this.toggleTitle(this.expandedChild);

    },

    firstExpanded: function () {
        var ret = undefined;
        var c = this.view.children;
        for (var i = 0; i < c.length; i++) {
            if (c[i].layout.expanded && !c[i].hidden) {
                ret = c[i];
                break;
            }
        }
        if (!ret) {
            for (i = 0; i < c.length; i++) {
                if (!c[i].hidden) {
                    ret = c[i];
                    break;
                }
            }
        }

        return ret;
    },

    resize: function () {
        var c = this.view.children;
        var h = this.viewport.height - this.titleHeight;
        if (this.expandedView) {
            this.expandedView.css('height', h);
        }
        var size = {
            width: this.viewport.width,
            height: h
        };
        for (var i = 0; i < c.length; i++) {
            c[i].resize(size);
        }
    }
});