eliace/ergo-js

View on GitHub
js/core/layout.js

Summary

Maintainability
F
5 days
Test Coverage

//= require dom







/**
 * @class
 * @name Ergo.core.Layout
 * @param {Object} opts
 * @extends Ergo.core.DOM
 */
Ergo.defineClass('Ergo.core.Layout', /** @lends Ergo.core.Layout.prototype */ {

    extends: 'Ergo.core.DOM',

    defaults: {
//        updateMode: 'auto'
    },


    _initialize: function() {
        Ergo.core.Layout.superclass._initialize.apply(this, arguments);

        var o = this.options;


        this.outerEl = this.el;
    this.innerEl = this.el;


//        this.options = o || {};
        if('name' in this.defaults) {
            this.options.name = this.defaults.name;
        }

        if('name' in o) this.el.setAttribute('layout', o.name);
        if('cls' in o) this.addClass(o.cls.join(' '));

        // зачатки шаблона
    if(this.html) {
      this.el.innerHTML = this.html;
      while(this.innerEl.childNodes.length) {
        this.innerEl = this.innerEl.childNodes[0];
      }
    }



    },




    detach: function() {
        if(this.outerEl.parentNode) {
            this.outerEl.parentNode.removeChild(this.outerEl);
        }
    },


/*
    _initialize: function(o){
//        this._super(o);
        Ergo.core.Layout.superclass._initialize.call(this, o);

        this.events = new Ergo.events.Observer(this);

//        var o = this.options = {};
//
//        Ergo.hierarchy(this.constructor, function(clazz){
//            if('defaults' in clazz) Ergo.smart_override(o, clazz.defaults);
//        });
//        Ergo.smart_override(o, this.defaults, opts);

    },
*/

    /**
     * ассоциация компоновки с виджетом
     * @param {Object} c виджет
     */
//     attach: function(c) {
//
//         var o = this.options;
//
// //        this._widget = c;
//
//         if('name' in o) this.el.setAttribute('layout', o.name);
//         if('cls' in o) this.addClass(o.cls.join(' '));
//
//         // this.dom = this._widget.dom;
//         // this.el = this._widget.dom.el;
//
//         // if(o.html){
//         //     var html = $(o.html);
//         //     this.el = (o.htmlSelector) ? $(o.htmlSelector, html) : html;
//         //     this._widget.el.append(html);
//         // }
//
//         // this._widget.events.on('diff', function(e) {
//         //     // перестраиваем компоновку
//         //     this._rebuild(e.updated);
//         // }.bind(this));
//
//     },

    /**
     * удаление ассоциации компоновки с виджетом
     */
//     detach: function() {
// //        if('containerCls' in this.options) this.container.el.removeClass(this.options.containerCls);
//         if('name' in this.options) this.el.removeAttribute('layout');
//         if('cls' in o) this.el.removeClass(o.cls.join(' '));
//         delete this._widget;
//     },



    /**
     * Оборачивание элемента.
     *
     * @param {Ergo.core.Widget} item виджет
     * @return {jQuery} jQuery-объект, содержащий обертку и элемент виджета
     *
     */
    wrap: function(item) {
        return item.dom.el;
    },


    /**
     * jQuery-элемент, куда будут добавляться виджеты
     *
     */
    select: function(item) {
        return this.innerEl;
//        var o = this.options;
//        return (o.elementSelector) ? o.elementSelector.call(this, item) : this.el;
    },






    /**
     * добавление нового элемента-виджета в компоновку
     *
     * @param {Ergo.core.Widget} item виджет
     * @param {int} index порядковый номер (может быть не определен)
     * @param {int} weight вес группы
     */
/*
    add: function(item, pos, weight, group) {

//        var selector = item.options.layoutSelector;

//        var el = this.el;

        var o = this.options;


        //FIXME определяем вес
        // если вес не указан, то вес считается равным 0
        var weight = item.options.weight || 0;
//        item._weight = weight;

        //получаем индекс
//        var index = item._index;


        // выбираем элемент, куда будет добавляться элемент-виджет
        var el = (o.selector) ? o.selector.call(this, item) : this.select(item);

        // создаем обертку (если она необходима) для элемента-виджета
        var item_el = (o.wrapper) ? o.wrapper.call(this, item) : this.wrap(item);



        if(item.options.wrapper) {

            if(item_el == item.__dom.el) {
                item_el = document.createElement('div');
                item_el.appendChild(item.__dom.el);
//                item_el = $('<div/>').append(item.dom.el);
            }

            item._wrapper = $.ergo( Ergo.deepMerge({etype: 'widgets:widget', tag: item_el, autoRender: false}, item.options.wrapper) );
            item._wrapper._weight = weight;

        }


        if(item_el != item.__dom.el) {
            item.__dom.outerEl = item_el;
        }

        // экспериментальный код
        if(item._key && item.options.autoClass) {
            $ergo.dom.addClass(item_el, item._key);
        }
//            item_el.addClass(item._key);



//        item_el.data('weight', weight);



//        item_el._group = item.options.group;


//        var elements = el.contents();
        var elements = el.childNodes;// new Ergo.core.Array(el[0].childNodes);

        // фильтруем список элементов
        // if(item.options.group) {
        //     elements = Array.prototype.filter.call(elements, function(elem) {
        //         return ( elem._ergo && elem._ergo.options.group == item.options.group )
        //     });
        //
        //     // var filtered = [];
        //     // for(var j = 0; j < elements.length; j++) {
        //     //     var elem = elements[j];
        //     //     if( elem._ergo && elem._ergo.options.group == item.options.group ) filtered.push(elem);
        //     // }
        //     // elements = filtered;
        //
        //
        //     // elements = Ergo.filter(elements, function(i, elem){
        //         // return (elem._ergo) ? (elem._ergo.options.group == item.options.group) : false;
        //     // });
        // }


        if(group) {

            var beforeEl = null;
            var afterEl = null;

            for(var i = 0; i < group.length; i++) {
                var g = group[i];
                var groupElements = [];

//                var lastEl = elements[elements.length-1];

                for(var j = 0; j < elements.length; j++) {
                    var siblingEl = elements[j];
                    var siblingGroup = siblingEl._group[i];
                    // та же группа (вес+индекс)
                    if(siblingGroup[0] == g[0] && siblingGroup[1] == g[1]) {
                        groupElements.push(elements[j]);
                    }
                    // меньшая группа (вес+индекс)
                    else if(siblingGroup[0] < g[0] || siblingGroup[1] < g[1]) {
                        beforeEl = siblingEl;
                    }
                    // большая группа (вес+индекс)
                    else if(siblingGroup[0] > g[0] && siblingGroup[1] > g[1]) {
                        afterEl = siblingEl;
                    }
                }

                elements = groupElements;
            }
        }






        // если индекс не определен, то элемент должен быть добавлен последним в свою группу
        if(pos == null) {

            pos = 0;

            // обходим все элементы контейнера в поисках первого с большим весом
            var after_el = null;

            // немножко эвристики
            var last = elements[elements.length-1];

//            index = 0;

            if(elements.length == 0) {
                el.appendChild( item_el );
            }
            else if(last._weight <= weight) {
                $ergo.dom.insertAfter(item_el, last);
                pos = last._pos+1;
//                last.after(item_el);
            }
            // else if(last[0]._weight == weight) {
            //     last.after(item_el);
            //     index = last[0]._index+1;
            // }
            else {

                for(var j = 0; j < elements.length; j++) {
                    var elem = elements[j];
                    var w = elem._weight;// $(elem).data('weight');
                    if(w > weight) {
                        after_el = elem;
                        break;
                    }
                    else if(w == weight) {
                        pos = elem._pos+1;
                    }
                    // else if(w == weight) {
                    //     index = elem._index+1;
                    // }
                }
                // elements.each(function(i, elem){
                    // var w = elem._weight;// $(elem).data('weight');
                    // if(w > weight) {
                        // after_el = $(elem);
                        // return false;
                    // }
                // });


                if(after_el)
                    $ergo.dom.insertBefore(item_el, after_el);
//                    after_el.before( item_el );
                else if(elements.length)
                    $ergo.dom.insertAfter(item_el, last);
//                    last.after( item_el );
                else
                    el.appendChild( item_el );
//                    el.append( item_el );
            }




//            item_el[0]._index = index;

            // var after_el = null;
            // el.children().each(function(i, elem){
                // var w = $(elem).ergo();
                // if(w && w._weight > weight) {
                    // after_el = $(elem);
                    // return false;
                // }
            // });
//
            // if(after_el)
                // after_el.before( item_el );
            // else
                // el.append( item_el );
        }
        // else if(index === 0) {
            // var before_a = [];
            // for(var i = elements.length-1; i >= 0; i--) {
                // var w = $(elements[i]).ergo();
                // if(w && w._weight < weight) before_a.push($(elements[i]));
            // }
//
            // if(before_a.length > 0)
                // before_a[before_a.length-1].after( item_el );
            // else if(elements.length)
                // elements.first().before( item_el );
            // else
                // el.prepend( item_el );
        // }
        else {

//            console.log(index);

//            var elements = el[0].childNodes;// new Ergo.core.Array(el[0].childNodes);


            // немножко эвристики
            var last = elements[elements.length-1];

            // if(elements.length > 0)
                // console.log('last ', last[0]);
//            console.log('--1--');
//            console.log(elements.length);
//            console.log(elements[elements.length-1]);
//            if(elements.length != 0)
//            console.log(last[0], last[0]._weight, last[0]._index);
//            console.log('--2--');

//            console.log(index);

            if(elements.length == 0) {
                el.appendChild( item_el );
//                el.append( item_el );
//                console.log('---1---');
            }
            else if(last._weight == weight && last._pos < pos){
                $ergo.dom.insertAfter(item_el, last);
//                last.after(item_el);
//                console.log('---2---');
            }
            else {

//                console.log('---3---', index, last[0]._index);

//                console.log(index, 'layout');

                var arr = [];
                var before_el = null;
                var after_el = null;
    //            this.container.children.each(function(it){
                for(var k = 0; k < elements.length; k++) {
//                elements.each(function(k, child){
                    var child = elements[k];

                    // it = $(child).ergo();
                    // if(!it || it == item) return; //если элемент еще не отрисован, это вызовет ошибку
                    // if(it._weight == weight) arr.push(it);//.el);
                    // else if(it._weight <= weight) before_el = it.el;
                    // else if(!after_el ) after_el = it.el;

                    if(child._weight == weight) arr.push( child );//.el);
                    else if(child._weight <= weight) before_el = child;

                    else if(!after_el ) after_el = child;
                }//);




                // for(var i = 0; i < arr.length; i++) {
                //     if( arr[i]._index >= index  ) {
                //         if(!after_el) after_el = arr[i].el;
                //     }
                //     else {
                //         before_el = arr[i].el;
                //     }
                // }


                for(var i = 0; i < arr.length; i++) {
                    if( arr[i]._pos >= pos  ) {
                        if(!after_el) after_el = arr[i];
                    }
                    else {
                        before_el = arr[i];
                    }
                }



                if(before_el) {
                    $ergo.dom.insertAfter( item_el, before_el );
                }
//                    before_el.after( item_el );
                else if(after_el)
                    $ergo.dom.insertBefore( item_el, after_el );
//                    after_el.before( item_el );
                else if(elements.length)
                    $ergo.dom.insertAfter( item_el, last );
//                    last.after( item_el );
                else {
                    el.appendChild( item_el );
//                    el.append( item_el ); //FIXME это уже не нужно
                }



                // }
                // else {
                    // arr[index].before(item_el);
                // }
            }



        }
        // else if(index == 0)
            // el.prepend( item.el );
        // else if($.isNumber(index))
            // el.children().eq(index-1).before(item.el);
        // else
            // index.el.before(item.el);

        // VDOM?

        item_el._pos = pos;

        item_el._weight = weight;

        item_el._group = group;


        var sibling = item_el.nextSibling;
        while(sibling && sibling._weight === item_el._weight) {
            sibling._pos++;
            sibling = sibling.nextSibling;
        }



        item._rendered = true;

        this._widget.events.fire('item#rendered', {item: item});

        // deprecated
//        if('itemCls' in this.options) item.el.addClass(this.options.itemCls);
//        if('itemStyle' in this.options) item.el.css(this.options.itemStyle);
    },
*/

    _groupElements: function(group) {

        var beforeEl = null;
        var afterEl = null;

        var elements = this.innerEl.childNodes;


        for(var i = 0; i < group.length; i++) {
            var g = group[i];
            var groupElements = [];

//                var lastEl = elements[elements.length-1];

            for(var j = 0; j < elements.length; j++) {

                if(!elements[j]._group) {
                    continue;
                }

                var siblingEl = elements[j];
                var siblingGroup = siblingEl._group[i];
                // та же группа (вес+индекс)
                if(siblingGroup[0] == g[0] && siblingGroup[1] == g[1]) {
                    groupElements.push(elements[j]);
                }
                // меньшая группа (вес+индекс)
                else if(siblingGroup[0] < g[0] || siblingGroup[1] < g[1]) {
                    beforeEl = siblingEl;
                }
                // большая группа (вес+индекс)
                else if(siblingGroup[0] > g[0] && siblingGroup[1] > g[1]) {
                    afterEl = siblingEl;
                }
            }

            elements = groupElements;
        }

        return elements;
    },



    at: function(pos, weight, group) {

        var w = weight || 0;

        var elements = this.innerEl.childNodes;

        if(group) {
            elements = this._groupElements(group);
        }

        for(var i = 0; i < elements.length; i++) {
            if( elements[i]._pos == pos && elements[i]._weight == w ) {
                return elements[i];
            }
        }

        return null;
    },



    addBefore: function(item, otherItem, w, group) {

        var o = this.options;

        var itemEl = item.dom.outerEl;

        var pos = 0;
        var weight = w || 0;


        // выбираем элемент, куда будет добавляться элемент-виджет
        var targetEl = (o.selector) ? o.selector.call(this, item) : this.select(item);

        // создаем обертку (если она необходима) для элемента-виджета
        var itemEl = (o.wrapper) ? o.wrapper.call(this, item) : this.wrap(item);



        if(item.options.wrapper) {

            if(itemEl == item.__dom.el) {
                itemEl = document.createElement('div');
                itemEl.appendChild(item.__dom.el);
//                item_el = $('<div/>').append(item.dom.el);
            }

            item._wrapper = $.ergo( Ergo.deepMerge({etype: 'widgets:widget', tag: itemEl, autoRender: false}, item.options.wrapper) );
            item._wrapper._weight = weight;

        }


        if(itemEl != item.__dom.el) {
            item.__dom.outerEl = itemEl;
        }

        // экспериментальный код
        if(item._key && item.options.autoClass) {
            $ergo.dom.addClass(item_el, item._key);
        }




        var elements = targetEl.childNodes;


        if(!otherItem && group) {

            elements = this._groupElements(group);

        }

        var lastEl = elements[elements.length-1];
        var firstEl = elements[0];



        // если указан предыдущий элемент
        if(otherItem) {

            var otherEl = otherItem.dom.outerEl;

            $ergo.dom.insertBefore(itemEl, otherEl);

            pos = otherEl._pos;

            // увеличиваем индекс всех последующих элементов того же веса
            var _el = itemEl.nextSibling;
            while(_el && _el._weight == weight) {
                _el._pos++;
                _el = _el.nextSibling;
            }

        }
        // если элементов в DOM вообще нет
        else if(elements.length == 0) {
            targetEl.appendChild(itemEl);
        }
        // если вес элемента меньше минимального веса
        else if(firstEl._weight > weight) {
            $ergo.dom.insertBefore(itemEl, firstEl);
        }
        // добавляем элемент в конец группы
        else {
            // ищем последний элемент меньшего веса или первый элемент большего веса
            var beforeEl = null;
            var afterEl = null;
            for(var i = 0; i < elements.length; i++) {
                var _el = elements[i];
                if(_el._weight != null && _el._weight < weight) {
                    beforeEl = _el;
                }
                else {
                    afterEl = _el;
                    break;
                }
            }

            if(beforeEl) {
                $ergo.dom.insertAfter(itemEl, beforeEl);
                pos = beforeEl._pos;
            }
            else if(afterEl) {
                $ergo.dom.insertBefore(itemEl, afterEl);
            }


        }


        itemEl._pos = pos;
        itemEl._weight = weight;
        itemEl._group = group;

        item._rendered = true;

        this._widget.events.fire('item#rendered', {item: item});


    },




    addAfter: function(item, otherItem, w, group) {

        var o = this.options;

        var itemEl = item.dom.outerEl;

        var pos = 0;
        var weight = w || 0;


        // выбираем элемент, куда будет добавляться элемент-виджет
        var targetEl = (o.selector) ? o.selector.call(this, item) : this.select(item);

        // создаем обертку (если она необходима) для элемента-виджета
        var itemEl = (o.wrapper) ? o.wrapper.call(this, item) : this.wrap(item);



        if(item.options.wrapper) {

            if(itemEl == item.__dom.el) {
                itemEl = document.createElement('div');
                itemEl.appendChild(item.__dom.el);
//                item_el = $('<div/>').append(item.dom.el);
            }

            item._wrapper = $.ergo( Ergo.deepMerge({etype: 'widgets:widget', tag: itemEl, autoRender: false}, item.options.wrapper) );
            item._wrapper._weight = weight;

        }


        if(itemEl != item.__dom.el) {
            item.__dom.outerEl = itemEl;
        }

        // FIXME
        if(otherItem && otherItem.__dom.targetKey != item.__dom.targetKey) {
            otherItem = null;
        }

        // экспериментальный код
        if(item._key && item.options.autoClass) {
            $ergo.dom.addClass(item_el, item._key);
        }




        var elements = targetEl.childNodes;


        if(!otherItem && group) {

            elements = this._groupElements(group);

        }

        var lastEl = elements[elements.length-1];



        // сомнительная оптимизация
        if(otherItem && otherItem.dom.outerEl == lastEl) {
//            console.log('element insert after (fast)');
            targetEl.appendChild(itemEl);
            pos = lastEl._pos+1;
        }
        // если указан предыдущий элемент
        else if(otherItem) {
//            console.log('element insert after');

            var otherEl = otherItem.dom.outerEl;

            $ergo.dom.insertAfter(itemEl, otherEl);

            pos = otherEl._pos+1;

            // увеличиваем индекс всех последующих элементов того же веса
            var _el = itemEl.nextSibling;
            while(_el && _el._weight == weight) {
                _el._pos++;
                _el = _el.nextSibling;
            }

        }
        // если элементов в DOM вообще нет
        else if(elements.length == 0) {
            targetEl.appendChild(itemEl);
        }
        // если вес элемента больше максимального веса
        else if(lastEl._weight <= weight) {
            targetEl.appendChild(itemEl);
            pos = lastEl._pos+1;
        }
        // добавляем элемент в конец группы
        else {
//            console.log('element lookup');

            // ищем последний элемент меньшего веса или первый элемент большего веса
            var beforeEl = null;
            var afterEl = null;
            for(var i = 0; i < elements.length; i++) {
                var _el = elements[i];
                if( (_el._weight || 0) <= weight) {
                    beforeEl = _el;
                }
                else {
                    afterEl = _el;
                    break;
                }
            }

            if(beforeEl) {
                $ergo.dom.insertAfter(itemEl, beforeEl);
                pos = beforeEl._pos;
            }
            else if(afterEl) {
                $ergo.dom.insertBefore(itemEl, afterEl);
            }


        }


        itemEl._pos = pos;
        itemEl._weight = weight;
        itemEl._group = group;

        item._rendered = true;

        this._widget.events.fire('item#rendered', {item: item});
    },





    /**
     * удаление элемента-виджета из компоновки
     * @param {Object} item
     */
    remove: function(item) {

        var item_el = item.dom.outerEl;// (item._wrapper_el || item.dom.el);


        var sibling = item_el.nextSibling;
        while(sibling && sibling._weight === item_el._weight) {
            sibling._pos--;
            sibling = sibling.nextSibling;
        }


        if(item._wrapper_el) {
            item._wrapper_el.remove(); //?

            if(item._wrapper)
                item._wrapper._destroy();
        }
        else if(item.__dom) {
            item.dom.detach(); //TODO опасный момент: все дочерние DOM-элементы не уничтожаются
        }

        item._rendered = false;

        if('itemCls' in this.options) item.dom.el.removeClass(this.options.itemCls);

        this._widget.events.fire('item#unrendered', {item: item});

    },


    /**
     * очистка компоновки от всех элементов (уничтожения дочерних элементов не происходит)
     */
    clear: function() {
        $ergo.dom.clear(this.el);
//        this.el.empty(); //WARN еще опасный момент все дочерние DOM-элементы уничтожаются
    },


    /**
     * обновление компоновки (позиции, размеров элементов)
     */
    update: function() {


        // AUTO WIDTH
        if(this._widget.options.autoWidth){

            var _el = this._widget.el;

            // если элемент не отображается - его не надо выравнивать
            if(!_el.not(':hidden')) return;


            // рассчитываем отступы
            var dw = 0;
            if(_el.is(':button')) dw = _el.outerWidth(true) - _el.outerWidth();
            else dw = _el.outerWidth(true) - _el.width();
            // скрываем элемент
//            _el.hide();
            _el.addClass('hidden');

            // ищем родителя, у которого определена ширина
            var w = 0;
            _el.parents().each(function(i, el){
                if(!w) w = $(el).width();
                if(w) return false;
            });

            if(_el.attr('autowidth') != 'ignore-siblings') {
                // обходим всех видимых соседей и получаем их ширину
                _el.siblings().not(':hidden').each(function(i, sibling){
                    var sibling = $(sibling);
                    if(sibling.attr('autoWidth') != 'ignore')
                        w -= sibling.outerWidth(true);
                });
            }

            // задаем ширину элемента (с учетом отступов), если она не нулевая
            if(w - dw)
                _el.width(w - dw);

            // отображаем элемент
//            _el.show();

            _el.removeClass('hidden');
        }

        // AUTO HEIGHT
        if(this._widget.options.autoHeight) {//} &&  this.container.options.autoHeight != 'ignore'){

            var _el = $(this.el);


            if(!_el.is(":visible")) return;
            if(_el.attr('autoHeight') == 'ignore') return;


            if(_el.attr('autoHeight') == 'fit') {

                var h0 = _el.height();
                var dh = _el.outerHeight() - _el.height();

                _el.hide();

                var h = this._widget.options.height || 0;
                _el.parents().each(function(i, el){
                    if(!h) h = $(el).height();
                    if(h) return false;
                });

                h = Math.floor(h - dh);

                if(h > h0)
                    _el.height(h);

                _el.show();

                return;
            }


            var debug = (this._widget.debug == 'autoheight');

            _el.height(0);


//            this.el.hide();

            var dh = 0;
            var h = 0;
            _el.parents().each(function(i, el){
                el = $(el);
                var w = el.ergo();
                if((w && w.options.height) || el.attr('autoHeight') == 'true' || el.attr('autoHeight') == 'stop' || el.is('body')){
                    h = el.height();
//                    h = el[0].scrollHeight;
                    return false;
                }
                else {
//                    if(dh == 0) dh = el.height();
                    if(el.attr('autoheight') != 'ignore-siblings') {
                        dh += (el.outerHeight(true) - el.height());
                        el.siblings().not('td, :hidden').each(function(i, sibling){
                            sibling = $(sibling);
                            if(sibling.attr('autoHeight') != 'ignore')
                                dh += sibling.outerHeight(true);
                        });
                    }
                }
            });


//            if(Ergo.context.debug) console.log({h: h, dh: dh});

            dh += (_el.outerHeight(true) - _el.height());

//            if(Ergo.context.debug) console.log({h: h, dh: dh});


            var self = this;

            // обходим все соседние элементы
            var h_ratio = 1;

            if(_el.attr('autoHeight') != 'ignore-siblings') {
                _el.siblings().not('td').each(function(i, sibling){
                    sibling = $(sibling);
                    var ah = sibling.attr('autoHeight');
                    // элемент видимый
                    if(!ah) {
                        if(sibling.is(':visible'))
                            dh += sibling.outerHeight(true);
                    }
                    else if(ah != 'ignore-siblings' && ah != 'ignore') {
                        h_ratio++;
                        dh += sibling.outerHeight(true) - sibling.height();
                    }
                });
            }



            // var h_ratio = 1;
//
            // if(this.container.parent) {
//
            // this.container.parent.children.each(function(sibling){
                // if(sibling == self.container) return;
                // var ah = sibling.options.autoHeight;
                // if(!ah) {
                    // dh += sibling.el.outerHeight(true);
                // }
                // else if(ah != 'ignore-siblings') {
                    // h_ratio++;
                    // dh += sibling.el.outerHeight(true) - sibling.el.height();
                // }
            // });
//
            // }



            // if(this.el.attr('autoheight') != 'ignore-siblings') {
                // this.el.siblings().not('td, :hidden').each(function(i, sibling){
                    // sibling = $(sibling);
                    // if(sibling.attr('autoHeight') != 'ignore') {
                        // dh += sibling.outerHeight(true);
                        // if(debug)    console.log({type: 'sibling', el: sibling, h: sibling.outerHeight(true)});
                    // }
                // });
            // }




            if(Ergo.context.debug) console.log({h: h, dh: dh, k: h_ratio});

//            this.el.height((h - dh)/h_ratio);

            if( this.options.autoHeightType == 'min' ) {

                _el.height('');

                _el.css('min-height', (h - dh)/h_ratio);
            }
            else if( this.options.autoHeightType == 'max' ) {

                _el.height('');

                _el.css('max-height', (h - dh)/h_ratio);
            }
            else {
                _el.height((h - dh)/h_ratio);
            }

//            this.el.show();

        }

        // AUTO FIT
        if(this._widget.options.autoFit === true){

            var _el = $(this.el);

            var dw = _el.outerWidth() - _el.width();
            var dh = _el.outerHeight() - _el.height();

            _el.hide();

            var h = this._widget.options.height || 0;
            var w = this._widget.options.width || 0;
            _el.parents().each(function(i, el){
                if(!h) h = $(el).height();
                if(!w) w = $(el).width();
                if(w && h) return false;
            });

            _el.siblings().not(':hidden').each(function(i, el){
                w -= $(el).outerWidth(true);
            });

            _el.width(w - dw);
            _el.height(Math.floor(h - dh));

            _el.show();
        }

    },

    /**
     * обновление компоновки (порядка, количества элементов)
     */
    _rebuild: function(updated) {

//        console.log('REBUILD LAYOUT', updated);

        // if(updated) {
        //     for(var i = 0; i < updated.length; i++) {
        //         var item = updated[i];
        //         item.unrender();
        //         item.render();
        //     }
        // }

    },



    build: function() {

//        var render_list = [];

        // this._widget.children.each(function(item){
//
//
//
        // });


    }



});//, 'layouts:default');


Ergo.alias('layouts:default', Ergo.core.Layout);


// Ergo.$layout = function(o, etype) {
    // return Ergo.object(o, 'layout:'+etype);
// };




/**
 *  Пространство имен для компоновок
 * @namespace
 */
Ergo.layouts = {};


//Ergo.$layouts = Ergo.object;

Ergo.$layouts = function(o, etype) {

    var clazz = Ergo._aliases['layouts:'+etype];

    if(!clazz/*!Ergo.alias(etype)*/) {

        // var i = etype.indexOf(':');
        // if(i > 0) {
        //     etype = etype.substr(i+1);
        // }

        o.unshift({name: etype});

        etype = 'default';
    }

    return Ergo.object('layouts', etype, o);
};