RenderStack

class webix.RenderStack()

Renderstack mixin

Referenced by

views
proto().

External references

Official documentation page.

Code

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
webix.RenderStack={
    $init:function(){
        webix.assert(this.data,"RenderStack :: Component doesn't have DataStore");
        webix.assert(webix.template,"webix.template :: webix.template is not accessible");

        //used for temporary HTML elements
        //automatically nulified during destruction
        this._html = document.createElement("DIV");

        this.data.attachEvent("onIdChange", webix.bind(this._render_change_id, this));
        this.attachEvent("onItemClick", this._call_onclick);

        //create copy of default type, and set it as active one
        if (!this.types){
            this.types = { "default" : this.type };
            this.type.name = "default";
        }

        this.type = webix.clone(this.type);
    },

    customize:function(obj){
        webix.type(this,obj);
    },
    item_setter:function(value){
        return this.type_setter(value);
    },
    type_setter:function(value){
        if(!this.types[value])
            this.customize(value);
        else {
            this.type = webix.clone(this.types[value]);
            if (this.type.css)
                this._contentobj.className+=" "+this.type.css;
        }
        if (this.type.on_click)
            webix.extend(this.on_click, this.type.on_click);

        return value;
    },

    template_setter:function(value){
        this.type.template=webix.template(value);
    },
    //convert single item to HTML text (templating)
    _toHTML:function(obj){
            var mark = this.data._marks[obj.id];
            //check if related template exist
            webix.assert((!obj.$template || this.type["template"+obj.$template]),"RenderStack :: Unknown template: "+obj.$template);
            this.callEvent("onItemRender",[obj]);
            return this.type.templateStart(obj,this.type, mark)+(obj.$template?this.type["template"+obj.$template]:this.type.template)(obj,this.type,mark)+this.type.templateEnd(obj, this.type,mark);
    },
    //convert item to HTML object (templating)
    _toHTMLObject:function(obj){
        this._html.innerHTML = this._toHTML(obj);
        return this._html.firstChild;
    },
    _render_change_id:function(old, newid){
        var obj = this.getItemNode(old);
        if (obj) {
            obj.setAttribute(this._id, newid);
            this._htmlmap[newid] = this._htmlmap[old];
            delete this._htmlmap[old];
        }
    },
    //calls function that is set in onclick property
    _call_onclick:function(){
        if (this._settings.click){
            var code = webix.toFunctor(this._settings.click, this.$scope);
            if (code && code.call) code.apply(this,arguments);
        }
    },
    //return html container by its ID
    //can return undefined if container doesn't exists
    getItemNode:function(search_id){
        if (this._htmlmap)
            return this._htmlmap[search_id];

        //fill map if it doesn't created yet
        this._htmlmap={};

        var t = this._dataobj.childNodes;
        for (var i=0; i < t.length; i++){
            var id = t[i].getAttribute(this._id); //get item's
            if (id)
                this._htmlmap[id]=t[i];
        }
        //call locator again, when map is filled
        return this.getItemNode(search_id);
    },
    //return id of item from html event
    locate:function(e){ return webix.html.locate(e,this._id); },
    /*change scrolling state of top level container, so related item will be in visible part*/
    showItem:function(id){

        var html = this.getItemNode(id);
        if (html&&this.scrollTo){
            var txmin = Math.abs(this._contentobj.offsetLeft-html.offsetLeft);
            var txmax = txmin + html.offsetWidth;
            var tymin = Math.abs(this._contentobj.offsetTop-html.offsetTop);
            var tymax = tymin + html.offsetHeight;
            var state = this.getScrollState();

            var x = state.x;
            if (x > txmin || x + this._content_width < txmax )
                x = txmin;
            var y = state.y;
            if (y > tymin || y + this._content_height < tymax )
                y = tymin - 5;

            this.scrollTo(x,y);
            if(this._setItemActive)
                this._setItemActive(id);
        }
    },
    //update view after data update
    //method calls low-level rendering for related items
    //when called without parameters - all view refreshed
    render:function(id,data,type){
        if (!this.isVisible(this._settings.id) || this.$blockRender)
            return;

        if (webix.debug_render)
            webix.log("Render: "+this.name+"@"+this._settings.id+", mode:"+(type||"#")+", item:"+(id||"#"));

        if (id){
            var cont = this.getItemNode(id); //get html element of updated item
            switch(type){
                case "paint":
                case "update":
                    //in case of update - replace existing html with updated one
                    if (!cont) return;
                    var t = this._htmlmap[id] = this._toHTMLObject(data);
                    webix.html.insertBefore(t, cont);
                    webix.html.remove(cont);
                    break;
                case "delete":
                    //in case of delete - remove related html
                    if (!cont) return;
                    webix.html.remove(cont);
                    delete this._htmlmap[id];
                    break;
                case "add":
                    //in case of add - put new html at necessary position
                    var t = this._htmlmap[id] = this._toHTMLObject(data);
                    webix.html.insertBefore(t, this.getItemNode(this.data.getNextId(id)), this._dataobj);
                    break;
                case "move":
                    //moving without repainting the item
                    webix.html.insertBefore(this.getItemNode(id), this.getItemNode(this.data.getNextId(id)), this._dataobj);
                    break;
                default:
                    webix.assert_error("Unknown render command: "+type);
                    break;
            }
        } else {
            //full reset
            if (this.callEvent("onBeforeRender",[this.data])){
                /*if (this.getScrollState)
                    var scroll = this.getScrollState();*/

                //getRange - returns all elements
                (this._renderobj||this._dataobj).innerHTML = this.data.getRange().map(this._toHTML,this).join("");
                this._htmlmap = null; //clear map, it will be filled at first getItemNode
                this.callEvent("onAfterRender",[]);
                var t = this._dataobj.offsetHeight;

                /*if (this.getScrollState)
                    this.scrollTo(scroll.x, scroll.y);*/
            }
        }
    }
};