-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathbackbone-treeview.js
110 lines (90 loc) · 3.3 KB
/
backbone-treeview.js
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
/* Global settings */
var COLLAPSE_SPEED = 50;
/* Single node object in tree */
window.TreeNodeModel = Backbone.Model.extend({
defaults: {
title: 'Node',
children: [], // Children are represented as ids not objects
},
/* Return a suitable label for the Node
* override this function to better serve the view
*/
getLabel: function() {
return this.get('title');
},
/* Return an array of actual TreeNodeModel instances
* override this function depending on how children are store
*/
getChildren: function() {
return _.map(this.get('children'), function(ref) {
// Lookup by ID in parent collection if string/num
if (typeof(ref) == 'string' || typeof(ref) == 'number')
return this.collection.get(ref);
// Else assume its a real object
return ref;
});
},
});
window.TreeNodeCollection = Backbone.Collection.extend({
model: TreeNodeModel,
});
/* Tree view is attached to a single node (root) and built automatically */
window.TreeView = Backbone.View.extend({
tagName: 'li',
template: '<a class="node-collapse" href="#"><span class="node-label"></span></a><ul class="nav nav-list node-tree"></ul>',
initialize: function() {
// When models children change, rebuild the tree
this.model.bind('change:children', this.render, this);
// Listen to model changes for updating view
this.model.bind('change', this.update, this);
// Collapse state
this.collapsed = true;
},
setupEvents: function() {
// Hack to get around event delegation not supporting ">" selector
var that = this;
this.$('> .node-collapse').click(function() { return that.toggleCollapse(); });
},
toggleCollapse: function() {
this.collapsed = !this.collapsed;
if (this.collapsed)
{
this.$('> .node-collapse i').attr('class', 'icon-plus');
this.$('> .node-tree').slideUp(COLLAPSE_SPEED);
}
else
{
this.$('> .node-collapse i').attr('class', 'icon-minus');
this.$('> .node-tree').slideDown(COLLAPSE_SPEED);
}
},
update: function() {
this.$('> a .node-label').html(this.model.getLabel());
this.collapsed && this.$('> .node-tree').hide() || this.$('> .node-tree').show();
},
render: function() {
// Load HTML template and setup events
this.$el.html(this.template);
this.setupEvents();
// Render this node
this.update();
// Build child views, insert and render each
var tree = this.$('> .node-tree'), childView = null;
_.each(this.model.getChildren(), function(model) {
childView = new TreeView({
model: model,
});
tree.append(childView.$el);
childView.render();
});
/* Apply some extra styling to views with children */
if (childView)
{
// Add bootstrap plus/minus icon
this.$('> .node-collapse').prepend($('<i class="icon-plus"/>'));
// Fixup css on last item to improve look of tree
childView.$el.addClass('last-item').before($('<li/>').addClass('dummy-item'));
}
return this;
},
});