Mercurial > repos > mingchen0919 > aurora_star_site
comparison vakata-jstree-3.3.5/src/jstree.checkbox.js @ 0:c12e291895ff draft
planemo upload commit 104ae24ee30761a0099eeb91362ed1e3e13aba4b
author | mingchen0919 |
---|---|
date | Tue, 01 May 2018 10:45:22 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c12e291895ff |
---|---|
1 /** | |
2 * ### Checkbox plugin | |
3 * | |
4 * This plugin renders checkbox icons in front of each node, making multiple selection much easier. | |
5 * It also supports tri-state behavior, meaning that if a node has a few of its children checked it will be rendered as undetermined, and state will be propagated up. | |
6 */ | |
7 /*globals jQuery, define, exports, require, document */ | |
8 (function (factory) { | |
9 "use strict"; | |
10 if (typeof define === 'function' && define.amd) { | |
11 define('jstree.checkbox', ['jquery','jstree'], factory); | |
12 } | |
13 else if(typeof exports === 'object') { | |
14 factory(require('jquery'), require('jstree')); | |
15 } | |
16 else { | |
17 factory(jQuery, jQuery.jstree); | |
18 } | |
19 }(function ($, jstree, undefined) { | |
20 "use strict"; | |
21 | |
22 if($.jstree.plugins.checkbox) { return; } | |
23 | |
24 var _i = document.createElement('I'); | |
25 _i.className = 'jstree-icon jstree-checkbox'; | |
26 _i.setAttribute('role', 'presentation'); | |
27 /** | |
28 * stores all defaults for the checkbox plugin | |
29 * @name $.jstree.defaults.checkbox | |
30 * @plugin checkbox | |
31 */ | |
32 $.jstree.defaults.checkbox = { | |
33 /** | |
34 * a boolean indicating if checkboxes should be visible (can be changed at a later time using `show_checkboxes()` and `hide_checkboxes`). Defaults to `true`. | |
35 * @name $.jstree.defaults.checkbox.visible | |
36 * @plugin checkbox | |
37 */ | |
38 visible : true, | |
39 /** | |
40 * a boolean indicating if checkboxes should cascade down and have an undetermined state. Defaults to `true`. | |
41 * @name $.jstree.defaults.checkbox.three_state | |
42 * @plugin checkbox | |
43 */ | |
44 three_state : true, | |
45 /** | |
46 * a boolean indicating if clicking anywhere on the node should act as clicking on the checkbox. Defaults to `true`. | |
47 * @name $.jstree.defaults.checkbox.whole_node | |
48 * @plugin checkbox | |
49 */ | |
50 whole_node : true, | |
51 /** | |
52 * a boolean indicating if the selected style of a node should be kept, or removed. Defaults to `true`. | |
53 * @name $.jstree.defaults.checkbox.keep_selected_style | |
54 * @plugin checkbox | |
55 */ | |
56 keep_selected_style : true, | |
57 /** | |
58 * This setting controls how cascading and undetermined nodes are applied. | |
59 * If 'up' is in the string - cascading up is enabled, if 'down' is in the string - cascading down is enabled, if 'undetermined' is in the string - undetermined nodes will be used. | |
60 * If `three_state` is set to `true` this setting is automatically set to 'up+down+undetermined'. Defaults to ''. | |
61 * @name $.jstree.defaults.checkbox.cascade | |
62 * @plugin checkbox | |
63 */ | |
64 cascade : '', | |
65 /** | |
66 * This setting controls if checkbox are bound to the general tree selection or to an internal array maintained by the checkbox plugin. Defaults to `true`, only set to `false` if you know exactly what you are doing. | |
67 * @name $.jstree.defaults.checkbox.tie_selection | |
68 * @plugin checkbox | |
69 */ | |
70 tie_selection : true, | |
71 | |
72 /** | |
73 * This setting controls if cascading down affects disabled checkboxes | |
74 * @name $.jstree.defaults.checkbox.cascade_to_disabled | |
75 * @plugin checkbox | |
76 */ | |
77 cascade_to_disabled : true, | |
78 | |
79 /** | |
80 * This setting controls if cascading down affects hidden checkboxes | |
81 * @name $.jstree.defaults.checkbox.cascade_to_hidden | |
82 * @plugin checkbox | |
83 */ | |
84 cascade_to_hidden : true | |
85 }; | |
86 $.jstree.plugins.checkbox = function (options, parent) { | |
87 this.bind = function () { | |
88 parent.bind.call(this); | |
89 this._data.checkbox.uto = false; | |
90 this._data.checkbox.selected = []; | |
91 if(this.settings.checkbox.three_state) { | |
92 this.settings.checkbox.cascade = 'up+down+undetermined'; | |
93 } | |
94 this.element | |
95 .on("init.jstree", $.proxy(function () { | |
96 this._data.checkbox.visible = this.settings.checkbox.visible; | |
97 if(!this.settings.checkbox.keep_selected_style) { | |
98 this.element.addClass('jstree-checkbox-no-clicked'); | |
99 } | |
100 if(this.settings.checkbox.tie_selection) { | |
101 this.element.addClass('jstree-checkbox-selection'); | |
102 } | |
103 }, this)) | |
104 .on("loading.jstree", $.proxy(function () { | |
105 this[ this._data.checkbox.visible ? 'show_checkboxes' : 'hide_checkboxes' ](); | |
106 }, this)); | |
107 if(this.settings.checkbox.cascade.indexOf('undetermined') !== -1) { | |
108 this.element | |
109 .on('changed.jstree uncheck_node.jstree check_node.jstree uncheck_all.jstree check_all.jstree move_node.jstree copy_node.jstree redraw.jstree open_node.jstree', $.proxy(function () { | |
110 // only if undetermined is in setting | |
111 if(this._data.checkbox.uto) { clearTimeout(this._data.checkbox.uto); } | |
112 this._data.checkbox.uto = setTimeout($.proxy(this._undetermined, this), 50); | |
113 }, this)); | |
114 } | |
115 if(!this.settings.checkbox.tie_selection) { | |
116 this.element | |
117 .on('model.jstree', $.proxy(function (e, data) { | |
118 var m = this._model.data, | |
119 p = m[data.parent], | |
120 dpc = data.nodes, | |
121 i, j; | |
122 for(i = 0, j = dpc.length; i < j; i++) { | |
123 m[dpc[i]].state.checked = m[dpc[i]].state.checked || (m[dpc[i]].original && m[dpc[i]].original.state && m[dpc[i]].original.state.checked); | |
124 if(m[dpc[i]].state.checked) { | |
125 this._data.checkbox.selected.push(dpc[i]); | |
126 } | |
127 } | |
128 }, this)); | |
129 } | |
130 if(this.settings.checkbox.cascade.indexOf('up') !== -1 || this.settings.checkbox.cascade.indexOf('down') !== -1) { | |
131 this.element | |
132 .on('model.jstree', $.proxy(function (e, data) { | |
133 var m = this._model.data, | |
134 p = m[data.parent], | |
135 dpc = data.nodes, | |
136 chd = [], | |
137 c, i, j, k, l, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection; | |
138 | |
139 if(s.indexOf('down') !== -1) { | |
140 // apply down | |
141 if(p.state[ t ? 'selected' : 'checked' ]) { | |
142 for(i = 0, j = dpc.length; i < j; i++) { | |
143 m[dpc[i]].state[ t ? 'selected' : 'checked' ] = true; | |
144 } | |
145 | |
146 this._data[ t ? 'core' : 'checkbox' ].selected = this._data[ t ? 'core' : 'checkbox' ].selected.concat(dpc); | |
147 } | |
148 else { | |
149 for(i = 0, j = dpc.length; i < j; i++) { | |
150 if(m[dpc[i]].state[ t ? 'selected' : 'checked' ]) { | |
151 for(k = 0, l = m[dpc[i]].children_d.length; k < l; k++) { | |
152 m[m[dpc[i]].children_d[k]].state[ t ? 'selected' : 'checked' ] = true; | |
153 } | |
154 this._data[ t ? 'core' : 'checkbox' ].selected = this._data[ t ? 'core' : 'checkbox' ].selected.concat(m[dpc[i]].children_d); | |
155 } | |
156 } | |
157 } | |
158 } | |
159 | |
160 if(s.indexOf('up') !== -1) { | |
161 // apply up | |
162 for(i = 0, j = p.children_d.length; i < j; i++) { | |
163 if(!m[p.children_d[i]].children.length) { | |
164 chd.push(m[p.children_d[i]].parent); | |
165 } | |
166 } | |
167 chd = $.vakata.array_unique(chd); | |
168 for(k = 0, l = chd.length; k < l; k++) { | |
169 p = m[chd[k]]; | |
170 while(p && p.id !== $.jstree.root) { | |
171 c = 0; | |
172 for(i = 0, j = p.children.length; i < j; i++) { | |
173 c += m[p.children[i]].state[ t ? 'selected' : 'checked' ]; | |
174 } | |
175 if(c === j) { | |
176 p.state[ t ? 'selected' : 'checked' ] = true; | |
177 this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id); | |
178 tmp = this.get_node(p, true); | |
179 if(tmp && tmp.length) { | |
180 tmp.attr('aria-selected', true).children('.jstree-anchor').addClass( t ? 'jstree-clicked' : 'jstree-checked'); | |
181 } | |
182 } | |
183 else { | |
184 break; | |
185 } | |
186 p = this.get_node(p.parent); | |
187 } | |
188 } | |
189 } | |
190 | |
191 this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_unique(this._data[ t ? 'core' : 'checkbox' ].selected); | |
192 }, this)) | |
193 .on(this.settings.checkbox.tie_selection ? 'select_node.jstree' : 'check_node.jstree', $.proxy(function (e, data) { | |
194 var self = this, | |
195 obj = data.node, | |
196 m = this._model.data, | |
197 par = this.get_node(obj.parent), | |
198 i, j, c, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection, | |
199 sel = {}, cur = this._data[ t ? 'core' : 'checkbox' ].selected; | |
200 | |
201 for (i = 0, j = cur.length; i < j; i++) { | |
202 sel[cur[i]] = true; | |
203 } | |
204 | |
205 // apply down | |
206 if(s.indexOf('down') !== -1) { | |
207 //this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_unique(this._data[ t ? 'core' : 'checkbox' ].selected.concat(obj.children_d)); | |
208 var selectedIds = this._cascade_new_checked_state(obj.id, true); | |
209 var temp = obj.children_d.concat(obj.id); | |
210 for (i = 0, j = temp.length; i < j; i++) { | |
211 if (selectedIds.indexOf(temp[i]) > -1) { | |
212 sel[temp[i]] = true; | |
213 } | |
214 else { | |
215 delete sel[temp[i]]; | |
216 } | |
217 } | |
218 } | |
219 | |
220 // apply up | |
221 if(s.indexOf('up') !== -1) { | |
222 while(par && par.id !== $.jstree.root) { | |
223 c = 0; | |
224 for(i = 0, j = par.children.length; i < j; i++) { | |
225 c += m[par.children[i]].state[ t ? 'selected' : 'checked' ]; | |
226 } | |
227 if(c === j) { | |
228 par.state[ t ? 'selected' : 'checked' ] = true; | |
229 sel[par.id] = true; | |
230 //this._data[ t ? 'core' : 'checkbox' ].selected.push(par.id); | |
231 tmp = this.get_node(par, true); | |
232 if(tmp && tmp.length) { | |
233 tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked'); | |
234 } | |
235 } | |
236 else { | |
237 break; | |
238 } | |
239 par = this.get_node(par.parent); | |
240 } | |
241 } | |
242 | |
243 cur = []; | |
244 for (i in sel) { | |
245 if (sel.hasOwnProperty(i)) { | |
246 cur.push(i); | |
247 } | |
248 } | |
249 this._data[ t ? 'core' : 'checkbox' ].selected = cur; | |
250 }, this)) | |
251 .on(this.settings.checkbox.tie_selection ? 'deselect_all.jstree' : 'uncheck_all.jstree', $.proxy(function (e, data) { | |
252 var obj = this.get_node($.jstree.root), | |
253 m = this._model.data, | |
254 i, j, tmp; | |
255 for(i = 0, j = obj.children_d.length; i < j; i++) { | |
256 tmp = m[obj.children_d[i]]; | |
257 if(tmp && tmp.original && tmp.original.state && tmp.original.state.undetermined) { | |
258 tmp.original.state.undetermined = false; | |
259 } | |
260 } | |
261 }, this)) | |
262 .on(this.settings.checkbox.tie_selection ? 'deselect_node.jstree' : 'uncheck_node.jstree', $.proxy(function (e, data) { | |
263 var self = this, | |
264 obj = data.node, | |
265 dom = this.get_node(obj, true), | |
266 i, j, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection, | |
267 cur = this._data[ t ? 'core' : 'checkbox' ].selected, sel = {}, | |
268 stillSelectedIds = [], | |
269 allIds = obj.children_d.concat(obj.id); | |
270 | |
271 // apply down | |
272 if(s.indexOf('down') !== -1) { | |
273 var selectedIds = this._cascade_new_checked_state(obj.id, false); | |
274 | |
275 cur = cur.filter(function(id) { | |
276 return allIds.indexOf(id) === -1 || selectedIds.indexOf(id) > -1; | |
277 }); | |
278 } | |
279 | |
280 // only apply up if cascade up is enabled and if this node is not selected | |
281 // (if all child nodes are disabled and cascade_to_disabled === false then this node will till be selected). | |
282 if(s.indexOf('up') !== -1 && cur.indexOf(obj.id) === -1) { | |
283 for(i = 0, j = obj.parents.length; i < j; i++) { | |
284 tmp = this._model.data[obj.parents[i]]; | |
285 tmp.state[ t ? 'selected' : 'checked' ] = false; | |
286 if(tmp && tmp.original && tmp.original.state && tmp.original.state.undetermined) { | |
287 tmp.original.state.undetermined = false; | |
288 } | |
289 tmp = this.get_node(obj.parents[i], true); | |
290 if(tmp && tmp.length) { | |
291 tmp.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked'); | |
292 } | |
293 } | |
294 | |
295 cur = cur.filter(function(id) { | |
296 return obj.parents.indexOf(id) === -1; | |
297 }); | |
298 } | |
299 | |
300 this._data[ t ? 'core' : 'checkbox' ].selected = cur; | |
301 }, this)); | |
302 } | |
303 if(this.settings.checkbox.cascade.indexOf('up') !== -1) { | |
304 this.element | |
305 .on('delete_node.jstree', $.proxy(function (e, data) { | |
306 // apply up (whole handler) | |
307 var p = this.get_node(data.parent), | |
308 m = this._model.data, | |
309 i, j, c, tmp, t = this.settings.checkbox.tie_selection; | |
310 while(p && p.id !== $.jstree.root && !p.state[ t ? 'selected' : 'checked' ]) { | |
311 c = 0; | |
312 for(i = 0, j = p.children.length; i < j; i++) { | |
313 c += m[p.children[i]].state[ t ? 'selected' : 'checked' ]; | |
314 } | |
315 if(j > 0 && c === j) { | |
316 p.state[ t ? 'selected' : 'checked' ] = true; | |
317 this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id); | |
318 tmp = this.get_node(p, true); | |
319 if(tmp && tmp.length) { | |
320 tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked'); | |
321 } | |
322 } | |
323 else { | |
324 break; | |
325 } | |
326 p = this.get_node(p.parent); | |
327 } | |
328 }, this)) | |
329 .on('move_node.jstree', $.proxy(function (e, data) { | |
330 // apply up (whole handler) | |
331 var is_multi = data.is_multi, | |
332 old_par = data.old_parent, | |
333 new_par = this.get_node(data.parent), | |
334 m = this._model.data, | |
335 p, c, i, j, tmp, t = this.settings.checkbox.tie_selection; | |
336 if(!is_multi) { | |
337 p = this.get_node(old_par); | |
338 while(p && p.id !== $.jstree.root && !p.state[ t ? 'selected' : 'checked' ]) { | |
339 c = 0; | |
340 for(i = 0, j = p.children.length; i < j; i++) { | |
341 c += m[p.children[i]].state[ t ? 'selected' : 'checked' ]; | |
342 } | |
343 if(j > 0 && c === j) { | |
344 p.state[ t ? 'selected' : 'checked' ] = true; | |
345 this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id); | |
346 tmp = this.get_node(p, true); | |
347 if(tmp && tmp.length) { | |
348 tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked'); | |
349 } | |
350 } | |
351 else { | |
352 break; | |
353 } | |
354 p = this.get_node(p.parent); | |
355 } | |
356 } | |
357 p = new_par; | |
358 while(p && p.id !== $.jstree.root) { | |
359 c = 0; | |
360 for(i = 0, j = p.children.length; i < j; i++) { | |
361 c += m[p.children[i]].state[ t ? 'selected' : 'checked' ]; | |
362 } | |
363 if(c === j) { | |
364 if(!p.state[ t ? 'selected' : 'checked' ]) { | |
365 p.state[ t ? 'selected' : 'checked' ] = true; | |
366 this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id); | |
367 tmp = this.get_node(p, true); | |
368 if(tmp && tmp.length) { | |
369 tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked'); | |
370 } | |
371 } | |
372 } | |
373 else { | |
374 if(p.state[ t ? 'selected' : 'checked' ]) { | |
375 p.state[ t ? 'selected' : 'checked' ] = false; | |
376 this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_remove_item(this._data[ t ? 'core' : 'checkbox' ].selected, p.id); | |
377 tmp = this.get_node(p, true); | |
378 if(tmp && tmp.length) { | |
379 tmp.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked'); | |
380 } | |
381 } | |
382 else { | |
383 break; | |
384 } | |
385 } | |
386 p = this.get_node(p.parent); | |
387 } | |
388 }, this)); | |
389 } | |
390 }; | |
391 /** | |
392 * get an array of all nodes whose state is "undetermined" | |
393 * @name get_undetermined([full]) | |
394 * @param {boolean} full: if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned | |
395 * @return {Array} | |
396 * @plugin checkbox | |
397 */ | |
398 this.get_undetermined = function (full) { | |
399 if (this.settings.checkbox.cascade.indexOf('undetermined') === -1) { | |
400 return []; | |
401 } | |
402 var i, j, k, l, o = {}, m = this._model.data, t = this.settings.checkbox.tie_selection, s = this._data[ t ? 'core' : 'checkbox' ].selected, p = [], tt = this, r = []; | |
403 for(i = 0, j = s.length; i < j; i++) { | |
404 if(m[s[i]] && m[s[i]].parents) { | |
405 for(k = 0, l = m[s[i]].parents.length; k < l; k++) { | |
406 if(o[m[s[i]].parents[k]] !== undefined) { | |
407 break; | |
408 } | |
409 if(m[s[i]].parents[k] !== $.jstree.root) { | |
410 o[m[s[i]].parents[k]] = true; | |
411 p.push(m[s[i]].parents[k]); | |
412 } | |
413 } | |
414 } | |
415 } | |
416 // attempt for server side undetermined state | |
417 this.element.find('.jstree-closed').not(':has(.jstree-children)') | |
418 .each(function () { | |
419 var tmp = tt.get_node(this), tmp2; | |
420 | |
421 if(!tmp) { return; } | |
422 | |
423 if(!tmp.state.loaded) { | |
424 if(tmp.original && tmp.original.state && tmp.original.state.undetermined && tmp.original.state.undetermined === true) { | |
425 if(o[tmp.id] === undefined && tmp.id !== $.jstree.root) { | |
426 o[tmp.id] = true; | |
427 p.push(tmp.id); | |
428 } | |
429 for(k = 0, l = tmp.parents.length; k < l; k++) { | |
430 if(o[tmp.parents[k]] === undefined && tmp.parents[k] !== $.jstree.root) { | |
431 o[tmp.parents[k]] = true; | |
432 p.push(tmp.parents[k]); | |
433 } | |
434 } | |
435 } | |
436 } | |
437 else { | |
438 for(i = 0, j = tmp.children_d.length; i < j; i++) { | |
439 tmp2 = m[tmp.children_d[i]]; | |
440 if(!tmp2.state.loaded && tmp2.original && tmp2.original.state && tmp2.original.state.undetermined && tmp2.original.state.undetermined === true) { | |
441 if(o[tmp2.id] === undefined && tmp2.id !== $.jstree.root) { | |
442 o[tmp2.id] = true; | |
443 p.push(tmp2.id); | |
444 } | |
445 for(k = 0, l = tmp2.parents.length; k < l; k++) { | |
446 if(o[tmp2.parents[k]] === undefined && tmp2.parents[k] !== $.jstree.root) { | |
447 o[tmp2.parents[k]] = true; | |
448 p.push(tmp2.parents[k]); | |
449 } | |
450 } | |
451 } | |
452 } | |
453 } | |
454 }); | |
455 for (i = 0, j = p.length; i < j; i++) { | |
456 if(!m[p[i]].state[ t ? 'selected' : 'checked' ]) { | |
457 r.push(full ? m[p[i]] : p[i]); | |
458 } | |
459 } | |
460 return r; | |
461 }; | |
462 /** | |
463 * set the undetermined state where and if necessary. Used internally. | |
464 * @private | |
465 * @name _undetermined() | |
466 * @plugin checkbox | |
467 */ | |
468 this._undetermined = function () { | |
469 if(this.element === null) { return; } | |
470 var p = this.get_undetermined(false), i, j, s; | |
471 | |
472 this.element.find('.jstree-undetermined').removeClass('jstree-undetermined'); | |
473 for (i = 0, j = p.length; i < j; i++) { | |
474 s = this.get_node(p[i], true); | |
475 if(s && s.length) { | |
476 s.children('.jstree-anchor').children('.jstree-checkbox').addClass('jstree-undetermined'); | |
477 } | |
478 } | |
479 }; | |
480 this.redraw_node = function(obj, deep, is_callback, force_render) { | |
481 obj = parent.redraw_node.apply(this, arguments); | |
482 if(obj) { | |
483 var i, j, tmp = null, icon = null; | |
484 for(i = 0, j = obj.childNodes.length; i < j; i++) { | |
485 if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) { | |
486 tmp = obj.childNodes[i]; | |
487 break; | |
488 } | |
489 } | |
490 if(tmp) { | |
491 if(!this.settings.checkbox.tie_selection && this._model.data[obj.id].state.checked) { tmp.className += ' jstree-checked'; } | |
492 icon = _i.cloneNode(false); | |
493 if(this._model.data[obj.id].state.checkbox_disabled) { icon.className += ' jstree-checkbox-disabled'; } | |
494 tmp.insertBefore(icon, tmp.childNodes[0]); | |
495 } | |
496 } | |
497 if(!is_callback && this.settings.checkbox.cascade.indexOf('undetermined') !== -1) { | |
498 if(this._data.checkbox.uto) { clearTimeout(this._data.checkbox.uto); } | |
499 this._data.checkbox.uto = setTimeout($.proxy(this._undetermined, this), 50); | |
500 } | |
501 return obj; | |
502 }; | |
503 /** | |
504 * show the node checkbox icons | |
505 * @name show_checkboxes() | |
506 * @plugin checkbox | |
507 */ | |
508 this.show_checkboxes = function () { this._data.core.themes.checkboxes = true; this.get_container_ul().removeClass("jstree-no-checkboxes"); }; | |
509 /** | |
510 * hide the node checkbox icons | |
511 * @name hide_checkboxes() | |
512 * @plugin checkbox | |
513 */ | |
514 this.hide_checkboxes = function () { this._data.core.themes.checkboxes = false; this.get_container_ul().addClass("jstree-no-checkboxes"); }; | |
515 /** | |
516 * toggle the node icons | |
517 * @name toggle_checkboxes() | |
518 * @plugin checkbox | |
519 */ | |
520 this.toggle_checkboxes = function () { if(this._data.core.themes.checkboxes) { this.hide_checkboxes(); } else { this.show_checkboxes(); } }; | |
521 /** | |
522 * checks if a node is in an undetermined state | |
523 * @name is_undetermined(obj) | |
524 * @param {mixed} obj | |
525 * @return {Boolean} | |
526 */ | |
527 this.is_undetermined = function (obj) { | |
528 obj = this.get_node(obj); | |
529 var s = this.settings.checkbox.cascade, i, j, t = this.settings.checkbox.tie_selection, d = this._data[ t ? 'core' : 'checkbox' ].selected, m = this._model.data; | |
530 if(!obj || obj.state[ t ? 'selected' : 'checked' ] === true || s.indexOf('undetermined') === -1 || (s.indexOf('down') === -1 && s.indexOf('up') === -1)) { | |
531 return false; | |
532 } | |
533 if(!obj.state.loaded && obj.original.state.undetermined === true) { | |
534 return true; | |
535 } | |
536 for(i = 0, j = obj.children_d.length; i < j; i++) { | |
537 if($.inArray(obj.children_d[i], d) !== -1 || (!m[obj.children_d[i]].state.loaded && m[obj.children_d[i]].original.state.undetermined)) { | |
538 return true; | |
539 } | |
540 } | |
541 return false; | |
542 }; | |
543 /** | |
544 * disable a node's checkbox | |
545 * @name disable_checkbox(obj) | |
546 * @param {mixed} obj an array can be used too | |
547 * @trigger disable_checkbox.jstree | |
548 * @plugin checkbox | |
549 */ | |
550 this.disable_checkbox = function (obj) { | |
551 var t1, t2, dom; | |
552 if($.isArray(obj)) { | |
553 obj = obj.slice(); | |
554 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { | |
555 this.disable_checkbox(obj[t1]); | |
556 } | |
557 return true; | |
558 } | |
559 obj = this.get_node(obj); | |
560 if(!obj || obj.id === $.jstree.root) { | |
561 return false; | |
562 } | |
563 dom = this.get_node(obj, true); | |
564 if(!obj.state.checkbox_disabled) { | |
565 obj.state.checkbox_disabled = true; | |
566 if(dom && dom.length) { | |
567 dom.children('.jstree-anchor').children('.jstree-checkbox').addClass('jstree-checkbox-disabled'); | |
568 } | |
569 /** | |
570 * triggered when an node's checkbox is disabled | |
571 * @event | |
572 * @name disable_checkbox.jstree | |
573 * @param {Object} node | |
574 * @plugin checkbox | |
575 */ | |
576 this.trigger('disable_checkbox', { 'node' : obj }); | |
577 } | |
578 }; | |
579 /** | |
580 * enable a node's checkbox | |
581 * @name disable_checkbox(obj) | |
582 * @param {mixed} obj an array can be used too | |
583 * @trigger enable_checkbox.jstree | |
584 * @plugin checkbox | |
585 */ | |
586 this.enable_checkbox = function (obj) { | |
587 var t1, t2, dom; | |
588 if($.isArray(obj)) { | |
589 obj = obj.slice(); | |
590 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { | |
591 this.enable_checkbox(obj[t1]); | |
592 } | |
593 return true; | |
594 } | |
595 obj = this.get_node(obj); | |
596 if(!obj || obj.id === $.jstree.root) { | |
597 return false; | |
598 } | |
599 dom = this.get_node(obj, true); | |
600 if(obj.state.checkbox_disabled) { | |
601 obj.state.checkbox_disabled = false; | |
602 if(dom && dom.length) { | |
603 dom.children('.jstree-anchor').children('.jstree-checkbox').removeClass('jstree-checkbox-disabled'); | |
604 } | |
605 /** | |
606 * triggered when an node's checkbox is enabled | |
607 * @event | |
608 * @name enable_checkbox.jstree | |
609 * @param {Object} node | |
610 * @plugin checkbox | |
611 */ | |
612 this.trigger('enable_checkbox', { 'node' : obj }); | |
613 } | |
614 }; | |
615 | |
616 this.activate_node = function (obj, e) { | |
617 if($(e.target).hasClass('jstree-checkbox-disabled')) { | |
618 return false; | |
619 } | |
620 if(this.settings.checkbox.tie_selection && (this.settings.checkbox.whole_node || $(e.target).hasClass('jstree-checkbox'))) { | |
621 e.ctrlKey = true; | |
622 } | |
623 if(this.settings.checkbox.tie_selection || (!this.settings.checkbox.whole_node && !$(e.target).hasClass('jstree-checkbox'))) { | |
624 return parent.activate_node.call(this, obj, e); | |
625 } | |
626 if(this.is_disabled(obj)) { | |
627 return false; | |
628 } | |
629 if(this.is_checked(obj)) { | |
630 this.uncheck_node(obj, e); | |
631 } | |
632 else { | |
633 this.check_node(obj, e); | |
634 } | |
635 this.trigger('activate_node', { 'node' : this.get_node(obj) }); | |
636 }; | |
637 | |
638 /** | |
639 * Cascades checked state to a node and all its descendants. This function does NOT affect hidden and disabled nodes (or their descendants). | |
640 * However if these unaffected nodes are already selected their ids will be included in the returned array. | |
641 * @private | |
642 * @param {string} id the node ID | |
643 * @param {bool} checkedState should the nodes be checked or not | |
644 * @returns {Array} Array of all node id's (in this tree branch) that are checked. | |
645 */ | |
646 this._cascade_new_checked_state = function (id, checkedState) { | |
647 var self = this; | |
648 var t = this.settings.checkbox.tie_selection; | |
649 var node = this._model.data[id]; | |
650 var selectedNodeIds = []; | |
651 var selectedChildrenIds = [], i, j, selectedChildIds; | |
652 | |
653 if ( | |
654 (this.settings.checkbox.cascade_to_disabled || !node.state.disabled) && | |
655 (this.settings.checkbox.cascade_to_hidden || !node.state.hidden) | |
656 ) { | |
657 //First try and check/uncheck the children | |
658 if (node.children) { | |
659 for (i = 0, j = node.children.length; i < j; i++) { | |
660 var childId = node.children[i]; | |
661 selectedChildIds = self._cascade_new_checked_state(childId, checkedState); | |
662 selectedNodeIds = selectedNodeIds.concat(selectedChildIds); | |
663 if (selectedChildIds.indexOf(childId) > -1) { | |
664 selectedChildrenIds.push(childId); | |
665 } | |
666 } | |
667 } | |
668 | |
669 var dom = self.get_node(node, true); | |
670 | |
671 //A node's state is undetermined if some but not all of it's children are checked/selected . | |
672 var undetermined = selectedChildrenIds.length > 0 && selectedChildrenIds.length < node.children.length; | |
673 | |
674 if(node.original && node.original.state && node.original.state.undetermined) { | |
675 node.original.state.undetermined = undetermined; | |
676 } | |
677 | |
678 //If a node is undetermined then remove selected class | |
679 if (undetermined) { | |
680 node.state[ t ? 'selected' : 'checked' ] = false; | |
681 dom.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked'); | |
682 } | |
683 //Otherwise, if the checkedState === true (i.e. the node is being checked now) and all of the node's children are checked (if it has any children), | |
684 //check the node and style it correctly. | |
685 else if (checkedState && selectedChildrenIds.length === node.children.length) { | |
686 node.state[ t ? 'selected' : 'checked' ] = checkedState; | |
687 selectedNodeIds.push(node.id); | |
688 | |
689 dom.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked'); | |
690 } | |
691 else { | |
692 node.state[ t ? 'selected' : 'checked' ] = false; | |
693 dom.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked'); | |
694 } | |
695 } | |
696 else { | |
697 selectedChildIds = this.get_checked_descendants(id); | |
698 | |
699 if (node.state[ t ? 'selected' : 'checked' ]) { | |
700 selectedChildIds.push(node.id); | |
701 } | |
702 | |
703 selectedNodeIds = selectedNodeIds.concat(selectedChildIds); | |
704 } | |
705 | |
706 return selectedNodeIds; | |
707 }; | |
708 | |
709 /** | |
710 * Gets ids of nodes selected in branch (of tree) specified by id (does not include the node specified by id) | |
711 * @name get_checked_descendants(obj) | |
712 * @param {string} id the node ID | |
713 * @return {Array} array of IDs | |
714 * @plugin checkbox | |
715 */ | |
716 this.get_checked_descendants = function (id) { | |
717 var self = this; | |
718 var t = self.settings.checkbox.tie_selection; | |
719 var node = self._model.data[id]; | |
720 | |
721 return node.children_d.filter(function(_id) { | |
722 return self._model.data[_id].state[ t ? 'selected' : 'checked' ]; | |
723 }); | |
724 }; | |
725 | |
726 /** | |
727 * check a node (only if tie_selection in checkbox settings is false, otherwise select_node will be called internally) | |
728 * @name check_node(obj) | |
729 * @param {mixed} obj an array can be used to check multiple nodes | |
730 * @trigger check_node.jstree | |
731 * @plugin checkbox | |
732 */ | |
733 this.check_node = function (obj, e) { | |
734 if(this.settings.checkbox.tie_selection) { return this.select_node(obj, false, true, e); } | |
735 var dom, t1, t2, th; | |
736 if($.isArray(obj)) { | |
737 obj = obj.slice(); | |
738 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { | |
739 this.check_node(obj[t1], e); | |
740 } | |
741 return true; | |
742 } | |
743 obj = this.get_node(obj); | |
744 if(!obj || obj.id === $.jstree.root) { | |
745 return false; | |
746 } | |
747 dom = this.get_node(obj, true); | |
748 if(!obj.state.checked) { | |
749 obj.state.checked = true; | |
750 this._data.checkbox.selected.push(obj.id); | |
751 if(dom && dom.length) { | |
752 dom.children('.jstree-anchor').addClass('jstree-checked'); | |
753 } | |
754 /** | |
755 * triggered when an node is checked (only if tie_selection in checkbox settings is false) | |
756 * @event | |
757 * @name check_node.jstree | |
758 * @param {Object} node | |
759 * @param {Array} selected the current selection | |
760 * @param {Object} event the event (if any) that triggered this check_node | |
761 * @plugin checkbox | |
762 */ | |
763 this.trigger('check_node', { 'node' : obj, 'selected' : this._data.checkbox.selected, 'event' : e }); | |
764 } | |
765 }; | |
766 /** | |
767 * uncheck a node (only if tie_selection in checkbox settings is false, otherwise deselect_node will be called internally) | |
768 * @name uncheck_node(obj) | |
769 * @param {mixed} obj an array can be used to uncheck multiple nodes | |
770 * @trigger uncheck_node.jstree | |
771 * @plugin checkbox | |
772 */ | |
773 this.uncheck_node = function (obj, e) { | |
774 if(this.settings.checkbox.tie_selection) { return this.deselect_node(obj, false, e); } | |
775 var t1, t2, dom; | |
776 if($.isArray(obj)) { | |
777 obj = obj.slice(); | |
778 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { | |
779 this.uncheck_node(obj[t1], e); | |
780 } | |
781 return true; | |
782 } | |
783 obj = this.get_node(obj); | |
784 if(!obj || obj.id === $.jstree.root) { | |
785 return false; | |
786 } | |
787 dom = this.get_node(obj, true); | |
788 if(obj.state.checked) { | |
789 obj.state.checked = false; | |
790 this._data.checkbox.selected = $.vakata.array_remove_item(this._data.checkbox.selected, obj.id); | |
791 if(dom.length) { | |
792 dom.children('.jstree-anchor').removeClass('jstree-checked'); | |
793 } | |
794 /** | |
795 * triggered when an node is unchecked (only if tie_selection in checkbox settings is false) | |
796 * @event | |
797 * @name uncheck_node.jstree | |
798 * @param {Object} node | |
799 * @param {Array} selected the current selection | |
800 * @param {Object} event the event (if any) that triggered this uncheck_node | |
801 * @plugin checkbox | |
802 */ | |
803 this.trigger('uncheck_node', { 'node' : obj, 'selected' : this._data.checkbox.selected, 'event' : e }); | |
804 } | |
805 }; | |
806 | |
807 /** | |
808 * checks all nodes in the tree (only if tie_selection in checkbox settings is false, otherwise select_all will be called internally) | |
809 * @name check_all() | |
810 * @trigger check_all.jstree, changed.jstree | |
811 * @plugin checkbox | |
812 */ | |
813 this.check_all = function () { | |
814 if(this.settings.checkbox.tie_selection) { return this.select_all(); } | |
815 var tmp = this._data.checkbox.selected.concat([]), i, j; | |
816 this._data.checkbox.selected = this._model.data[$.jstree.root].children_d.concat(); | |
817 for(i = 0, j = this._data.checkbox.selected.length; i < j; i++) { | |
818 if(this._model.data[this._data.checkbox.selected[i]]) { | |
819 this._model.data[this._data.checkbox.selected[i]].state.checked = true; | |
820 } | |
821 } | |
822 this.redraw(true); | |
823 /** | |
824 * triggered when all nodes are checked (only if tie_selection in checkbox settings is false) | |
825 * @event | |
826 * @name check_all.jstree | |
827 * @param {Array} selected the current selection | |
828 * @plugin checkbox | |
829 */ | |
830 this.trigger('check_all', { 'selected' : this._data.checkbox.selected }); | |
831 }; | |
832 /** | |
833 * uncheck all checked nodes (only if tie_selection in checkbox settings is false, otherwise deselect_all will be called internally) | |
834 * @name uncheck_all() | |
835 * @trigger uncheck_all.jstree | |
836 * @plugin checkbox | |
837 */ | |
838 this.uncheck_all = function () { | |
839 if(this.settings.checkbox.tie_selection) { return this.deselect_all(); } | |
840 var tmp = this._data.checkbox.selected.concat([]), i, j; | |
841 for(i = 0, j = this._data.checkbox.selected.length; i < j; i++) { | |
842 if(this._model.data[this._data.checkbox.selected[i]]) { | |
843 this._model.data[this._data.checkbox.selected[i]].state.checked = false; | |
844 } | |
845 } | |
846 this._data.checkbox.selected = []; | |
847 this.element.find('.jstree-checked').removeClass('jstree-checked'); | |
848 /** | |
849 * triggered when all nodes are unchecked (only if tie_selection in checkbox settings is false) | |
850 * @event | |
851 * @name uncheck_all.jstree | |
852 * @param {Object} node the previous selection | |
853 * @param {Array} selected the current selection | |
854 * @plugin checkbox | |
855 */ | |
856 this.trigger('uncheck_all', { 'selected' : this._data.checkbox.selected, 'node' : tmp }); | |
857 }; | |
858 /** | |
859 * checks if a node is checked (if tie_selection is on in the settings this function will return the same as is_selected) | |
860 * @name is_checked(obj) | |
861 * @param {mixed} obj | |
862 * @return {Boolean} | |
863 * @plugin checkbox | |
864 */ | |
865 this.is_checked = function (obj) { | |
866 if(this.settings.checkbox.tie_selection) { return this.is_selected(obj); } | |
867 obj = this.get_node(obj); | |
868 if(!obj || obj.id === $.jstree.root) { return false; } | |
869 return obj.state.checked; | |
870 }; | |
871 /** | |
872 * get an array of all checked nodes (if tie_selection is on in the settings this function will return the same as get_selected) | |
873 * @name get_checked([full]) | |
874 * @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned | |
875 * @return {Array} | |
876 * @plugin checkbox | |
877 */ | |
878 this.get_checked = function (full) { | |
879 if(this.settings.checkbox.tie_selection) { return this.get_selected(full); } | |
880 return full ? $.map(this._data.checkbox.selected, $.proxy(function (i) { return this.get_node(i); }, this)) : this._data.checkbox.selected; | |
881 }; | |
882 /** | |
883 * get an array of all top level checked nodes (ignoring children of checked nodes) (if tie_selection is on in the settings this function will return the same as get_top_selected) | |
884 * @name get_top_checked([full]) | |
885 * @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned | |
886 * @return {Array} | |
887 * @plugin checkbox | |
888 */ | |
889 this.get_top_checked = function (full) { | |
890 if(this.settings.checkbox.tie_selection) { return this.get_top_selected(full); } | |
891 var tmp = this.get_checked(true), | |
892 obj = {}, i, j, k, l; | |
893 for(i = 0, j = tmp.length; i < j; i++) { | |
894 obj[tmp[i].id] = tmp[i]; | |
895 } | |
896 for(i = 0, j = tmp.length; i < j; i++) { | |
897 for(k = 0, l = tmp[i].children_d.length; k < l; k++) { | |
898 if(obj[tmp[i].children_d[k]]) { | |
899 delete obj[tmp[i].children_d[k]]; | |
900 } | |
901 } | |
902 } | |
903 tmp = []; | |
904 for(i in obj) { | |
905 if(obj.hasOwnProperty(i)) { | |
906 tmp.push(i); | |
907 } | |
908 } | |
909 return full ? $.map(tmp, $.proxy(function (i) { return this.get_node(i); }, this)) : tmp; | |
910 }; | |
911 /** | |
912 * get an array of all bottom level checked nodes (ignoring selected parents) (if tie_selection is on in the settings this function will return the same as get_bottom_selected) | |
913 * @name get_bottom_checked([full]) | |
914 * @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned | |
915 * @return {Array} | |
916 * @plugin checkbox | |
917 */ | |
918 this.get_bottom_checked = function (full) { | |
919 if(this.settings.checkbox.tie_selection) { return this.get_bottom_selected(full); } | |
920 var tmp = this.get_checked(true), | |
921 obj = [], i, j; | |
922 for(i = 0, j = tmp.length; i < j; i++) { | |
923 if(!tmp[i].children.length) { | |
924 obj.push(tmp[i].id); | |
925 } | |
926 } | |
927 return full ? $.map(obj, $.proxy(function (i) { return this.get_node(i); }, this)) : obj; | |
928 }; | |
929 this.load_node = function (obj, callback) { | |
930 var k, l, i, j, c, tmp; | |
931 if(!$.isArray(obj) && !this.settings.checkbox.tie_selection) { | |
932 tmp = this.get_node(obj); | |
933 if(tmp && tmp.state.loaded) { | |
934 for(k = 0, l = tmp.children_d.length; k < l; k++) { | |
935 if(this._model.data[tmp.children_d[k]].state.checked) { | |
936 c = true; | |
937 this._data.checkbox.selected = $.vakata.array_remove_item(this._data.checkbox.selected, tmp.children_d[k]); | |
938 } | |
939 } | |
940 } | |
941 } | |
942 return parent.load_node.apply(this, arguments); | |
943 }; | |
944 this.get_state = function () { | |
945 var state = parent.get_state.apply(this, arguments); | |
946 if(this.settings.checkbox.tie_selection) { return state; } | |
947 state.checkbox = this._data.checkbox.selected.slice(); | |
948 return state; | |
949 }; | |
950 this.set_state = function (state, callback) { | |
951 var res = parent.set_state.apply(this, arguments); | |
952 if(res && state.checkbox) { | |
953 if(!this.settings.checkbox.tie_selection) { | |
954 this.uncheck_all(); | |
955 var _this = this; | |
956 $.each(state.checkbox, function (i, v) { | |
957 _this.check_node(v); | |
958 }); | |
959 } | |
960 delete state.checkbox; | |
961 this.set_state(state, callback); | |
962 return false; | |
963 } | |
964 return res; | |
965 }; | |
966 this.refresh = function (skip_loading, forget_state) { | |
967 if(!this.settings.checkbox.tie_selection) { | |
968 this._data.checkbox.selected = []; | |
969 } | |
970 return parent.refresh.apply(this, arguments); | |
971 }; | |
972 }; | |
973 | |
974 // include the checkbox plugin by default | |
975 // $.jstree.defaults.plugins.push("checkbox"); | |
976 })); |