Project

General

Profile

Download (10.7 KB) Statistics
| Branch: | Revision:
1
dojo.require("dojo.dnd.Source");
2

    
3

    
4
/**
5
 * dojo TreeTable
6
 */
7
var TreeTable = function(config) {
8
    this.config = config;
9
    this.id = config.renderTo + '_TreeTable';
10
    this.nodes = {};
11
    this._nodes = {};
12
    
13
    if (this.config.nodes) {
14
        for (var i in this.config.nodes) {
15
            this.addNode(this.config.nodes[i]);
16
        }
17
    }
18
}
19

    
20
TreeTable.prototype.bodyEl = function() {
21
    return this.tbodyEl;
22
}
23

    
24
TreeTable.prototype.addNode = function(node) {
25
    if (typeof this.nodes[node.pid] == 'undefined') {
26
        this.nodes[node.pid] = [];
27
    }
28
    // if dataObject was received
29
    if (!(node instanceof TreeNode)) {
30
        node = new TreeNode(node, this);
31
    }
32
    this.nodes[node.pid].push(node);
33
    this._nodes[node.id] = node;
34
}
35

    
36
TreeTable.prototype.createSibling = function() {
37
    dojo.query('#' + this.id + ' .node-sibling').orphan();
38
    var el = dojo.doc.createElement("tr");
39
    el.className = 'node-hidden node-sibling';
40
    el.innerHTML = '<td></td><td></td><td></td>';
41
    return el;
42
}
43

    
44
TreeTable.prototype.siblingFix = function() {
45
    var el = this.createSibling();
46
    this.bodyEl().appendChild(el);
47
}
48

    
49

    
50

    
51
TreeTable.prototype.render = function() {
52
    var to = dojo.byId(this.config.renderTo);
53
    
54
    var tableEl = dojo.doc.createElement('table');
55
    tableEl.id = this.id;
56
    tableEl.className = 'dojo-treetable';
57
    if (this.config.width)
58
        tableEl.style.width = this.config.width
59
    
60
    var theadEl = dojo.doc.createElement('thead');
61
    tableEl.appendChild(theadEl);
62
    
63
    var trEl = dojo.doc.createElement('tr');
64
    theadEl.appendChild(trEl);
65
    
66
    for (var i in this.config.cm) {
67
        var thEl = dojo.doc.createElement('th');
68
        thEl.innerHTML = this.config.cm[i].text;
69
        if (this.config.cm[i].width) {
70
            thEl.style.width = this.config.cm[i].width;
71
        }
72
        trEl.appendChild(thEl);
73
    }
74
    var tbodyEl = dojo.doc.createElement('tbody');
75
    tbodyEl.id = this.id + '_tbody';
76
    tableEl.appendChild(tbodyEl);
77
    this.tbodyEl = tbodyEl;
78
    
79
    if (!this.nodes[0])
80
        return;
81
    for (var i in this.nodes[0]) {
82
        node = this.nodes[0][i];
83
        node.render();
84
    }
85
    to.appendChild(tableEl);
86
    // fix for nextSibling
87
    this.siblingFix();
88
}
89

    
90
TreeTable.prototype.node = function(id) {
91
    return this._nodes[id];
92
}
93

    
94
TreeTable.prototype.colorize = function() {
95
    dojo.query('#' + this.id + '_tbody .dojoxGridRowOdd').removeClass('dojoxGridRowOdd');
96
    
97
    var nodes = dojo.query('#' + this.id + '_tbody tr.node-visible');
98
    for (var i = 0, n = nodes.length; i < n; i++) {
99
        if (i % 2) {
100
            dojo.addClass(nodes[i], 'dojoxGridRowOdd');
101
        }
102
    }
103
}
104

    
105
/** 
106
 * TreeNode
107
 */
108
var TreeNode = function(config, tree) {
109
    this.config = config;
110
    this.id = config.id;
111
    this.elId = 'node_' + this.id;
112
    this.pid = config.pid;
113
    this.tree = tree;
114
    this.expanded = false;
115
    this._visibleChilds = [];
116
}
117

    
118
TreeNode.prototype.el = function() {
119
    return this.nodeEl;
120
}
121

    
122
TreeNode.prototype.hasChilds = function() {
123
    return (typeof this.tree.nodes[this.id]) != 'undefined' && this.tree.nodes[this.id].length > 0;
124
}
125

    
126
TreeNode.prototype.lastRenderedChild = function() {
127
    if (this.hasChilds()) {
128
        var nodes = this.tree.nodes[this.id];
129
        var last = nodes[nodes.length - 1];
130
        var all = [last].concat(last.childsAll());
131
        var last;
132
        for (var i in all) {
133
            if (all[i].rendered)
134
                last = all[i];
135
        }
136
        return last;
137
    } else
138
        return false;
139
}
140

    
141
TreeNode.prototype.childs = function() {
142
    return this.hasChilds() ? this.tree.nodes[this.id] : [];
143
}
144

    
145
TreeNode.prototype.childsAll = function() {
146
    var nodes = [];
147
    
148
    var _nodes = this.childs();
149
    for (var i in _nodes) {
150
        var node = _nodes[i];
151
        nodes = nodes.concat(node, node.childsAll());
152
    }
153
    return nodes;
154
}
155

    
156
TreeNode.prototype.hasNode = function(node) {
157
    var nodes = this.childsAll();
158
    for (var i in nodes) {
159
        if (nodes[i].id == node.id)
160
            return true;
161
    }
162
    return false;
163
}
164

    
165
TreeNode.prototype.icon = function() {
166
    return this.iconEl;
167
}
168

    
169
TreeNode.prototype.titleEl = function() {
170
    return dojo.query('#' + this.elId + ' .node-title')[0];
171
}
172

    
173
TreeNode.prototype.visibleChilds = function() {
174
    var nodes = [];
175
    if (!this.hasChilds())
176
        return nodes;
177
    
178
    var _nodes = this.childs();
179
    for (var i in _nodes) {
180
        var node = _nodes[i];
181
        if (node.nodeEl && dojo.hasClass(node.nodeEl, 'node-visible')) {
182
            nodes.push(node);
183
        } else 
184
            continue;
185
        
186
        nodes = nodes.concat(node.visibleChilds());
187
    }
188
    return nodes;
189
}
190

    
191
TreeNode.prototype.lvl = function() {
192
    if (this.pid == 0) {
193
        return 0;
194
    }
195
    
196
    return 1 + this.tree._nodes[this.pid].lvl();
197
}
198

    
199
TreeNode.prototype.updateLvl = function() {
200
    if (this.rendered)
201
        this.dndSourceEl.style.paddingLeft =  (this.lvl() * this.tree.config.indent) + 'px';
202
}
203

    
204
TreeNode.prototype.show = function() {
205
    dojo.removeClass(this.elId, 'node-hidden');
206
    dojo.addClass(this.elId, 'node-visible');
207
}
208

    
209
TreeNode.prototype.hide = function() {
210
    dojo.removeClass(this.elId, 'node-visible');
211
    dojo.addClass(this.elId, 'node-hidden');
212
}
213

    
214
TreeNode.prototype.iconUpdate = function() {
215
    var icon = this.icon();
216
    
217
    dojo.removeClass(icon, 'folder-expanded');
218
    dojo.removeClass(icon, 'folder-collapsed');
219
    dojo.removeClass(icon, 'doc');
220
    
221
    if (this.hasChilds()) {
222
        if (this.expanded)
223
            dojo.addClass(icon, 'folder-expanded');
224
        else
225
            dojo.addClass(icon, 'folder-collapsed');
226
    } else {
227
        dojo.addClass(icon, 'doc');
228
        this.expanded = false;
229
    }
230
}
231

    
232
TreeNode.prototype.expand = function() {
233
    if (!this.hasChilds()) {
234
        return;
235
    }
236
    var _nodes = this.tree.nodes[this.id].concat().reverse();
237
    for (var i in _nodes) {
238
        if (!_nodes[i].rendered) {
239
            _nodes[i].render();
240
        }
241
        _nodes[i].show();
242
    }
243
    this.expanded = true;
244
    
245
    this.iconUpdate();
246
    
247
    for (var i in this._visibleChilds) {
248
        this._visibleChilds[i].show();
249
    }
250
    this.tree.colorize();
251
}
252

    
253
TreeNode.prototype.collapse = function() {
254
    if (!this.hasChilds()) {
255
        return;
256
    }
257
    
258
    this._visibleChilds = this.visibleChilds();
259
    
260
    for (var i in this._visibleChilds) {
261
        this._visibleChilds[i].hide();
262
    }
263
    this.expanded = false;
264
    
265
    var icon = this.icon();
266
    dojo.addClass(icon, 'folder-collapsed');
267
    dojo.removeClass(icon, 'folder-expanded');
268
    
269
    this.tree.colorize();
270
}
271

    
272
TreeNode.prototype.toggle = function() {
273
    if (this.expanded)
274
        this.collapse();
275
    else
276
        this.expand();
277
}
278

    
279
TreeNode.prototype.onDrop = function(src, nodes, copy) {
280
    var toNode = this.ssnode;
281
    var srcNode = src.ssnode;
282
    srcNode.move(toNode);
283
    toNode.tree.colorize();
284
}
285

    
286
TreeNode.prototype.render = function() {
287
    var el = dojo.doc.createElement("tr");
288
    this.nodeEl = el;
289
    
290
    if (this.pid != 0) {
291
        dojo.addClass(el, 'node-hidden');
292
    } else {
293
        dojo.addClass(el, 'node-visible');
294
    }
295
    
296
    el.id = this.elId;
297
    
298
    // checkbox
299
    var cbEl = dojo.doc.createElement('td');
300
    cbEl.innerHTML = '<input type="checkbox">';
301
    el.appendChild(cbEl);
302
    
303
    // title
304
    this.type = this.hasChilds() ? 'folder' : 'doc';
305
    var cls = this.hasChilds() ? 'folder-collapsed' : 'doc';
306
    
307
    var titleEl = dojo.doc.createElement('td');
308
    titleEl.style.paddingLeft = (this.lvl() * this.tree.config.indent) + 'px';
309
    titleEl.style.paddingRight = '30px';
310
    titleEl.id = 'dnd_source_' + this.id;
311
    
312
    var aEl = dojo.doc.createElement('a');
313
    dojo.addClass(aEl, 'node-icon');
314
    dojo.addClass(aEl, cls);
315
    titleEl.appendChild(aEl);
316
    this.iconEl = aEl;
317
    
318
    var spanEl = dojo.doc.createElement('span');
319
    spanEl.innerHTML = this.config.title;
320
    spanEl.className = 'node-title';
321
    titleEl.appendChild(spanEl);
322
    
323
    el.appendChild(titleEl);
324
    // title end
325
    
326
    for (var i = 2, n = this.tree.config.cm.length; i < n; i++) {
327
        var cm = this.tree.config.cm[i];
328
        var oEl = dojo.doc.createElement('td');
329
        if (cm.renderer) {
330
            oEl.innerHTML = cm.renderer(this);
331
        } else {
332
            oEl.innerHTML = this.config[cm.data];
333
        }
334
        el.appendChild(oEl);
335
    }
336
    
337
    // insert element
338
    if (this.pid == 0)
339
        this.tree.tbodyEl.appendChild(el);
340
    else {
341
        var node = this.tree._nodes[this.pid].nodeEl;
342
        node.parentNode.insertBefore(el, node.nextSibling);
343
    }
344
    
345
    // add events
346
    dojo.connect(this.iconEl, 'onclick', this, 'toggle');
347
    
348
    // render all childs nodes
349
    /*if (this.hasChilds()) {
350
        var _nodes = this.tree.nodes[this.id].concat().reverse();
351
        for (var i in _nodes) {
352
            _nodes[i].render();
353
        }
354
    }*/
355
    
356
    // d'n'd
357
    this.dndSourceEl = titleEl;
358
    this.dndSource = new dojo.dnd.Source(this.dndSourceEl);
359
    this.dndSource.ssnode = this;
360
    this.dndSource.insertNodes(false, [spanEl]);
361
    this.dndSource.onDrop = this.onDrop;
362
    
363
    this.rendered = true;
364
}
365

    
366
TreeNode.prototype.move = function(toNode) {
367
    // update connection in the tree
368
    if (this.hasNode(toNode) || toNode.id == this.id) {
369
        return false;
370
    }
371
    if (!toNode.expanded) {
372
        toNode.expand();
373
    }
374
    
375
    var fromNode = this.tree._nodes[this.pid];
376
    
377
    /* Search afterEl */
378
    var afterEl, afterNode, lastNode;
379
    if (lastNode = toNode.lastRenderedChild()) {
380
        afterNode = lastNode;
381
    } else {
382
        afterNode = toNode;
383
    }
384
    
385
    if (toNode.hasNode(afterNode)) {
386
        afterEl = afterNode.el().nextSibling;
387
    } else
388
        afterEl = afterNode.el();
389
    
390
    /*
391
     * last element fix
392
     * insert sibling node & using it as afterEl
393
     * it will be removed at the end
394
     */
395
    var sibling = this.tree.createSibling();
396
    var afterEl = afterNode.el();
397
    afterEl.parentNode.insertBefore(sibling, afterEl.nextSibling);
398
    afterEl = sibling;
399
    
400
    /* move all */
401
    var nodes = [this].concat(this.childsAll()).reverse();
402
    for (var i in nodes) {
403
        if (!nodes[i].rendered) continue;
404
        afterEl.parentNode.insertBefore(nodes[i].el(), afterEl.nextSibling);
405
        nodes[i].updateLvl();
406
    }
407
    this.tree.siblingFix();
408
    
409
    // update tree joins
410
    var pid = this.pid;
411
    this.pid = toNode.id;
412
    for (var i in this.tree.nodes[pid]) {
413
        if (this.tree.nodes[pid][i].id == this.id) {
414
            this.tree.nodes[pid].splice(i, 1);
415
            break;
416
        }
417
    }
418
    this.tree.addNode(this);
419
    
420
    for (var i in nodes) {
421
        nodes[i].updateLvl();
422
    }
423
    
424
    // fix for empty nodes
425
    toNode.expanded = true;
426
    
427
    toNode.iconUpdate();
428
    if (fromNode)
429
        fromNode.iconUpdate();
430
}
(2-2/4)