flexite 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +12 -0
- data/Rakefile +40 -0
- data/app/assets/fonts/glyphicons-halflings-regular.eot +0 -0
- data/app/assets/fonts/glyphicons-halflings-regular.svg +288 -0
- data/app/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/app/assets/fonts/glyphicons-halflings-regular.woff +0 -0
- data/app/assets/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/app/assets/images/flexite/glyphicons-halflings-white.png +0 -0
- data/app/assets/images/flexite/glyphicons-halflings.png +0 -0
- data/app/assets/javascripts/flexite/application.js +18 -0
- data/app/assets/javascripts/flexite/bootstrap.min.js +7 -0
- data/app/assets/javascripts/flexite/common.js.erb +2 -0
- data/app/assets/javascripts/flexite/jquery.min.js +2 -0
- data/app/assets/javascripts/flexite/tree.js.erb +83 -0
- data/app/assets/javascripts/flexite/treeview.js +1335 -0
- data/app/assets/stylesheets/flexite/application.css +29 -0
- data/app/assets/stylesheets/flexite/bootstrap-treeview.min.css +1 -0
- data/app/assets/stylesheets/flexite/bootstrap.min.css +5 -0
- data/app/assets/stylesheets/flexite/configs.css +8 -0
- data/app/assets/stylesheets/flexite/entries.css +4 -0
- data/app/assets/stylesheets/flexite/tree.css +21 -0
- data/app/assets/stylesheets/scaffold.css +56 -0
- data/app/controllers/flexite/application_controller.rb +25 -0
- data/app/controllers/flexite/configs_controller.rb +61 -0
- data/app/controllers/flexite/entries_controller.rb +82 -0
- data/app/factories/flexite/base_factory.rb +7 -0
- data/app/factories/flexite/service_factory.rb +19 -0
- data/app/forms/flexite/base_form.rb +24 -0
- data/app/forms/flexite/config/form.rb +12 -0
- data/app/forms/flexite/entry/array_form.rb +11 -0
- data/app/forms/flexite/entry/form.rb +16 -0
- data/app/helpers/flexite/application_helper.rb +15 -0
- data/app/helpers/flexite/configs_helper.rb +4 -0
- data/app/helpers/flexite/entries_helper.rb +46 -0
- data/app/models/flexite/arr_entry.rb +35 -0
- data/app/models/flexite/bool_entry.rb +19 -0
- data/app/models/flexite/config.rb +64 -0
- data/app/models/flexite/entry.rb +30 -0
- data/app/models/flexite/int_entry.rb +13 -0
- data/app/models/flexite/str_entry.rb +2 -0
- data/app/models/flexite/sym_entry.rb +17 -0
- data/app/services/flexite/action_service/result.rb +44 -0
- data/app/services/flexite/action_service.rb +30 -0
- data/app/services/flexite/config/create_service.rb +25 -0
- data/app/services/flexite/config/update_service.rb +28 -0
- data/app/services/flexite/data/hash.rb +17 -0
- data/app/services/flexite/data/migrators/yaml.rb +62 -0
- data/app/services/flexite/data/new.rb +87 -0
- data/app/services/flexite/entry/array_create_service.rb +69 -0
- data/app/services/flexite/entry/array_update_service.rb +69 -0
- data/app/services/flexite/entry/create_service.rb +31 -0
- data/app/services/flexite/entry/destroy_array_entry_service.rb +25 -0
- data/app/services/flexite/entry/destroy_service.rb +22 -0
- data/app/services/flexite/entry/update_service.rb +26 -0
- data/app/simple_form/array_input.rb +13 -0
- data/app/views/flexite/application/index.html.haml +14 -0
- data/app/views/flexite/configs/_form.html.haml +7 -0
- data/app/views/flexite/configs/create.js.haml +3 -0
- data/app/views/flexite/configs/edit.js.haml +1 -0
- data/app/views/flexite/configs/index.json.erb +10 -0
- data/app/views/flexite/configs/new.js.haml +1 -0
- data/app/views/flexite/configs/update.js.haml +2 -0
- data/app/views/flexite/entries/_form.html.haml +11 -0
- data/app/views/flexite/entries/_new_array_entry_form.html.haml +7 -0
- data/app/views/flexite/entries/_popup.html.haml +18 -0
- data/app/views/flexite/entries/_types_dropdown.html.haml +8 -0
- data/app/views/flexite/entries/create.js.haml +4 -0
- data/app/views/flexite/entries/destroy.js.haml +4 -0
- data/app/views/flexite/entries/destroy_array_entry.js.haml +6 -0
- data/app/views/flexite/entries/edit.js.haml +1 -0
- data/app/views/flexite/entries/new.js.haml +1 -0
- data/app/views/flexite/entries/new_array_entry.js.haml +1 -0
- data/app/views/flexite/entries/select_type.js.haml +1 -0
- data/app/views/flexite/entries/types/_arr_entry.html.haml +19 -0
- data/app/views/flexite/entries/types/_bool_entry.html.haml +4 -0
- data/app/views/flexite/entries/types/_int_entry.html.haml +4 -0
- data/app/views/flexite/entries/types/_str_entry.html.haml +4 -0
- data/app/views/flexite/entries/types/_sym_entry.html.haml +1 -0
- data/app/views/flexite/entries/update.js.haml +1 -0
- data/app/views/flexite/shared/_messages.html.haml +5 -0
- data/app/views/flexite/shared/_show_flash.js.haml +5 -0
- data/app/views/layouts/flexite/application.html.haml +11 -0
- data/config/initializers/simple_form.rb +143 -0
- data/config/initializers/simple_form_bootstrap.rb +43 -0
- data/config/locales/simple_form.en.yml +26 -0
- data/config/routes.rb +17 -0
- data/db/migrate/20180503102555_create_flexite_configs.rb +14 -0
- data/db/migrate/20180503103109_create_flexite_entries.rb +14 -0
- data/lib/flexite/cached_node.rb +19 -0
- data/lib/flexite/configuration.rb +24 -0
- data/lib/flexite/engine.rb +18 -0
- data/lib/flexite/flexy.rb +13 -0
- data/lib/flexite/nodes_hash.rb +5 -0
- data/lib/flexite/version.rb +3 -0
- data/lib/flexite.rb +44 -0
- data/lib/tasks/flexite_tasks.rake +20 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/application.yml +599 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +40 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/flexite.rb +5 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +3 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/schema.rb +39 -0
- data/test/dummy/db/seeds.rb +3 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +86588 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/tmp/cache/assets/C67/060/sprockets%2Ffaf176441f0544dd2b51901280044b40 +0 -0
- data/test/dummy/tmp/cache/assets/C9D/530/sprockets%2Fdcd49c063327c12052812f41c20a8e74 +0 -0
- data/test/dummy/tmp/cache/assets/CA3/270/sprockets%2F502b740063f8ec15e7e12811da71c772 +0 -0
- data/test/dummy/tmp/cache/assets/CDC/060/sprockets%2F8ff1d307d1b36810549d0829722b7aea +0 -0
- data/test/dummy/tmp/cache/assets/CDE/120/sprockets%2F5fd8b3fa3724451579552aed373410ce +0 -0
- data/test/dummy/tmp/cache/assets/CF8/980/sprockets%2F4e5077b95460dc34d7c9d7d9880e7b47 +0 -0
- data/test/dummy/tmp/cache/assets/D00/4C0/sprockets%2F603c6d7b825c2f4a6422b3d4af4e6203 +0 -0
- data/test/dummy/tmp/cache/assets/D00/C40/sprockets%2F999847c008fb4ce26fff5607c39d8358 +0 -0
- data/test/dummy/tmp/cache/assets/D09/A30/sprockets%2F2a71e20a2f3544acb51956504cd8d8e9 +0 -0
- data/test/dummy/tmp/cache/assets/D0D/E60/sprockets%2F90ea9eaa4671ec2d76703bae31972634 +0 -0
- data/test/dummy/tmp/cache/assets/D2A/160/sprockets%2Fbe17d4d0be4b381500e2824bbe273d70 +0 -0
- data/test/dummy/tmp/cache/assets/D2E/700/sprockets%2Fb21f85e0940bbcb3a8914c9cb0b07218 +0 -0
- data/test/dummy/tmp/cache/assets/D39/CD0/sprockets%2F0e8f6981475e49ea9fe14698fa57e4e9 +0 -0
- data/test/dummy/tmp/cache/assets/D40/590/sprockets%2F181dd5673b58f1ec4c89e50028bfad60 +0 -0
- data/test/dummy/tmp/cache/assets/D46/870/sprockets%2Fd880bdf72c5d0009b88fda8521439dc8 +0 -0
- data/test/dummy/tmp/cache/assets/D48/5F0/sprockets%2Fe6a83afb2d92f4692ffb391b5285a518 +0 -0
- data/test/dummy/tmp/cache/assets/D4B/A20/sprockets%2Febde89014596e655c35df9c4a01ee636 +0 -0
- data/test/dummy/tmp/cache/assets/D54/A50/sprockets%2Ff49839d906f69fd92904ebac07a3a38e +0 -0
- data/test/dummy/tmp/cache/assets/D69/B50/sprockets%2F4186e2787004e19ceb0c4afed46cf04b +0 -0
- data/test/dummy/tmp/cache/assets/D6D/C20/sprockets%2F112e96fea93f5f1e31d2077f1cdaf283 +0 -0
- data/test/dummy/tmp/cache/assets/D7A/8F0/sprockets%2F8eefc5f9824d950317a5c4a2e68b3c4e +0 -0
- data/test/dummy/tmp/cache/assets/DB3/360/sprockets%2F24b1f98ad736884b91d6ebeb2fc68ed8 +0 -0
- data/test/dummy/tmp/cache/assets/DD2/E90/sprockets%2Ff01f48c96ba588d15b20dd57ed3ccb7f +0 -0
- data/test/dummy/tmp/cache/assets/DEE/0C0/sprockets%2Feedcd74856542eedb5ea3ab1b1502ca4 +0 -0
- data/test/dummy/tmp/cache/assets/DF1/210/sprockets%2Ff4fe7dba1445ba1b668aaa1bd34ca984 +0 -0
- data/test/dummy/tmp/cache/assets/E44/790/sprockets%2Fefb8d8ea9e97da9d675a8bfa1d5de343 +0 -0
- data/test/fixtures/flexite/configs.yml +7 -0
- data/test/fixtures/flexite/entries.yml +13 -0
- data/test/flexite_test.rb +7 -0
- data/test/functional/flexite/configs_controller_test.rb +51 -0
- data/test/functional/flexite/entries_controller_test.rb +51 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/test_helper.rb +15 -0
- data/test/unit/flexite/config_test.rb +9 -0
- data/test/unit/flexite/entry_test.rb +9 -0
- data/test/unit/helpers/flexite/configs_helper_test.rb +6 -0
- data/test/unit/helpers/flexite/entries_helper_test.rb +6 -0
- metadata +366 -0
@@ -0,0 +1,1335 @@
|
|
1
|
+
/* =========================================================
|
2
|
+
* bootstrap-treeview.js v1.2.0
|
3
|
+
* =========================================================
|
4
|
+
* Copyright 2013 Jonathan Miles
|
5
|
+
* Project URL : http://www.jondmiles.com/bootstrap-treeview
|
6
|
+
*
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
* you may not use this file except in compliance with the License.
|
9
|
+
* You may obtain a copy of the License at
|
10
|
+
*
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
*
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
* See the License for the specific language governing permissions and
|
17
|
+
* limitations under the License.
|
18
|
+
* ========================================================= */
|
19
|
+
|
20
|
+
;(function ($, window, document, undefined) {
|
21
|
+
|
22
|
+
/*global jQuery, console*/
|
23
|
+
|
24
|
+
'use strict';
|
25
|
+
|
26
|
+
var pluginName = 'treeview';
|
27
|
+
|
28
|
+
var _default = {};
|
29
|
+
|
30
|
+
_default.settings = {
|
31
|
+
|
32
|
+
injectStyle: true,
|
33
|
+
|
34
|
+
levels: 2,
|
35
|
+
|
36
|
+
expandIcon: 'glyphicon glyphicon-plus',
|
37
|
+
collapseIcon: 'glyphicon glyphicon-minus',
|
38
|
+
emptyIcon: 'glyphicon',
|
39
|
+
nodeIcon: '',
|
40
|
+
selectedIcon: '',
|
41
|
+
checkedIcon: 'glyphicon glyphicon-check',
|
42
|
+
uncheckedIcon: 'glyphicon glyphicon-unchecked',
|
43
|
+
|
44
|
+
color: undefined, // '#000000',
|
45
|
+
backColor: undefined, // '#FFFFFF',
|
46
|
+
borderColor: undefined, // '#dddddd',
|
47
|
+
onhoverColor: '#F5F5F5',
|
48
|
+
selectedColor: '#FFFFFF',
|
49
|
+
selectedBackColor: '#428bca',
|
50
|
+
searchResultColor: '#D9534F',
|
51
|
+
searchResultBackColor: undefined, //'#FFFFFF',
|
52
|
+
|
53
|
+
enableLinks: false,
|
54
|
+
highlightSelected: true,
|
55
|
+
highlightSearchResults: true,
|
56
|
+
showBorder: true,
|
57
|
+
showIcon: true,
|
58
|
+
showCheckbox: false,
|
59
|
+
showTags: false,
|
60
|
+
multiSelect: false,
|
61
|
+
|
62
|
+
// Event handlers
|
63
|
+
onNodeChecked: undefined,
|
64
|
+
onNodeCollapsed: undefined,
|
65
|
+
onNodeDisabled: undefined,
|
66
|
+
onNodeEnabled: undefined,
|
67
|
+
onNodeExpanded: undefined,
|
68
|
+
onNodeSelected: undefined,
|
69
|
+
onNodeUnchecked: undefined,
|
70
|
+
onNodeUnselected: undefined,
|
71
|
+
onSearchComplete: undefined,
|
72
|
+
onSearchCleared: undefined
|
73
|
+
};
|
74
|
+
|
75
|
+
_default.options = {
|
76
|
+
silent: false,
|
77
|
+
ignoreChildren: false
|
78
|
+
};
|
79
|
+
|
80
|
+
_default.searchOptions = {
|
81
|
+
ignoreCase: true,
|
82
|
+
exactMatch: false,
|
83
|
+
revealResults: true
|
84
|
+
};
|
85
|
+
|
86
|
+
var Tree = function (element, options) {
|
87
|
+
|
88
|
+
this.$element = $(element);
|
89
|
+
this.elementId = element.id;
|
90
|
+
this.styleId = this.elementId + '-style';
|
91
|
+
|
92
|
+
this.init(options);
|
93
|
+
|
94
|
+
return {
|
95
|
+
|
96
|
+
// Options (public access)
|
97
|
+
options: this.options,
|
98
|
+
|
99
|
+
// Initialize / destroy methods
|
100
|
+
init: $.proxy(this.init, this),
|
101
|
+
remove: $.proxy(this.remove, this),
|
102
|
+
|
103
|
+
// Get methods
|
104
|
+
addNodes: $.proxy(this.addNodes, this),
|
105
|
+
editNode: $.proxy(this.editNode, this),
|
106
|
+
addToParent: $.proxy(this.addToParent, this),
|
107
|
+
removeNodes: $.proxy(this.removeNodes, this),
|
108
|
+
removeNode: $.proxy(this.removeNode, this),
|
109
|
+
getNode: $.proxy(this.getNode, this),
|
110
|
+
getParent: $.proxy(this.getParent, this),
|
111
|
+
getSiblings: $.proxy(this.getSiblings, this),
|
112
|
+
getSelected: $.proxy(this.getSelected, this),
|
113
|
+
getUnselected: $.proxy(this.getUnselected, this),
|
114
|
+
getExpanded: $.proxy(this.getExpanded, this),
|
115
|
+
getCollapsed: $.proxy(this.getCollapsed, this),
|
116
|
+
getChecked: $.proxy(this.getChecked, this),
|
117
|
+
getUnchecked: $.proxy(this.getUnchecked, this),
|
118
|
+
getDisabled: $.proxy(this.getDisabled, this),
|
119
|
+
getEnabled: $.proxy(this.getEnabled, this),
|
120
|
+
|
121
|
+
// Select methods
|
122
|
+
selectNode: $.proxy(this.selectNode, this),
|
123
|
+
unselectNode: $.proxy(this.unselectNode, this),
|
124
|
+
toggleNodeSelected: $.proxy(this.toggleNodeSelected, this),
|
125
|
+
|
126
|
+
// Expand / collapse methods
|
127
|
+
collapseAll: $.proxy(this.collapseAll, this),
|
128
|
+
collapseNode: $.proxy(this.collapseNode, this),
|
129
|
+
expandAll: $.proxy(this.expandAll, this),
|
130
|
+
expandNode: $.proxy(this.expandNode, this),
|
131
|
+
toggleNodeExpanded: $.proxy(this.toggleNodeExpanded, this),
|
132
|
+
revealNode: $.proxy(this.revealNode, this),
|
133
|
+
|
134
|
+
// Expand / collapse methods
|
135
|
+
checkAll: $.proxy(this.checkAll, this),
|
136
|
+
checkNode: $.proxy(this.checkNode, this),
|
137
|
+
uncheckAll: $.proxy(this.uncheckAll, this),
|
138
|
+
uncheckNode: $.proxy(this.uncheckNode, this),
|
139
|
+
toggleNodeChecked: $.proxy(this.toggleNodeChecked, this),
|
140
|
+
|
141
|
+
// Disable / enable methods
|
142
|
+
disableAll: $.proxy(this.disableAll, this),
|
143
|
+
disableNode: $.proxy(this.disableNode, this),
|
144
|
+
enableAll: $.proxy(this.enableAll, this),
|
145
|
+
enableNode: $.proxy(this.enableNode, this),
|
146
|
+
toggleNodeDisabled: $.proxy(this.toggleNodeDisabled, this),
|
147
|
+
|
148
|
+
// Search methods
|
149
|
+
search: $.proxy(this.search, this),
|
150
|
+
clearSearch: $.proxy(this.clearSearch, this)
|
151
|
+
};
|
152
|
+
};
|
153
|
+
|
154
|
+
Tree.prototype.init = function (options) {
|
155
|
+
|
156
|
+
this.tree = [];
|
157
|
+
this.nodes = [];
|
158
|
+
|
159
|
+
if (options.data) {
|
160
|
+
if (typeof options.data === 'string') {
|
161
|
+
options.data = $.parseJSON(options.data);
|
162
|
+
}
|
163
|
+
this.tree = $.extend(true, [], options.data);
|
164
|
+
delete options.data;
|
165
|
+
}
|
166
|
+
this.options = $.extend({}, _default.settings, options);
|
167
|
+
|
168
|
+
this.destroy();
|
169
|
+
this.subscribeEvents();
|
170
|
+
this.setInitialStates({ nodes: this.tree }, 0);
|
171
|
+
this.render();
|
172
|
+
};
|
173
|
+
|
174
|
+
Tree.prototype.addNodes = function (nodes, parentId) {
|
175
|
+
var parent = this.getNode(parentId);
|
176
|
+
|
177
|
+
$.each(nodes, function(index, node) {
|
178
|
+
parent.nodes.push(node);
|
179
|
+
});
|
180
|
+
|
181
|
+
this.nodes = [];
|
182
|
+
this.destroy();
|
183
|
+
this.subscribeEvents();
|
184
|
+
this.setInitialStates({ nodes: this.tree }, 0);
|
185
|
+
this.render();
|
186
|
+
};
|
187
|
+
|
188
|
+
Tree.prototype.removeNodes = function(parentId) {
|
189
|
+
var parent = this.getNode(parentId);
|
190
|
+
|
191
|
+
parent.nodes = [];
|
192
|
+
this.nodes = [];
|
193
|
+
this.destroy();
|
194
|
+
this.subscribeEvents();
|
195
|
+
this.setInitialStates({ nodes: this.tree }, 0);
|
196
|
+
this.render();
|
197
|
+
};
|
198
|
+
|
199
|
+
Tree.prototype.removeNode = function(node) {
|
200
|
+
if (typeof node.parentId !== 'undefined') {
|
201
|
+
var parent = this.getNode(node.parentId);
|
202
|
+
var pIndex = parent.nodes.indexOf(node);
|
203
|
+
parent.nodes.splice(pIndex, 1);
|
204
|
+
|
205
|
+
if (!parent.nodes.length) {
|
206
|
+
parent.nodes = null;
|
207
|
+
parent.state.expanded = false;
|
208
|
+
}
|
209
|
+
} else {
|
210
|
+
var tIndex = this.tree.indexOf(node);
|
211
|
+
this.tree.splice(tIndex, 1);
|
212
|
+
}
|
213
|
+
|
214
|
+
this.nodes = [];
|
215
|
+
this.destroy();
|
216
|
+
this.subscribeEvents();
|
217
|
+
this.setInitialStates({ nodes: this.tree }, 0);
|
218
|
+
this.render();
|
219
|
+
};
|
220
|
+
|
221
|
+
Tree.prototype.remove = function () {
|
222
|
+
this.destroy();
|
223
|
+
$.removeData(this, pluginName);
|
224
|
+
$('#' + this.styleId).remove();
|
225
|
+
};
|
226
|
+
|
227
|
+
Tree.prototype.destroy = function () {
|
228
|
+
|
229
|
+
if (!this.initialized) return;
|
230
|
+
|
231
|
+
this.$wrapper.remove();
|
232
|
+
this.$wrapper = null;
|
233
|
+
|
234
|
+
// Switch off events
|
235
|
+
this.unsubscribeEvents();
|
236
|
+
|
237
|
+
// Reset this.initialized flag
|
238
|
+
this.initialized = false;
|
239
|
+
};
|
240
|
+
|
241
|
+
Tree.prototype.unsubscribeEvents = function () {
|
242
|
+
|
243
|
+
this.$element.off('click');
|
244
|
+
this.$element.off('nodeChecked');
|
245
|
+
this.$element.off('nodeCollapsed');
|
246
|
+
this.$element.off('nodeDisabled');
|
247
|
+
this.$element.off('nodeEnabled');
|
248
|
+
this.$element.off('nodeExpanded');
|
249
|
+
this.$element.off('nodeSelected');
|
250
|
+
this.$element.off('nodeUnchecked');
|
251
|
+
this.$element.off('nodeUnselected');
|
252
|
+
this.$element.off('searchComplete');
|
253
|
+
this.$element.off('searchCleared');
|
254
|
+
};
|
255
|
+
|
256
|
+
Tree.prototype.subscribeEvents = function () {
|
257
|
+
|
258
|
+
this.unsubscribeEvents();
|
259
|
+
|
260
|
+
this.$element.on('click', $.proxy(this.clickHandler, this));
|
261
|
+
|
262
|
+
if (typeof (this.options.onNodeChecked) === 'function') {
|
263
|
+
this.$element.on('nodeChecked', this.options.onNodeChecked);
|
264
|
+
}
|
265
|
+
|
266
|
+
if (typeof (this.options.onNodeCollapsed) === 'function') {
|
267
|
+
this.$element.on('nodeCollapsed', this.options.onNodeCollapsed);
|
268
|
+
}
|
269
|
+
|
270
|
+
if (typeof (this.options.onNodeDisabled) === 'function') {
|
271
|
+
this.$element.on('nodeDisabled', this.options.onNodeDisabled);
|
272
|
+
}
|
273
|
+
|
274
|
+
if (typeof (this.options.onNodeEnabled) === 'function') {
|
275
|
+
this.$element.on('nodeEnabled', this.options.onNodeEnabled);
|
276
|
+
}
|
277
|
+
|
278
|
+
if (typeof (this.options.onNodeExpanded) === 'function') {
|
279
|
+
this.$element.on('nodeExpanded', this.options.onNodeExpanded);
|
280
|
+
}
|
281
|
+
|
282
|
+
if (typeof (this.options.onNodeSelected) === 'function') {
|
283
|
+
this.$element.on('nodeSelected', this.options.onNodeSelected);
|
284
|
+
}
|
285
|
+
|
286
|
+
if (typeof (this.options.onNodeUnchecked) === 'function') {
|
287
|
+
this.$element.on('nodeUnchecked', this.options.onNodeUnchecked);
|
288
|
+
}
|
289
|
+
|
290
|
+
if (typeof (this.options.onNodeUnselected) === 'function') {
|
291
|
+
this.$element.on('nodeUnselected', this.options.onNodeUnselected);
|
292
|
+
}
|
293
|
+
|
294
|
+
if (typeof (this.options.onSearchComplete) === 'function') {
|
295
|
+
this.$element.on('searchComplete', this.options.onSearchComplete);
|
296
|
+
}
|
297
|
+
|
298
|
+
if (typeof (this.options.onSearchCleared) === 'function') {
|
299
|
+
this.$element.on('searchCleared', this.options.onSearchCleared);
|
300
|
+
}
|
301
|
+
};
|
302
|
+
|
303
|
+
/*
|
304
|
+
Recurse the tree structure and ensure all nodes have
|
305
|
+
valid initial states. User defined states will be preserved.
|
306
|
+
For performance we also take this opportunity to
|
307
|
+
index nodes in a flattened structure
|
308
|
+
*/
|
309
|
+
Tree.prototype.setInitialStates = function (node, level) {
|
310
|
+
|
311
|
+
if (!node.nodes) return;
|
312
|
+
level += 1;
|
313
|
+
|
314
|
+
var parent = node;
|
315
|
+
var _this = this;
|
316
|
+
$.each(node.nodes, function checkStates(index, node) {
|
317
|
+
|
318
|
+
// nodeId : unique, incremental identifier
|
319
|
+
node.nodeId = _this.nodes.length;
|
320
|
+
|
321
|
+
// parentId : transversing up the tree
|
322
|
+
node.parentId = parent.nodeId;
|
323
|
+
|
324
|
+
// if not provided set selectable default value
|
325
|
+
if (!node.hasOwnProperty('selectable')) {
|
326
|
+
node.selectable = true;
|
327
|
+
}
|
328
|
+
|
329
|
+
// where provided we should preserve states
|
330
|
+
node.state = node.state || {};
|
331
|
+
|
332
|
+
// set checked state; unless set always false
|
333
|
+
if (!node.state.hasOwnProperty('checked')) {
|
334
|
+
node.state.checked = false;
|
335
|
+
}
|
336
|
+
|
337
|
+
// set enabled state; unless set always false
|
338
|
+
if (!node.state.hasOwnProperty('disabled')) {
|
339
|
+
node.state.disabled = false;
|
340
|
+
}
|
341
|
+
|
342
|
+
// set expanded state; if not provided based on levels
|
343
|
+
if (!node.state.hasOwnProperty('expanded')) {
|
344
|
+
if (!node.state.disabled &&
|
345
|
+
(level < _this.options.levels) &&
|
346
|
+
(node.nodes && node.nodes.length > 0)) {
|
347
|
+
node.state.expanded = true;
|
348
|
+
}
|
349
|
+
else {
|
350
|
+
node.state.expanded = false;
|
351
|
+
}
|
352
|
+
}
|
353
|
+
|
354
|
+
// set selected state; unless set always false
|
355
|
+
if (!node.state.hasOwnProperty('selected')) {
|
356
|
+
node.state.selected = false;
|
357
|
+
}
|
358
|
+
|
359
|
+
// index nodes in a flattened structure for use later
|
360
|
+
_this.nodes.push(node);
|
361
|
+
|
362
|
+
// recurse child nodes and transverse the tree
|
363
|
+
if (node.nodes) {
|
364
|
+
_this.setInitialStates(node, level);
|
365
|
+
}
|
366
|
+
});
|
367
|
+
};
|
368
|
+
|
369
|
+
Tree.prototype.clickHandler = function (event) {
|
370
|
+
|
371
|
+
if (!this.options.enableLinks) event.preventDefault();
|
372
|
+
|
373
|
+
var target = $(event.target);
|
374
|
+
var node = this.findNode(target);
|
375
|
+
if (!node || node.state.disabled) return;
|
376
|
+
|
377
|
+
var classList = target.attr('class') ? target.attr('class').split(' ') : [];
|
378
|
+
if ((classList.indexOf('expand-icon') !== -1)) {
|
379
|
+
|
380
|
+
this.toggleExpandedState(node, _default.options);
|
381
|
+
this.render();
|
382
|
+
}
|
383
|
+
else if ((classList.indexOf('check-icon') !== -1)) {
|
384
|
+
|
385
|
+
this.toggleCheckedState(node, _default.options);
|
386
|
+
this.render();
|
387
|
+
}
|
388
|
+
else {
|
389
|
+
|
390
|
+
if (node.selectable) {
|
391
|
+
this.toggleSelectedState(node, _default.options);
|
392
|
+
} else {
|
393
|
+
this.toggleExpandedState(node, _default.options);
|
394
|
+
}
|
395
|
+
|
396
|
+
this.render();
|
397
|
+
}
|
398
|
+
};
|
399
|
+
|
400
|
+
// Looks up the DOM for the closest parent list item to retrieve the
|
401
|
+
// data attribute nodeid, which is used to lookup the node in the flattened structure.
|
402
|
+
Tree.prototype.findNode = function (target) {
|
403
|
+
|
404
|
+
var nodeId = target.closest('li.list-group-item').attr('data-nodeid');
|
405
|
+
var node = this.nodes[nodeId];
|
406
|
+
|
407
|
+
if (!node) {
|
408
|
+
console.log('Error: node does not exist');
|
409
|
+
}
|
410
|
+
return node;
|
411
|
+
};
|
412
|
+
|
413
|
+
Tree.prototype.toggleExpandedState = function (node, options) {
|
414
|
+
if (!node) return;
|
415
|
+
this.setExpandedState(node, !node.state.expanded, options);
|
416
|
+
};
|
417
|
+
|
418
|
+
Tree.prototype.setExpandedState = function (node, state, options) {
|
419
|
+
|
420
|
+
if (state === node.state.expanded) return;
|
421
|
+
|
422
|
+
if (state && node.nodes) {
|
423
|
+
|
424
|
+
// Expand a node
|
425
|
+
node.state.expanded = true;
|
426
|
+
if (!options.silent) {
|
427
|
+
this.$element.trigger('nodeExpanded', $.extend(true, {}, node));
|
428
|
+
}
|
429
|
+
}
|
430
|
+
else if (!state) {
|
431
|
+
|
432
|
+
// Collapse a node
|
433
|
+
node.state.expanded = false;
|
434
|
+
if (!options.silent) {
|
435
|
+
this.$element.trigger('nodeCollapsed', $.extend(true, {}, node));
|
436
|
+
}
|
437
|
+
|
438
|
+
// Collapse child nodes
|
439
|
+
if (node.nodes && !options.ignoreChildren) {
|
440
|
+
$.each(node.nodes, $.proxy(function (index, node) {
|
441
|
+
this.setExpandedState(node, false, options);
|
442
|
+
}, this));
|
443
|
+
}
|
444
|
+
}
|
445
|
+
};
|
446
|
+
|
447
|
+
Tree.prototype.toggleSelectedState = function (node, options) {
|
448
|
+
if (!node) return;
|
449
|
+
this.setSelectedState(node, !node.state.selected, options);
|
450
|
+
};
|
451
|
+
|
452
|
+
Tree.prototype.setSelectedState = function (node, state, options) {
|
453
|
+
|
454
|
+
if (state === node.state.selected) return;
|
455
|
+
|
456
|
+
if (state) {
|
457
|
+
|
458
|
+
// If multiSelect false, unselect previously selected
|
459
|
+
if (!this.options.multiSelect) {
|
460
|
+
$.each(this.findNodes('true', 'g', 'state.selected'), $.proxy(function (index, node) {
|
461
|
+
this.setSelectedState(node, false, options);
|
462
|
+
}, this));
|
463
|
+
}
|
464
|
+
|
465
|
+
// Continue selecting node
|
466
|
+
node.state.selected = true;
|
467
|
+
if (!options.silent) {
|
468
|
+
this.$element.trigger('nodeSelected', $.extend(true, {}, node));
|
469
|
+
}
|
470
|
+
}
|
471
|
+
else {
|
472
|
+
|
473
|
+
// Unselect node
|
474
|
+
node.state.selected = false;
|
475
|
+
if (!options.silent) {
|
476
|
+
this.$element.trigger('nodeUnselected', $.extend(true, {}, node));
|
477
|
+
}
|
478
|
+
}
|
479
|
+
};
|
480
|
+
|
481
|
+
Tree.prototype.toggleCheckedState = function (node, options) {
|
482
|
+
if (!node) return;
|
483
|
+
this.setCheckedState(node, !node.state.checked, options);
|
484
|
+
};
|
485
|
+
|
486
|
+
Tree.prototype.setCheckedState = function (node, state, options) {
|
487
|
+
|
488
|
+
if (state === node.state.checked) return;
|
489
|
+
|
490
|
+
if (state) {
|
491
|
+
|
492
|
+
// Check node
|
493
|
+
node.state.checked = true;
|
494
|
+
|
495
|
+
if (!options.silent) {
|
496
|
+
this.$element.trigger('nodeChecked', $.extend(true, {}, node));
|
497
|
+
}
|
498
|
+
}
|
499
|
+
else {
|
500
|
+
|
501
|
+
// Uncheck node
|
502
|
+
node.state.checked = false;
|
503
|
+
if (!options.silent) {
|
504
|
+
this.$element.trigger('nodeUnchecked', $.extend(true, {}, node));
|
505
|
+
}
|
506
|
+
}
|
507
|
+
};
|
508
|
+
|
509
|
+
Tree.prototype.setDisabledState = function (node, state, options) {
|
510
|
+
|
511
|
+
if (state === node.state.disabled) return;
|
512
|
+
|
513
|
+
if (state) {
|
514
|
+
|
515
|
+
// Disable node
|
516
|
+
node.state.disabled = true;
|
517
|
+
|
518
|
+
// Disable all other states
|
519
|
+
this.setExpandedState(node, false, options);
|
520
|
+
this.setSelectedState(node, false, options);
|
521
|
+
this.setCheckedState(node, false, options);
|
522
|
+
|
523
|
+
if (!options.silent) {
|
524
|
+
this.$element.trigger('nodeDisabled', $.extend(true, {}, node));
|
525
|
+
}
|
526
|
+
}
|
527
|
+
else {
|
528
|
+
|
529
|
+
// Enabled node
|
530
|
+
node.state.disabled = false;
|
531
|
+
if (!options.silent) {
|
532
|
+
this.$element.trigger('nodeEnabled', $.extend(true, {}, node));
|
533
|
+
}
|
534
|
+
}
|
535
|
+
};
|
536
|
+
|
537
|
+
Tree.prototype.render = function () {
|
538
|
+
|
539
|
+
if (!this.initialized) {
|
540
|
+
|
541
|
+
// Setup first time only components
|
542
|
+
this.$element.addClass(pluginName);
|
543
|
+
this.$wrapper = $(this.template.list);
|
544
|
+
|
545
|
+
this.injectStyle();
|
546
|
+
|
547
|
+
this.initialized = true;
|
548
|
+
}
|
549
|
+
|
550
|
+
this.$element.empty().append(this.$wrapper.empty());
|
551
|
+
|
552
|
+
// Build tree
|
553
|
+
this.buildTree(this.tree, 0);
|
554
|
+
};
|
555
|
+
|
556
|
+
// Starting from the root node, and recursing down the
|
557
|
+
// structure we build the tree one node at a time
|
558
|
+
Tree.prototype.buildTree = function (nodes, level) {
|
559
|
+
|
560
|
+
if (!nodes) return;
|
561
|
+
level += 1;
|
562
|
+
|
563
|
+
var _this = this;
|
564
|
+
$.each(nodes, function addNodes(id, node) {
|
565
|
+
|
566
|
+
var treeItem = $(_this.template.item)
|
567
|
+
.addClass('node-' + _this.elementId)
|
568
|
+
.addClass(node.state.checked ? 'node-checked' : '')
|
569
|
+
.addClass(node.state.disabled ? 'node-disabled': '')
|
570
|
+
.addClass(node.state.selected ? 'node-selected' : '')
|
571
|
+
.addClass(node.searchResult ? 'search-result' : '')
|
572
|
+
.attr('data-nodeid', node.nodeId)
|
573
|
+
.attr('style', _this.buildStyleOverride(node));
|
574
|
+
|
575
|
+
// Add indent/spacer to mimic tree structure
|
576
|
+
for (var i = 0; i < (level - 1); i++) {
|
577
|
+
treeItem.append(_this.template.indent);
|
578
|
+
}
|
579
|
+
|
580
|
+
// Add expand, collapse or empty spacer icons
|
581
|
+
var classList = [];
|
582
|
+
if (node.nodes) {
|
583
|
+
classList.push('expand-icon');
|
584
|
+
if (node.state.expanded) {
|
585
|
+
classList.push(_this.options.collapseIcon);
|
586
|
+
}
|
587
|
+
else {
|
588
|
+
classList.push(_this.options.expandIcon);
|
589
|
+
}
|
590
|
+
}
|
591
|
+
else {
|
592
|
+
classList.push(_this.options.emptyIcon);
|
593
|
+
}
|
594
|
+
|
595
|
+
treeItem
|
596
|
+
.append($(_this.template.icon)
|
597
|
+
.addClass(classList.join(' '))
|
598
|
+
);
|
599
|
+
|
600
|
+
|
601
|
+
// Add node icon
|
602
|
+
if (_this.options.showIcon) {
|
603
|
+
|
604
|
+
var classList = ['node-icon'];
|
605
|
+
|
606
|
+
classList.push(node.icon || _this.options.nodeIcon);
|
607
|
+
if (node.state.selected) {
|
608
|
+
classList.pop();
|
609
|
+
classList.push(node.selectedIcon || _this.options.selectedIcon ||
|
610
|
+
node.icon || _this.options.nodeIcon);
|
611
|
+
}
|
612
|
+
|
613
|
+
treeItem
|
614
|
+
.append($(_this.template.icon)
|
615
|
+
.addClass(classList.join(' '))
|
616
|
+
);
|
617
|
+
}
|
618
|
+
|
619
|
+
// Add check / unchecked icon
|
620
|
+
if (_this.options.showCheckbox) {
|
621
|
+
|
622
|
+
var classList = ['check-icon'];
|
623
|
+
if (node.state.checked) {
|
624
|
+
classList.push(_this.options.checkedIcon);
|
625
|
+
}
|
626
|
+
else {
|
627
|
+
classList.push(_this.options.uncheckedIcon);
|
628
|
+
}
|
629
|
+
|
630
|
+
treeItem
|
631
|
+
.append($(_this.template.icon)
|
632
|
+
.addClass(classList.join(' '))
|
633
|
+
);
|
634
|
+
}
|
635
|
+
|
636
|
+
// Add text
|
637
|
+
if (_this.options.enableLinks) {
|
638
|
+
// Add hyperlink
|
639
|
+
treeItem
|
640
|
+
.append($(_this.template.link)
|
641
|
+
.attr('href', node.href)
|
642
|
+
.append(node.text)
|
643
|
+
);
|
644
|
+
}
|
645
|
+
else {
|
646
|
+
// otherwise just text
|
647
|
+
treeItem
|
648
|
+
.append(node.text);
|
649
|
+
}
|
650
|
+
|
651
|
+
// Add tags as badges
|
652
|
+
if (_this.options.showTags && node.tags) {
|
653
|
+
$.each(node.tags, function addTag(id, tag) {
|
654
|
+
treeItem
|
655
|
+
.append($(_this.template.badge)
|
656
|
+
.append(tag)
|
657
|
+
);
|
658
|
+
});
|
659
|
+
}
|
660
|
+
|
661
|
+
// Add item to the tree
|
662
|
+
_this.$wrapper.append(treeItem);
|
663
|
+
|
664
|
+
// Recursively add child ndoes
|
665
|
+
if (node.nodes && node.state.expanded && !node.state.disabled) {
|
666
|
+
return _this.buildTree(node.nodes, level);
|
667
|
+
}
|
668
|
+
});
|
669
|
+
};
|
670
|
+
|
671
|
+
// Define any node level style override for
|
672
|
+
// 1. selectedNode
|
673
|
+
// 2. node|data assigned color overrides
|
674
|
+
Tree.prototype.buildStyleOverride = function (node) {
|
675
|
+
|
676
|
+
if (node.state.disabled) return '';
|
677
|
+
|
678
|
+
var color = node.color;
|
679
|
+
var backColor = node.backColor;
|
680
|
+
|
681
|
+
if (this.options.highlightSelected && node.state.selected) {
|
682
|
+
if (this.options.selectedColor) {
|
683
|
+
color = this.options.selectedColor;
|
684
|
+
}
|
685
|
+
if (this.options.selectedBackColor) {
|
686
|
+
backColor = this.options.selectedBackColor;
|
687
|
+
}
|
688
|
+
}
|
689
|
+
|
690
|
+
if (this.options.highlightSearchResults && node.searchResult && !node.state.disabled) {
|
691
|
+
if (this.options.searchResultColor) {
|
692
|
+
color = this.options.searchResultColor;
|
693
|
+
}
|
694
|
+
if (this.options.searchResultBackColor) {
|
695
|
+
backColor = this.options.searchResultBackColor;
|
696
|
+
}
|
697
|
+
}
|
698
|
+
|
699
|
+
return 'color:' + color +
|
700
|
+
';background-color:' + backColor + ';';
|
701
|
+
};
|
702
|
+
|
703
|
+
// Add inline style into head
|
704
|
+
Tree.prototype.injectStyle = function () {
|
705
|
+
|
706
|
+
if (this.options.injectStyle && !document.getElementById(this.styleId)) {
|
707
|
+
$('<style type="text/css" id="' + this.styleId + '"> ' + this.buildStyle() + ' </style>').appendTo('head');
|
708
|
+
}
|
709
|
+
};
|
710
|
+
|
711
|
+
// Construct trees style based on user options
|
712
|
+
Tree.prototype.buildStyle = function () {
|
713
|
+
|
714
|
+
var style = '.node-' + this.elementId + '{';
|
715
|
+
|
716
|
+
if (this.options.color) {
|
717
|
+
style += 'color:' + this.options.color + ';';
|
718
|
+
}
|
719
|
+
|
720
|
+
if (this.options.backColor) {
|
721
|
+
style += 'background-color:' + this.options.backColor + ';';
|
722
|
+
}
|
723
|
+
|
724
|
+
if (!this.options.showBorder) {
|
725
|
+
style += 'border:none;';
|
726
|
+
}
|
727
|
+
else if (this.options.borderColor) {
|
728
|
+
style += 'border:1px solid ' + this.options.borderColor + ';';
|
729
|
+
}
|
730
|
+
style += '}';
|
731
|
+
|
732
|
+
if (this.options.onhoverColor) {
|
733
|
+
style += '.node-' + this.elementId + ':not(.node-disabled):hover{' +
|
734
|
+
'background-color:' + this.options.onhoverColor + ';' +
|
735
|
+
'}';
|
736
|
+
}
|
737
|
+
|
738
|
+
return this.css + style;
|
739
|
+
};
|
740
|
+
|
741
|
+
Tree.prototype.template = {
|
742
|
+
list: '<ul class="list-group"></ul>',
|
743
|
+
item: '<li class="list-group-item"></li>',
|
744
|
+
indent: '<span class="indent"></span>',
|
745
|
+
icon: '<span class="icon"></span>',
|
746
|
+
link: '<a href="#" style="color:inherit;"></a>',
|
747
|
+
badge: '<span class="badge"></span>'
|
748
|
+
};
|
749
|
+
|
750
|
+
Tree.prototype.css = '.treeview .list-group-item{cursor:pointer}.treeview span.indent{margin-left:10px;margin-right:10px}.treeview span.icon{width:12px;margin-right:5px}.treeview .node-disabled{color:silver;cursor:not-allowed}'
|
751
|
+
|
752
|
+
|
753
|
+
/**
|
754
|
+
Returns a single node object that matches the given node id.
|
755
|
+
@param {Number} nodeId - A node's unique identifier
|
756
|
+
@return {Object} node - Matching node
|
757
|
+
*/
|
758
|
+
Tree.prototype.getNode = function (nodeId) {
|
759
|
+
return this.nodes[nodeId];
|
760
|
+
};
|
761
|
+
|
762
|
+
Tree.prototype.addToParent = function(node, parentId) {
|
763
|
+
var parent = this.nodes.find(function (node, index) {
|
764
|
+
return node.id == parentId;
|
765
|
+
});
|
766
|
+
|
767
|
+
if (parent && parent.state.expanded) {
|
768
|
+
parent.nodes.push(node);
|
769
|
+
} else if(parent && !parent.state.expanded && !parent.nodes) {
|
770
|
+
parent.nodes = [];
|
771
|
+
} else if (!parentId) {
|
772
|
+
this.tree.push(node);
|
773
|
+
}
|
774
|
+
|
775
|
+
this.nodes = [];
|
776
|
+
this.destroy();
|
777
|
+
this.subscribeEvents();
|
778
|
+
this.setInitialStates({ nodes: this.tree }, 0);
|
779
|
+
this.render();
|
780
|
+
};
|
781
|
+
|
782
|
+
Tree.prototype.editNode = function(editedNode) {
|
783
|
+
var node = this.nodes.find(function (node, index) {
|
784
|
+
return node.id == editedNode.id;
|
785
|
+
});
|
786
|
+
|
787
|
+
delete editedNode.nodes;
|
788
|
+
Object.assign(node, editedNode);
|
789
|
+
this.nodes = [];
|
790
|
+
this.destroy();
|
791
|
+
this.subscribeEvents();
|
792
|
+
this.setInitialStates({ nodes: this.tree }, 0);
|
793
|
+
this.render();
|
794
|
+
};
|
795
|
+
|
796
|
+
/**
|
797
|
+
Returns the parent node of a given node, if valid otherwise returns undefined.
|
798
|
+
@param {Object|Number} identifier - A valid node or node id
|
799
|
+
@returns {Object} node - The parent node
|
800
|
+
*/
|
801
|
+
Tree.prototype.getParent = function (identifier) {
|
802
|
+
var node = this.identifyNode(identifier);
|
803
|
+
return this.nodes[node.parentId];
|
804
|
+
};
|
805
|
+
|
806
|
+
/**
|
807
|
+
Returns an array of sibling nodes for a given node, if valid otherwise returns undefined.
|
808
|
+
@param {Object|Number} identifier - A valid node or node id
|
809
|
+
@returns {Array} nodes - Sibling nodes
|
810
|
+
*/
|
811
|
+
Tree.prototype.getSiblings = function (identifier) {
|
812
|
+
var node = this.identifyNode(identifier);
|
813
|
+
var parent = this.getParent(node);
|
814
|
+
var nodes = parent ? parent.nodes : this.tree;
|
815
|
+
return nodes.filter(function (obj) {
|
816
|
+
return obj.nodeId !== node.nodeId;
|
817
|
+
});
|
818
|
+
};
|
819
|
+
|
820
|
+
/**
|
821
|
+
Returns an array of selected nodes.
|
822
|
+
@returns {Array} nodes - Selected nodes
|
823
|
+
*/
|
824
|
+
Tree.prototype.getSelected = function () {
|
825
|
+
return this.findNodes('true', 'g', 'state.selected');
|
826
|
+
};
|
827
|
+
|
828
|
+
/**
|
829
|
+
Returns an array of unselected nodes.
|
830
|
+
@returns {Array} nodes - Unselected nodes
|
831
|
+
*/
|
832
|
+
Tree.prototype.getUnselected = function () {
|
833
|
+
return this.findNodes('false', 'g', 'state.selected');
|
834
|
+
};
|
835
|
+
|
836
|
+
/**
|
837
|
+
Returns an array of expanded nodes.
|
838
|
+
@returns {Array} nodes - Expanded nodes
|
839
|
+
*/
|
840
|
+
Tree.prototype.getExpanded = function () {
|
841
|
+
return this.findNodes('true', 'g', 'state.expanded');
|
842
|
+
};
|
843
|
+
|
844
|
+
/**
|
845
|
+
Returns an array of collapsed nodes.
|
846
|
+
@returns {Array} nodes - Collapsed nodes
|
847
|
+
*/
|
848
|
+
Tree.prototype.getCollapsed = function () {
|
849
|
+
return this.findNodes('false', 'g', 'state.expanded');
|
850
|
+
};
|
851
|
+
|
852
|
+
/**
|
853
|
+
Returns an array of checked nodes.
|
854
|
+
@returns {Array} nodes - Checked nodes
|
855
|
+
*/
|
856
|
+
Tree.prototype.getChecked = function () {
|
857
|
+
return this.findNodes('true', 'g', 'state.checked');
|
858
|
+
};
|
859
|
+
|
860
|
+
/**
|
861
|
+
Returns an array of unchecked nodes.
|
862
|
+
@returns {Array} nodes - Unchecked nodes
|
863
|
+
*/
|
864
|
+
Tree.prototype.getUnchecked = function () {
|
865
|
+
return this.findNodes('false', 'g', 'state.checked');
|
866
|
+
};
|
867
|
+
|
868
|
+
/**
|
869
|
+
Returns an array of disabled nodes.
|
870
|
+
@returns {Array} nodes - Disabled nodes
|
871
|
+
*/
|
872
|
+
Tree.prototype.getDisabled = function () {
|
873
|
+
return this.findNodes('true', 'g', 'state.disabled');
|
874
|
+
};
|
875
|
+
|
876
|
+
/**
|
877
|
+
Returns an array of enabled nodes.
|
878
|
+
@returns {Array} nodes - Enabled nodes
|
879
|
+
*/
|
880
|
+
Tree.prototype.getEnabled = function () {
|
881
|
+
return this.findNodes('false', 'g', 'state.disabled');
|
882
|
+
};
|
883
|
+
|
884
|
+
|
885
|
+
/**
|
886
|
+
Set a node state to selected
|
887
|
+
@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
|
888
|
+
@param {optional Object} options
|
889
|
+
*/
|
890
|
+
Tree.prototype.selectNode = function (identifiers, options) {
|
891
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
892
|
+
this.setSelectedState(node, true, options);
|
893
|
+
}, this));
|
894
|
+
|
895
|
+
this.render();
|
896
|
+
};
|
897
|
+
|
898
|
+
/**
|
899
|
+
Set a node state to unselected
|
900
|
+
@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
|
901
|
+
@param {optional Object} options
|
902
|
+
*/
|
903
|
+
Tree.prototype.unselectNode = function (identifiers, options) {
|
904
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
905
|
+
this.setSelectedState(node, false, options);
|
906
|
+
}, this));
|
907
|
+
|
908
|
+
this.render();
|
909
|
+
};
|
910
|
+
|
911
|
+
/**
|
912
|
+
Toggles a node selected state; selecting if unselected, unselecting if selected.
|
913
|
+
@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
|
914
|
+
@param {optional Object} options
|
915
|
+
*/
|
916
|
+
Tree.prototype.toggleNodeSelected = function (identifiers, options) {
|
917
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
918
|
+
this.toggleSelectedState(node, options);
|
919
|
+
}, this));
|
920
|
+
|
921
|
+
this.render();
|
922
|
+
};
|
923
|
+
|
924
|
+
|
925
|
+
/**
|
926
|
+
Collapse all tree nodes
|
927
|
+
@param {optional Object} options
|
928
|
+
*/
|
929
|
+
Tree.prototype.collapseAll = function (options) {
|
930
|
+
var identifiers = this.findNodes('true', 'g', 'state.expanded');
|
931
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
932
|
+
this.setExpandedState(node, false, options);
|
933
|
+
}, this));
|
934
|
+
|
935
|
+
this.render();
|
936
|
+
};
|
937
|
+
|
938
|
+
/**
|
939
|
+
Collapse a given tree node
|
940
|
+
@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
|
941
|
+
@param {optional Object} options
|
942
|
+
*/
|
943
|
+
Tree.prototype.collapseNode = function (identifiers, options) {
|
944
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
945
|
+
this.setExpandedState(node, false, options);
|
946
|
+
}, this));
|
947
|
+
|
948
|
+
this.render();
|
949
|
+
};
|
950
|
+
|
951
|
+
/**
|
952
|
+
Expand all tree nodes
|
953
|
+
@param {optional Object} options
|
954
|
+
*/
|
955
|
+
Tree.prototype.expandAll = function (options) {
|
956
|
+
options = $.extend({}, _default.options, options);
|
957
|
+
|
958
|
+
if (options && options.levels) {
|
959
|
+
this.expandLevels(this.tree, options.levels, options);
|
960
|
+
}
|
961
|
+
else {
|
962
|
+
var identifiers = this.findNodes('false', 'g', 'state.expanded');
|
963
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
964
|
+
this.setExpandedState(node, true, options);
|
965
|
+
}, this));
|
966
|
+
}
|
967
|
+
|
968
|
+
this.render();
|
969
|
+
};
|
970
|
+
|
971
|
+
/**
|
972
|
+
Expand a given tree node
|
973
|
+
@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
|
974
|
+
@param {optional Object} options
|
975
|
+
*/
|
976
|
+
Tree.prototype.expandNode = function (identifiers, options) {
|
977
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
978
|
+
this.setExpandedState(node, true, options);
|
979
|
+
if (node.nodes && (options && options.levels)) {
|
980
|
+
this.expandLevels(node.nodes, options.levels-1, options);
|
981
|
+
}
|
982
|
+
}, this));
|
983
|
+
|
984
|
+
this.render();
|
985
|
+
};
|
986
|
+
|
987
|
+
Tree.prototype.expandLevels = function (nodes, level, options) {
|
988
|
+
options = $.extend({}, _default.options, options);
|
989
|
+
|
990
|
+
$.each(nodes, $.proxy(function (index, node) {
|
991
|
+
this.setExpandedState(node, (level > 0) ? true : false, options);
|
992
|
+
if (node.nodes) {
|
993
|
+
this.expandLevels(node.nodes, level-1, options);
|
994
|
+
}
|
995
|
+
}, this));
|
996
|
+
};
|
997
|
+
|
998
|
+
/**
|
999
|
+
Reveals a given tree node, expanding the tree from node to root.
|
1000
|
+
@param {Object|Number|Array} identifiers - A valid node, node id or array of node identifiers
|
1001
|
+
@param {optional Object} options
|
1002
|
+
*/
|
1003
|
+
Tree.prototype.revealNode = function (identifiers, options) {
|
1004
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
1005
|
+
var parentNode = this.getParent(node);
|
1006
|
+
while (parentNode) {
|
1007
|
+
this.setExpandedState(parentNode, true, options);
|
1008
|
+
parentNode = this.getParent(parentNode);
|
1009
|
+
};
|
1010
|
+
}, this));
|
1011
|
+
|
1012
|
+
this.render();
|
1013
|
+
};
|
1014
|
+
|
1015
|
+
/**
|
1016
|
+
Toggles a nodes expanded state; collapsing if expanded, expanding if collapsed.
|
1017
|
+
@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
|
1018
|
+
@param {optional Object} options
|
1019
|
+
*/
|
1020
|
+
Tree.prototype.toggleNodeExpanded = function (identifiers, options) {
|
1021
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
1022
|
+
this.toggleExpandedState(node, options);
|
1023
|
+
}, this));
|
1024
|
+
|
1025
|
+
this.render();
|
1026
|
+
};
|
1027
|
+
|
1028
|
+
|
1029
|
+
/**
|
1030
|
+
Check all tree nodes
|
1031
|
+
@param {optional Object} options
|
1032
|
+
*/
|
1033
|
+
Tree.prototype.checkAll = function (options) {
|
1034
|
+
var identifiers = this.findNodes('false', 'g', 'state.checked');
|
1035
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
1036
|
+
this.setCheckedState(node, true, options);
|
1037
|
+
}, this));
|
1038
|
+
|
1039
|
+
this.render();
|
1040
|
+
};
|
1041
|
+
|
1042
|
+
/**
|
1043
|
+
Check a given tree node
|
1044
|
+
@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
|
1045
|
+
@param {optional Object} options
|
1046
|
+
*/
|
1047
|
+
Tree.prototype.checkNode = function (identifiers, options) {
|
1048
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
1049
|
+
this.setCheckedState(node, true, options);
|
1050
|
+
}, this));
|
1051
|
+
|
1052
|
+
this.render();
|
1053
|
+
};
|
1054
|
+
|
1055
|
+
/**
|
1056
|
+
Uncheck all tree nodes
|
1057
|
+
@param {optional Object} options
|
1058
|
+
*/
|
1059
|
+
Tree.prototype.uncheckAll = function (options) {
|
1060
|
+
var identifiers = this.findNodes('true', 'g', 'state.checked');
|
1061
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
1062
|
+
this.setCheckedState(node, false, options);
|
1063
|
+
}, this));
|
1064
|
+
|
1065
|
+
this.render();
|
1066
|
+
};
|
1067
|
+
|
1068
|
+
/**
|
1069
|
+
Uncheck a given tree node
|
1070
|
+
@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
|
1071
|
+
@param {optional Object} options
|
1072
|
+
*/
|
1073
|
+
Tree.prototype.uncheckNode = function (identifiers, options) {
|
1074
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
1075
|
+
this.setCheckedState(node, false, options);
|
1076
|
+
}, this));
|
1077
|
+
|
1078
|
+
this.render();
|
1079
|
+
};
|
1080
|
+
|
1081
|
+
/**
|
1082
|
+
Toggles a nodes checked state; checking if unchecked, unchecking if checked.
|
1083
|
+
@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
|
1084
|
+
@param {optional Object} options
|
1085
|
+
*/
|
1086
|
+
Tree.prototype.toggleNodeChecked = function (identifiers, options) {
|
1087
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
1088
|
+
this.toggleCheckedState(node, options);
|
1089
|
+
}, this));
|
1090
|
+
|
1091
|
+
this.render();
|
1092
|
+
};
|
1093
|
+
|
1094
|
+
|
1095
|
+
/**
|
1096
|
+
Disable all tree nodes
|
1097
|
+
@param {optional Object} options
|
1098
|
+
*/
|
1099
|
+
Tree.prototype.disableAll = function (options) {
|
1100
|
+
var identifiers = this.findNodes('false', 'g', 'state.disabled');
|
1101
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
1102
|
+
this.setDisabledState(node, true, options);
|
1103
|
+
}, this));
|
1104
|
+
|
1105
|
+
this.render();
|
1106
|
+
};
|
1107
|
+
|
1108
|
+
/**
|
1109
|
+
Disable a given tree node
|
1110
|
+
@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
|
1111
|
+
@param {optional Object} options
|
1112
|
+
*/
|
1113
|
+
Tree.prototype.disableNode = function (identifiers, options) {
|
1114
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
1115
|
+
this.setDisabledState(node, true, options);
|
1116
|
+
}, this));
|
1117
|
+
|
1118
|
+
this.render();
|
1119
|
+
};
|
1120
|
+
|
1121
|
+
/**
|
1122
|
+
Enable all tree nodes
|
1123
|
+
@param {optional Object} options
|
1124
|
+
*/
|
1125
|
+
Tree.prototype.enableAll = function (options) {
|
1126
|
+
var identifiers = this.findNodes('true', 'g', 'state.disabled');
|
1127
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
1128
|
+
this.setDisabledState(node, false, options);
|
1129
|
+
}, this));
|
1130
|
+
|
1131
|
+
this.render();
|
1132
|
+
};
|
1133
|
+
|
1134
|
+
/**
|
1135
|
+
Enable a given tree node
|
1136
|
+
@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
|
1137
|
+
@param {optional Object} options
|
1138
|
+
*/
|
1139
|
+
Tree.prototype.enableNode = function (identifiers, options) {
|
1140
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
1141
|
+
this.setDisabledState(node, false, options);
|
1142
|
+
}, this));
|
1143
|
+
|
1144
|
+
this.render();
|
1145
|
+
};
|
1146
|
+
|
1147
|
+
/**
|
1148
|
+
Toggles a nodes disabled state; disabling is enabled, enabling if disabled.
|
1149
|
+
@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
|
1150
|
+
@param {optional Object} options
|
1151
|
+
*/
|
1152
|
+
Tree.prototype.toggleNodeDisabled = function (identifiers, options) {
|
1153
|
+
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
|
1154
|
+
this.setDisabledState(node, !node.state.disabled, options);
|
1155
|
+
}, this));
|
1156
|
+
|
1157
|
+
this.render();
|
1158
|
+
};
|
1159
|
+
|
1160
|
+
|
1161
|
+
/**
|
1162
|
+
Common code for processing multiple identifiers
|
1163
|
+
*/
|
1164
|
+
Tree.prototype.forEachIdentifier = function (identifiers, options, callback) {
|
1165
|
+
|
1166
|
+
options = $.extend({}, _default.options, options);
|
1167
|
+
|
1168
|
+
if (!(identifiers instanceof Array)) {
|
1169
|
+
identifiers = [identifiers];
|
1170
|
+
}
|
1171
|
+
|
1172
|
+
$.each(identifiers, $.proxy(function (index, identifier) {
|
1173
|
+
callback(this.identifyNode(identifier), options);
|
1174
|
+
}, this));
|
1175
|
+
};
|
1176
|
+
|
1177
|
+
/*
|
1178
|
+
Identifies a node from either a node id or object
|
1179
|
+
*/
|
1180
|
+
Tree.prototype.identifyNode = function (identifier) {
|
1181
|
+
return ((typeof identifier) === 'number') ?
|
1182
|
+
this.nodes[identifier] :
|
1183
|
+
identifier;
|
1184
|
+
};
|
1185
|
+
|
1186
|
+
/**
|
1187
|
+
Searches the tree for nodes (text) that match given criteria
|
1188
|
+
@param {String} pattern - A given string to match against
|
1189
|
+
@param {optional Object} options - Search criteria options
|
1190
|
+
@return {Array} nodes - Matching nodes
|
1191
|
+
*/
|
1192
|
+
Tree.prototype.search = function (pattern, options) {
|
1193
|
+
options = $.extend({}, _default.searchOptions, options);
|
1194
|
+
|
1195
|
+
this.clearSearch({ render: false });
|
1196
|
+
|
1197
|
+
var results = [];
|
1198
|
+
if (pattern && pattern.length > 0) {
|
1199
|
+
|
1200
|
+
if (options.exactMatch) {
|
1201
|
+
pattern = '^' + pattern + '$';
|
1202
|
+
}
|
1203
|
+
|
1204
|
+
var modifier = 'g';
|
1205
|
+
if (options.ignoreCase) {
|
1206
|
+
modifier += 'i';
|
1207
|
+
}
|
1208
|
+
|
1209
|
+
results = this.findNodes(pattern, modifier);
|
1210
|
+
|
1211
|
+
// Add searchResult property to all matching nodes
|
1212
|
+
// This will be used to apply custom styles
|
1213
|
+
// and when identifying result to be cleared
|
1214
|
+
$.each(results, function (index, node) {
|
1215
|
+
node.searchResult = true;
|
1216
|
+
})
|
1217
|
+
}
|
1218
|
+
|
1219
|
+
// If revealResults, then render is triggered from revealNode
|
1220
|
+
// otherwise we just call render.
|
1221
|
+
if (options.revealResults) {
|
1222
|
+
this.revealNode(results);
|
1223
|
+
}
|
1224
|
+
else {
|
1225
|
+
this.render();
|
1226
|
+
}
|
1227
|
+
|
1228
|
+
this.$element.trigger('searchComplete', $.extend(true, {}, results));
|
1229
|
+
|
1230
|
+
return results;
|
1231
|
+
};
|
1232
|
+
|
1233
|
+
/**
|
1234
|
+
Clears previous search results
|
1235
|
+
*/
|
1236
|
+
Tree.prototype.clearSearch = function (options) {
|
1237
|
+
|
1238
|
+
options = $.extend({}, { render: true }, options);
|
1239
|
+
|
1240
|
+
var results = $.each(this.findNodes('true', 'g', 'searchResult'), function (index, node) {
|
1241
|
+
node.searchResult = false;
|
1242
|
+
});
|
1243
|
+
|
1244
|
+
if (options.render) {
|
1245
|
+
this.render();
|
1246
|
+
}
|
1247
|
+
|
1248
|
+
this.$element.trigger('searchCleared', $.extend(true, {}, results));
|
1249
|
+
};
|
1250
|
+
|
1251
|
+
/**
|
1252
|
+
Find nodes that match a given criteria
|
1253
|
+
@param {String} pattern - A given string to match against
|
1254
|
+
@param {optional String} modifier - Valid RegEx modifiers
|
1255
|
+
@param {optional String} attribute - Attribute to compare pattern against
|
1256
|
+
@return {Array} nodes - Nodes that match your criteria
|
1257
|
+
*/
|
1258
|
+
Tree.prototype.findNodes = function (pattern, modifier, attribute) {
|
1259
|
+
|
1260
|
+
modifier = modifier || 'g';
|
1261
|
+
attribute = attribute || 'text';
|
1262
|
+
|
1263
|
+
var _this = this;
|
1264
|
+
return $.grep(this.nodes, function (node) {
|
1265
|
+
var val = _this.getNodeValue(node, attribute);
|
1266
|
+
if (typeof val === 'string') {
|
1267
|
+
return val.match(new RegExp(pattern, modifier));
|
1268
|
+
}
|
1269
|
+
});
|
1270
|
+
};
|
1271
|
+
|
1272
|
+
/**
|
1273
|
+
Recursive find for retrieving nested attributes values
|
1274
|
+
All values are return as strings, unless invalid
|
1275
|
+
@param {Object} obj - Typically a node, could be any object
|
1276
|
+
@param {String} attr - Identifies an object property using dot notation
|
1277
|
+
@return {String} value - Matching attributes string representation
|
1278
|
+
*/
|
1279
|
+
Tree.prototype.getNodeValue = function (obj, attr) {
|
1280
|
+
var index = attr.indexOf('.');
|
1281
|
+
if (index > 0) {
|
1282
|
+
var _obj = obj[attr.substring(0, index)];
|
1283
|
+
var _attr = attr.substring(index + 1, attr.length);
|
1284
|
+
return this.getNodeValue(_obj, _attr);
|
1285
|
+
}
|
1286
|
+
else {
|
1287
|
+
if (obj.hasOwnProperty(attr)) {
|
1288
|
+
return obj[attr].toString();
|
1289
|
+
}
|
1290
|
+
else {
|
1291
|
+
return undefined;
|
1292
|
+
}
|
1293
|
+
}
|
1294
|
+
};
|
1295
|
+
|
1296
|
+
var logError = function (message) {
|
1297
|
+
if (window.console) {
|
1298
|
+
window.console.error(message);
|
1299
|
+
}
|
1300
|
+
};
|
1301
|
+
|
1302
|
+
// Prevent against multiple instantiations,
|
1303
|
+
// handle updates and method calls
|
1304
|
+
$.fn[pluginName] = function (options, args) {
|
1305
|
+
|
1306
|
+
var result;
|
1307
|
+
|
1308
|
+
this.each(function () {
|
1309
|
+
var _this = $.data(this, pluginName);
|
1310
|
+
if (typeof options === 'string') {
|
1311
|
+
if (!_this) {
|
1312
|
+
logError('Not initialized, can not call method : ' + options);
|
1313
|
+
}
|
1314
|
+
else if (!$.isFunction(_this[options]) || options.charAt(0) === '_') {
|
1315
|
+
logError('No such method : ' + options);
|
1316
|
+
}
|
1317
|
+
else {
|
1318
|
+
if (!(args instanceof Array)) {
|
1319
|
+
args = [ args ];
|
1320
|
+
}
|
1321
|
+
result = _this[options].apply(_this, args);
|
1322
|
+
}
|
1323
|
+
}
|
1324
|
+
else if (typeof options === 'boolean') {
|
1325
|
+
result = _this;
|
1326
|
+
}
|
1327
|
+
else {
|
1328
|
+
$.data(this, pluginName, new Tree(this, $.extend(true, {}, options)));
|
1329
|
+
}
|
1330
|
+
});
|
1331
|
+
|
1332
|
+
return result || this;
|
1333
|
+
};
|
1334
|
+
|
1335
|
+
})(jQuery, window, document);
|