}(function ($, undefined) {
"use strict";
/*!
- * jsTree 3.3.3
+ * jsTree 3.3.4
* http://jstree.com/
*
* Copyright (c) 2014 Ivan Bozhanov (http://vakata.com)
* specifies the jstree version in use
* @name $.jstree.version
*/
- version : '3.3.3',
+ version : '3.3.4',
/**
* holds all the default options used when creating new instances
* @name $.jstree.defaults
* $('#tree').jstree({
* 'core' : {
* 'check_callback' : function (operation, node, node_parent, node_position, more) {
- * // operation can be 'create_node', 'rename_node', 'delete_node', 'move_node' or 'copy_node'
+ * // operation can be 'create_node', 'rename_node', 'delete_node', 'move_node', 'copy_node' or 'edit'
* // in case of 'rename_node' node_position is filled with the new node name
* return operation === 'rename_node' ? true : false;
* }
.remove();
this.element.html("<"+"ul class='jstree-container-ul jstree-children' role='group'><"+"li id='j"+this._id+"_loading' class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='tree-item'><i class='jstree-icon jstree-ocl'></i><"+"a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>" + this.get_string("Loading ...") + "</a></li></ul>");
this.element.attr('aria-activedescendant','j' + this._id + '_loading');
- this._data.core.li_height = this.get_container_ul().children("li").first().height() || 24;
+ this._data.core.li_height = this.get_container_ul().children("li").first().outerHeight() || 24;
this._data.core.node = this._create_prototype_node();
/**
* triggered after the loading text is shown and before loading starts
* @param {Boolean} keep_html if not set to `true` the container will be emptied, otherwise the current DOM elements will be kept intact
*/
destroy : function (keep_html) {
+ /**
+ * triggered before the tree is destroyed
+ * @event
+ * @name destroy.jstree
+ */
+ this.trigger("destroy");
if(this._wrk) {
try {
window.URL.revokeObjectURL(this._wrk);
return callback.call(this, false);
}, this))
.fail($.proxy(function (f) {
- callback.call(this, false);
this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id, 'xhr' : f }) };
+ callback.call(this, false);
this.settings.core.error.call(this, this._data.core.last_error);
}, this));
}
- t = ($.isArray(s) || $.isPlainObject(s)) ? JSON.parse(JSON.stringify(s)) : s;
+ if ($.isArray(s)) {
+ t = $.extend(true, [], s);
+ } else if ($.isPlainObject(s)) {
+ t = $.extend(true, {}, s);
+ } else {
+ t = s;
+ }
if(obj.id === $.jstree.root) {
return this._append_json_data(obj, t, function (status) {
callback.call(this, status);
*/
set_state : function (state, callback) {
if(state) {
+ if(state.core && state.core.selected && state.core.initial_selection === undefined) {
+ state.core.initial_selection = this._data.core.selected.concat([]).sort().join(',');
+ }
if(state.core) {
var res, n, t, _this, i;
if(state.core.open) {
}
if(state.core.selected) {
_this = this;
- this.deselect_all();
- $.each(state.core.selected, function (i, v) {
- _this.select_node(v, false, true);
- });
+ if (state.core.initial_selection === undefined ||
+ state.core.initial_selection === this._data.core.selected.concat([]).sort().join(',')
+ ) {
+ this.deselect_all();
+ $.each(state.core.selected, function (i, v) {
+ _this.select_node(v, false, true);
+ });
+ }
+ delete state.core.initial_selection;
delete state.core.selected;
this.set_state(state, callback);
return false;
'li_attr' : $.extend(true, {}, obj.li_attr),
'a_attr' : $.extend(true, {}, obj.a_attr),
'state' : {},
- 'data' : options && options.no_data ? false : $.extend(true, {}, obj.data)
+ 'data' : options && options.no_data ? false : $.extend(true, $.isArray(obj.data)?[]:{}, obj.data)
//( this.get_node(obj, true).length ? this.get_node(obj, true).data() : obj.data ),
}, i, j;
if(options && options.flat) {
return this.load_node(par, function () { this.create_node(par, node, pos, callback, true); });
}
if(!node) { node = { "text" : this.get_string('New node') }; }
- if(typeof node === "string") { node = { "text" : node }; }
+ if(typeof node === "string") {
+ node = { "text" : node };
+ } else {
+ node = $.extend(true, {}, node);
+ }
if(node.text === undefined) { node.text = this.get_string('New node'); }
var tmp, dpc, i, j;
par.children = tmp;
this.redraw_node(par, true);
- if(callback) { callback.call(this, this.get_node(node)); }
/**
* triggered when a node is created
* @event
* @param {Number} position the position of the new node among the parent's children
*/
this.trigger('create_node', { "node" : this.get_node(node), "parent" : par.id, "position" : pos });
+ if(callback) { callback.call(this, this.get_node(node)); }
return node.id;
},
/**
var rtl, w, a, s, t, h1, h2, fn, tmp, cancel = false;
obj = this.get_node(obj);
if(!obj) { return false; }
- if(this.settings.core.check_callback === false) {
- this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_07', 'reason' : 'Could not edit node because of check_callback' };
+ if(!this.check("edit", obj, this.get_parent(obj))) {
this.settings.core.error.call(this, this._data.core.last_error);
return false;
}
* @name $.jstree.defaults.checkbox.tie_selection
* @plugin checkbox
*/
- tie_selection : true
+ tie_selection : true,
+
+ /**
+ * This setting controls if cascading down affects disabled checkboxes
+ * @name $.jstree.defaults.checkbox.cascade_to_disabled
+ * @plugin checkbox
+ */
+ cascade_to_disabled : true,
+
+ /**
+ * This setting controls if cascading down affects hidden checkboxes
+ * @name $.jstree.defaults.checkbox.cascade_to_hidden
+ * @plugin checkbox
+ */
+ cascade_to_hidden : true
};
$.jstree.plugins.checkbox = function (options, parent) {
this.bind = function () {
for(i = 0, j = dpc.length; i < j; i++) {
m[dpc[i]].state[ t ? 'selected' : 'checked' ] = true;
}
+
this._data[ t ? 'core' : 'checkbox' ].selected = this._data[ t ? 'core' : 'checkbox' ].selected.concat(dpc);
}
else {
this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_unique(this._data[ t ? 'core' : 'checkbox' ].selected);
}, this))
.on(this.settings.checkbox.tie_selection ? 'select_node.jstree' : 'check_node.jstree', $.proxy(function (e, data) {
- var obj = data.node,
+ var self = this,
+ obj = data.node,
m = this._model.data,
par = this.get_node(obj.parent),
- dom = this.get_node(obj, true),
i, j, c, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection,
sel = {}, cur = this._data[ t ? 'core' : 'checkbox' ].selected;
for (i = 0, j = cur.length; i < j; i++) {
sel[cur[i]] = true;
}
+
// apply down
if(s.indexOf('down') !== -1) {
//this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_unique(this._data[ t ? 'core' : 'checkbox' ].selected.concat(obj.children_d));
- for(i = 0, j = obj.children_d.length; i < j; i++) {
- sel[obj.children_d[i]] = true;
- tmp = m[obj.children_d[i]];
- tmp.state[ t ? 'selected' : 'checked' ] = true;
- if(tmp && tmp.original && tmp.original.state && tmp.original.state.undetermined) {
- tmp.original.state.undetermined = false;
- }
- }
+ var selectedIds = this._cascade_new_checked_state(obj.id, true);
+ obj.children_d.concat(obj.id).forEach(function(id) {
+ if (selectedIds.indexOf(id) > -1) {
+ sel[id] = true;
+ }
+ else {
+ delete sel[id];
+ }
+ });
}
// apply up
}
}
this._data[ t ? 'core' : 'checkbox' ].selected = cur;
-
- // apply down (process .children separately?)
- if(s.indexOf('down') !== -1 && dom.length) {
- dom.find('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked').parent().attr('aria-selected', true);
- }
}, this))
.on(this.settings.checkbox.tie_selection ? 'deselect_all.jstree' : 'uncheck_all.jstree', $.proxy(function (e, data) {
var obj = this.get_node($.jstree.root),
}
}, this))
.on(this.settings.checkbox.tie_selection ? 'deselect_node.jstree' : 'uncheck_node.jstree', $.proxy(function (e, data) {
- var obj = data.node,
+ var self = this,
+ obj = data.node,
dom = this.get_node(obj, true),
i, j, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection,
- cur = this._data[ t ? 'core' : 'checkbox' ].selected, sel = {};
- if(obj && obj.original && obj.original.state && obj.original.state.undetermined) {
- obj.original.state.undetermined = false;
- }
+ cur = this._data[ t ? 'core' : 'checkbox' ].selected, sel = {},
+ stillSelectedIds = [],
+ allIds = obj.children_d.concat(obj.id);
// apply down
if(s.indexOf('down') !== -1) {
- for(i = 0, j = obj.children_d.length; i < j; i++) {
- tmp = this._model.data[obj.children_d[i]];
- tmp.state[ t ? 'selected' : 'checked' ] = false;
- if(tmp && tmp.original && tmp.original.state && tmp.original.state.undetermined) {
- tmp.original.state.undetermined = false;
- }
- }
+ var selectedIds = this._cascade_new_checked_state(obj.id, false);
+
+ cur = cur.filter(function(id) {
+ return allIds.indexOf(id) === -1 || selectedIds.indexOf(id) > -1;
+ });
}
- // apply up
- if(s.indexOf('up') !== -1) {
+ // only apply up if cascade up is enabled and if this node is not selected
+ // (if all child nodes are disabled and cascade_to_disabled === false then this node will till be selected).
+ if(s.indexOf('up') !== -1 && cur.indexOf(obj.id) === -1) {
for(i = 0, j = obj.parents.length; i < j; i++) {
tmp = this._model.data[obj.parents[i]];
tmp.state[ t ? 'selected' : 'checked' ] = false;
tmp.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');
}
}
+
+ cur = cur.filter(function(id) {
+ return obj.parents.indexOf(id) === -1;
+ });
}
- sel = {};
- for(i = 0, j = cur.length; i < j; i++) {
- // apply down + apply up
- if(
- (s.indexOf('down') === -1 || $.inArray(cur[i], obj.children_d) === -1) &&
- (s.indexOf('up') === -1 || $.inArray(cur[i], obj.parents) === -1)
- ) {
- sel[cur[i]] = true;
- }
- }
- cur = [];
- for (i in sel) {
- if (sel.hasOwnProperty(i)) {
- cur.push(i);
- }
- }
+
this._data[ t ? 'core' : 'checkbox' ].selected = cur;
-
- // apply down (process .children separately?)
- if(s.indexOf('down') !== -1 && dom.length) {
- dom.find('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked').parent().attr('aria-selected', false);
- }
}, this));
}
if(this.settings.checkbox.cascade.indexOf('up') !== -1) {
}, this));
}
};
+
/**
* set the undetermined state where and if necessary. Used internally.
* @private
this.element.find('.jstree-closed').not(':has(.jstree-children)')
.each(function () {
var tmp = tt.get_node(this), tmp2;
+
+ if(!tmp) { return; }
+
if(!tmp.state.loaded) {
if(tmp.original && tmp.original.state && tmp.original.state.undetermined && tmp.original.state.undetermined === true) {
if(o[tmp.id] === undefined && tmp.id !== $.jstree.root) {
this.trigger('activate_node', { 'node' : this.get_node(obj) });
};
+ /**
+ * Unchecks a node and all its descendants. This function does NOT affect hidden and disabled nodes (or their descendants).
+ * However if these unaffected nodes are already selected their ids will be included in the returned array.
+ * @param id
+ * @param checkedState
+ * @returns {Array} Array of all node id's (in this tree branch) that are checked.
+ */
+ this._cascade_new_checked_state = function(id, checkedState) {
+ var self = this;
+ var t = this.settings.checkbox.tie_selection;
+ var node = this._model.data[id];
+ var selectedNodeIds = [];
+ var selectedChildrenIds = [];
+
+ if (
+ (this.settings.checkbox.cascade_to_disabled || !node.state.disabled) &&
+ (this.settings.checkbox.cascade_to_hidden || !node.state.hidden)
+ ) {
+ //First try and check/uncheck the children
+ if (node.children) {
+ node.children.forEach(function(childId) {
+ var selectedChildIds = self._cascade_new_checked_state(childId, checkedState);
+ selectedNodeIds = selectedNodeIds.concat(selectedChildIds);
+ if (selectedChildIds.indexOf(childId) > -1) {
+ selectedChildrenIds.push(childId);
+ }
+ });
+ }
+
+ var dom = self.get_node(node, true);
+
+ //A node's state is undetermined if some but not all of it's children are checked/selected .
+ var undetermined = selectedChildrenIds.length > 0 && selectedChildrenIds.length < node.children.length;
+
+ if(node.original && node.original.state && node.original.state.undetermined) {
+ node.original.state.undetermined = undetermined;
+ }
+
+ //If a node is undetermined then remove selected class
+ if (undetermined) {
+ node.state[ t ? 'selected' : 'checked' ] = false;
+ dom.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');
+ }
+ //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),
+ //check the node and style it correctly.
+ else if (checkedState && selectedChildrenIds.length === node.children.length) {
+ node.state[ t ? 'selected' : 'checked' ] = checkedState;
+ selectedNodeIds.push(node.id);
+
+ dom.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');
+ }
+ else {
+ node.state[ t ? 'selected' : 'checked' ] = false;
+ dom.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');
+ }
+ }
+ else {
+ var selectedChildIds = this.get_checked_descendants(id);
+
+ if (node.state[ t ? 'selected' : 'checked' ]) {
+ selectedChildIds.push(node.id);
+ }
+
+ selectedNodeIds = selectedNodeIds.concat(selectedChildIds);
+ }
+
+ return selectedNodeIds;
+ };
+
+ /**
+ * Gets ids of nodes selected in branch (of tree) specified by id (does not include the node specified by id)
+ * @param id
+ */
+ this.get_checked_descendants = function(id) {
+ var self = this;
+ var t = self.settings.checkbox.tie_selection;
+ var node = self._model.data[id];
+
+ return node.children_d.filter(function(_id) {
+ return self._model.data[_id].state[ t ? 'selected' : 'checked' ];
+ });
+ };
+
/**
* check a node (only if tie_selection in checkbox settings is false, otherwise select_node will be called internally)
* @name check_node(obj)
this.trigger('uncheck_node', { 'node' : obj, 'selected' : this._data.checkbox.selected, 'event' : e });
}
};
+
/**
* checks all nodes in the tree (only if tie_selection in checkbox settings is false, otherwise select_all will be called internally)
* @name check_all()
// include the checkbox plugin by default
// $.jstree.defaults.plugins.push("checkbox");
+
/**
* ### Conditionalselect plugin
*
var inst = $.jstree.reference(data.reference),
obj = inst.get_node(data.reference);
inst.create_node(obj, {}, "last", function (new_node) {
- setTimeout(function () { inst.edit(new_node); },0);
+ try {
+ inst.edit(new_node);
+ } catch (ex) {
+ setTimeout(function () { inst.edit(new_node); },0);
+ }
});
}
},
var last_ts = 0, cto = null, ex, ey;
this.element
+ .on("init.jstree loading.jstree ready.jstree", $.proxy(function () {
+ this.get_container_ul().addClass('jstree-contextmenu');
+ }, this))
.on("contextmenu.jstree", ".jstree-anchor", $.proxy(function (e, data) {
if (e.target.tagName.toLowerCase() === 'input') {
return;
$(document)
.on("mousedown.vakata.jstree", function (e) {
- if(vakata_context.is_visible && !$.contains(vakata_context.element[0], e.target)) {
+ if(vakata_context.is_visible && vakata_context.element[0] !== e.target && !$.contains(vakata_context.element[0], e.target)) {
$.vakata.context.hide();
}
})
marker.appendTo('body'); //.show();
})
.on('dnd_move.vakata.jstree', function (e, data) {
+ var isDifferentNode = data.event.target !== lastev.target;
if(opento) {
- if (!data.event || data.event.type !== 'dragover' || data.event.target !== lastev.target) {
+ if (!data.event || data.event.type !== 'dragover' || isDifferentNode) {
clearTimeout(opento);
}
}
}
}
if(v === 'i' && ref.parent().is('.jstree-closed') && ins.settings.dnd.open_timeout) {
- opento = setTimeout((function (x, z) { return function () { x.open_node(z); }; }(ins, ref)), ins.settings.dnd.open_timeout);
+ if (!data.event || data.event.type !== 'dragover' || isDifferentNode) {
+ if (opento) { clearTimeout(opento); }
+ opento = setTimeout((function (x, z) { return function () { x.open_node(z); }; }(ins, ref)), ins.settings.dnd.open_timeout);
+ }
}
if(ok) {
pn = ins.get_node(p, true);