eliace/ergo-js

View on GitHub
js/core/widget-render.js

Summary

Maintainability
D
2 days
Test Coverage



/**
 * @lends Ergo.core.Widget.prototype
 */
Ergo.WidgetRender = {



    /**
     * Подключение DOM к виджету
     *
     * Если опция autoBind = false, то связывание осуществлено не будет.
     *
     * @param {Object|Array|String} data подключаемые данные
     */
    _bindDOM: function() {

        var o = this.options;

        if(o.layout) {
            if(typeof o.layout === 'string') {
                var clazz = $ergo.alias(o.layout) || $ergo.alias('layouts:'+o.layout);
                if(!clazz) {
                    throw new Error('Unknown layout ['+o.layout+']');
                }
                this.__dom = new (clazz)(o.tag, this, null, o.tagNS);
            }
            else if( o.layout.constructor === Object ) {
                var name = o.layout.etype || 'default';
                this.__dom = new ($ergo.alias('layouts:'+name))(o.tag, this, o.layout, o.tagNS);
//                this.__dom = new Ergo.core.Layout(o.tag, this, o.layout);
            }
            else if( o.layout.constructor === Function ) {
                this.__dom = o.layout.call(this, o.tag, null, o.tagNS);
            }
            else {
                console.error('Can not create dom for layout ['+o.layout+']');
            }
//         this.__dom = (o.layoutFactory || this.layoutFactory)(layout);
        }
        else {
         this.__dom = new Ergo.core.Layout(o.tag, this, null, o.tagNS);
        }

//        this.__dom.attach(this);//this.layout.options._widget || this);


        if( o.dynamic ) {
            this.events.on('diff', function(e) {
                // перестраиваем компоновку
                this._rerender(false, {created: e.created, deleted: e.deleted, updated: e.updated});
            }.bind(this));
        }

    },




    /**
     * Отрисовка виджета
     *
     * @example
     *
     * w.render();
     * w.render(true);
     * w.render('body');
     * w.render('#some-id');
     * w.render('.some-class');
     *
     * @param {DOMElement|String} target целевой объект отрисовки
     * @param {true|false} cascade каскадное обновление компоновки
     * @param {Ergo.core.Widget} beforeItem элемент, после которого будет добавлен виджет
     *
     */
    render: function(target, cascade, beforeItem) {

//        console.log('render');

//    var el = this.el;


        // нет дочерних элементов и non-empty не рисуем
        if( (this.options.autoRender === false && target !== true) || (this.options.autoRender == 'non-empty' && !this.__txt && (!this.__c || this.__c.src.length == 0)) ) {
            return;
        }


        // для краткой формы отрисовки
        if(arguments.length == 0 && !this._rendered && this.parent) {
            return this.parent.render(true);
        }


        // есть дочерние элементы и виджет явно не управляетя данными
        if( this.__c) {

            var self = this;
            var o = this.options;
            var filter = o.renderFilter ? o.renderFilter.bind(this) : null;
            var sorter = o.renderSorter ? o.renderSorter.bind(this) : null;
            var pager = o.renderPager ? o.renderPager.bind(this) : null;


            // сначала добавляем все неотрисованные элементы в DOM
            var prev = null;
            this.__c.stream(filter, sorter, pager, function(child, i) {

                // // динамические элемены не рисуем
                // if(item._dynamic && item.data) {
                //     return;
                // }

//                if(!item._rendered && item.options.autoRender !== false && !(item.options.autoRender == 'non-empty' && item.children.src.length == 0 && !item.options.text)) {  // options.text?
                if(!child._rendered) {
                    child.render(null, false, prev);
                }

                if(child._rendered) {
                    prev = child;
                }


                    // if( self.__c.src.length == 1 && item._type != 'item' ) {
                    //     item.el._weight = item._weight;
                //   self.el.appendChild(item.el);
                    //     item._rendered = true;
                    // }
                    // else {
                    //     item._type == 'item' ? self.layout.add(item, item._index) : self.layout.add(item);
                    // }
//                }

//                item.render(false);

        });

            // this.__c.src.forEach(function(item) {
            // });
        }
        // нет дочерних элементов
        else {

        }



        if( this.parent && (target == null || target === true) ) {//} && (target == null || (this.options.autoRender == 'non-empty' && (!this.__c || !this.__c.src.length == 0 || this.options.text))) ) {

            if(!this._rendered && this.options.autoRender !== false && this.options.autoRender !== 'ignore') {
                // if( this.parent.__c.src.length == 1 && this._type != 'item' ) {
                //     this.dom.el._weight = this._weight;
                //     this.parent.dom.el.appendChild(this.dom.el);
                //     this._rendered = true;
                // }
                // else {

                if(beforeItem && beforeItem.options.weight == this.options.weight) {
                    this.parent.dom.addAfter(this, beforeItem, this.options.weight);
                }
                else {
                    this.parent.dom.addAfter(this, undefined, this.options.weight);
                }

                // if(forcedIndex != null) {
                //     this._type == 'item' ? this.parent.dom.add(this, forcedIndex) : this.parent.dom.add(this);
                // }
                // else {
                //     this._type == 'item' ? this.parent.dom.add(this, this._index) : this.parent.dom.add(this);
                // }

//                }
            }

        }
        else if(target === true) {
            this._rendered = true;
        }
        else if(target) {
            if( target.constructor === String ) {
                if(target[0] == '#') {
                    target = document.getElementById(target.substr(1));
                }
                else if(target[0] == '.') {
                    target = document.getElementsByClassName(target.substr(1))[0];
                }
                else {
                    target = document.getElementsByTagName(target)[0];
                }
            }
            if(target) {
                target.appendChild(this.dom.outerEl);
                this._rendered = true;
            }
        }


        // обновляем компоновку
//        if(cascade !== false) {
            this._layoutChanged(cascade);
//        }


        if( this.options.showOnRender || this.options.renderEffects ) {
            this.show();
        }


  },




    /**
     * Удаление виджета из DOM/компоновки
     *
     */
    unrender: function() {

        this._rendered = false;

        var callback = function() {
            if(this.parent) {
                this.parent.dom.remove(this);
            }
            else {
                this.__dom.detach();
            }
        };


        if( this.options.hideOnUnrender || this.options.renderEffects ) {
            this.hide().then(callback.bind(this));
        }
        else {
            callback.call(this);
        }

//        return $.when( /*(this.options.hideOnUnrender || this.options.renderEffects) ? this.hide() :*/ true )
//            .then(function() {
//            }.bind(this));
    },




  /**
     * Отрисовка (рендеринг) виджета, т.е. добавление его в DOM-дерево
     *
     * Если метод вызывается без параметров, а виджет входит в виртуальное дерево
     * виджетов, то он будет добавлен в компоновку родителя
     *
     * Отрисовка выполняется для всех дочерних виджетов
     *
     * После отрисовки вызывается обработчик _layoutChanged
     *
     */


/*
    render: function(target, cascade) {


        var self = this;


        if(target === true)
            this.options.autoRender = true; //?


        // только если проиниализирован .children
        if( this.__c ) {

            for(var i = 0; i < this.children.src.length; i++) {

                var item = this.children.src[i];

//            this.children.each(function(item){
                if(!item._rendered && item.options.autoRender !== false && !(item.options.autoRender == 'non-empty' && item.children.src.length == 0 && !item.options.text)) {

                    if(this.children.src.length == 1 && item._type != 'item') {
                            // если элемент один, то компоновка ему еще не нужна
                        // if(item._type == 'item') {
                        //     item.el[0]._index = item._index;
                        //     self.el.append(item.el);
                        // }
                        // else {
                            item.el[0]._weight = item._weight;
                            self.el.append(item.el);
//                        }

                        item._rendered = true;
                    }
                    else {
                        item._type == 'item' ? self.layout.add(item, item._index) : self.layout.add(item);
                    }

                }

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


            for(var i = 0; i < this.children.src.length; i++) {
                var item = this.children.src[i];
//            this.children.each(function(item){
                if( !(item.options.dynamic && item.data) )  //FIXME
                    item.render(false, false);
//            });
            }

        }


        if( (target !== false || (this.options.autoRender == 'non-empty' && (!this.__c || !this.children.src.length == 0 || this.options.text))) && this.parent) {

            if(!this._rendered && this.options.autoRender !== false) {

//                console.log(this);

                if(this.parent.children.src.length == 1 && this._type != 'item') {
                        // если элемент один, то компоновка ему еще не нужна
                    // if(this._type == 'item') {
                    //     this.el[0]._index = this._index;
                    //     this.parent.el.append(this.el);
                    // }
                    // else {
                        this.el[0]._weight = this._weight;
                        this.parent.el.append(this.el);
//                    }

                    this._rendered = true;
                }
                else {
                    this._type == 'item' ? this.parent.layout.add(this, this._index) : this.parent.layout.add(this);
                }



//                if(this.options.showOnRender) this.show();
//                if(this.options.hideOnRender) this.hide();
            }

        }
        else if(target) {
            $(target).append(this.el);
            this._rendered = true;
//            target.layout.add(this, this._index);
        }





        if(cascade !== false)
            this._layoutChanged();


        return $.when( (this.options.showOnRender || this.options.renderEffects) ? this.show() : true );
    },
*/


    /**
     * Удаление виджета из DOM-дерева
     *
     */


/*
    unrender: function() {

        this._rendered = false;

        return $.when( (this.options.hideOnUnrender || this.options.renderEffects) ? this.hide() : true )
            .then(function() {
                if(this.parent && this.parent.__l) {
                    this.parent.layout.remove(this);
                }
                else {
                    this.el.detach();
                }
            }.bind(this));

    },
*/



    /**
     * Перерисовка виджета
     *
     *
     *
     * @protected
     *
     */
    _rerender: function(cascade, diff) {

        var w = this;
        var o = this.options;

        // если .children не проинициализирован, значит перерисовывать нечего
        if(!this.__c) return;



        // обработка `non-empty`
        if( this._rendered ) {
            if( this.options.autoRender !== true && (this.options.autoRender == 'non-empty' && this.__c.src.length == 0 && !this.__txt) ) {
                this.unrender();
//                w.layout.remove( this );
            }
        }
        else if( this.parent ) {
            if( this.options.autoRender !== false && !(this.options.autoRender == 'non-empty' && this.__c.src.length == 0 && !this.__txt) ) {
                this.render();
//                this._type == 'item' ? this.parent.layout.add(this, this._index /*item._index*/) : this.parent.layout.add(this, undefined, this._weight);
            }
        }


        if(diff) {

            // Частичная перерисовка

            this._renderDiff(diff.created, diff.deleted, diff.updated);


        }
        else {

            var filter = o.renderFilter ? o.renderFilter.bind(this) : null;
            var sorter = o.renderSorter ? o.renderSorter.bind(this) : null;
            var pager = o.renderPager ? o.renderPager.bind(this) : null;

            // Полная перерисовка

//            console.log('full rerender');

            // убираем из DOM-дерева все элементы
            this.__c.each(function(child){
                if(child._rendered)
                    child.unrender();
            });


            // добавляем в DOM-дерево элементы
            var prev = undefined;
            this.__c.stream(filter, sorter, pager, function(child, i) {

                if(!child._rendered && child.options.autoRender !== false) {
                    child.render(null, false, prev);
//                    this.dom.addAfter(child, prev, child.options.weight);
//                    child._type == 'item' ? this.dom.add(child, i) : this.dom.add(child);
//                    item.render();
                }

                if(child._rendered) {
                    prev = child;
                }

                // if(!item._rendered && item.options.autoRender !== false && !(item.options.autoRender == 'non-empty' && item.__c.src.length == 0 && !item.options.text)) {
                //
                //     item._type == 'item' ? w.layout.add(item, i /*item._index*/) : w.layout.add(item, undefined, i);
                //
                // }
            });

        }


        // обновляем компоновку
//        if(cascade !== false) {
            this._layoutChanged(cascade);
//        }

    },





  /**
     * Обработчик обновления компоновки (dom)
     *
     * @protected
     */
    _layoutChanged: function(cascade) {

//        if(this.options.autoHeight || this.options.autoWidth || this.options.autoFit) {
//            console.log(this.el);
        this.dom.update();

        if(this.options.rendering) {
            this.options.rendering.call(this);
        }
//        }
        //FIXME возможно следует поменять эту строку на fire('layoutChanged')
//        if(this.layout.options.updateMode == 'auto') this.layout.update();

        if(cascade !== false && this.__c && !(this.data && this.options.dynamic)) {
            this.__c.applyAll('_layoutChanged');
        }

//        this.events.fire('layoutChanged');
    },




    /**
     * Обработчик обновления компоновки на основе diff
     *
     * @protected
     */
    _renderDiff: function(created, deleted, updated) {

//        console.log('render diff', arguments);

        var o = this.options;

        var filter = o.renderFilter ? o.renderFilter.bind(this) : null;
        var sorter = o.renderSorter ? o.renderSorter.bind(this) : null;
        var pager = o.renderPager ? o.renderPager.bind(this) : null;

        var self = this;

        var dom = this.dom;




        if(created) {
            for(var i = 0; i < created.length; i++) {
                var item = created[i];
                var index = item._index;

                if(filter) {
                    if( !filter(item, item._index) ) {
                        // если элемент не прошел фильтр, то не будем его добавлять в dom
                        continue;
                    }

                }


                item.render();  //FIXME куда рендерим?

//                console.log('create', item.text, item._index, item.dom.outerEl._pos);

            }
        }




        var kv_a = [];
//        var prev = undefined;
        this.items.each(function(item, i) {
            // добавляем только отфильтрованные отрисованные элементы
            if(item._rendered && (!filter || filter(item, item._index))) {
                kv_a.push( [item._index, item, item.dom.el._pos, item.dom] );
//                prev = item;
            }
        });


        if(sorter) {
            // Sorting KV-array
            kv_a.sort(sorter);
        }


        // console.log('disorder', kv_a);
        // var texts = [];
        // for(var i = 0; i < this.dom.innerEl.childNodes.length; i++) {
        //     texts.push(this.dom.innerEl.childNodes[i].textContent);
        // }
        // console.log('text', texts);


        $ergo.fixDisorder(kv_a, function(i, j, kv_i, kv_j) {

            var item_i = this.dom.at(i)._dom._widget;
            var item_j = this.dom.at(j)._dom._widget;

//            var _item = this.items.get(i);

//            console.log('move', i, j, item_i, item_j);//kv_i[1].text, kv_j[1].text, i, j, kv_i[1].dom.outerEl._pos, kv_j[1].dom.outerEl._pos);

            //TODO нужно оптимизировать с помощью функции items.move()
            dom.remove(item_i);
//            dom.add(_item, j);
            if(i < j) {
                dom.addAfter(item_i, item_j, item_i.options.weight);
            }
            else {
                dom.addBefore(item_i, item_j, item_i.options.weight);
            }

        }.bind(this));





/*
        if(deleted) {

        }



        if(created) {
            for(var i = 0; i < created.length; i++) {
                var item = created[i];
                var index = item._index;

                if(filter) {
                    if( !filter(item, item._index) ) {
                        // если элемент не прошел фильтр, то не будем его добавлять в dom
                        continue;
                    }

                }

                // // при наличии сортировки индекс виджета не важен
                // if(sorter) {
                //     index = null;
                // }

//                dom.render(item);
                item.render();  //FIXME куда рендерим?

            }
        }


        if(updated) {

            for(var i = 0; i < updated.length; i++) {
                var item = updated[i];

                if(filter) {
                    if( !filter(item, item._index) ) {
                        // если элемент не прошел фильтр и отрисован, то убираем его из VDOM
                        if(item._rendered) {
                            dom.remove(item);
                        }
                        continue;
                    }
                }


                // если элемент не отрисован рисуем его в позицию item._index
                if(!item._rendered) {
                    dom.add(item, item._index);
                }
                // если есть sorter, то обновлять отрисованный элемент нет смысла
                else if(!sorter){
                    // MOVE
                    dom.remove(item);
                    dom.add(item, item._index);
                }

            }

        }



        if(sorter) {

            var kv_a = [];
            this.items.each(function(item, i) {
                // добавляем только отрисованные элементы
                if(item._rendered) {
                    kv_a.push( [item._index, item, item.dom.el._pos, item.dom] );
                }
            });


            // Sorting KV-array
            kv_a.sort(sorter);


            $ergo.fixDisorder(kv_a, function(i, j) {

                var _item = this.items.get(i);

                //TODO нужно оптимизировать с помощью функции items.move()
                dom.remove(_item);
                dom.add(_item, j);

            }.bind(this));


        }
*/

    }







};