vendor/assets/javascripts/jstree.js in jstree-rails-4-3.3.3 vs vendor/assets/javascripts/jstree.js in jstree-rails-4-3.3.4
- old
+ new
@@ -11,11 +11,11 @@
factory(jQuery);
}
}(function ($, undefined) {
"use strict";
/*!
- * jsTree 3.3.3
+ * jsTree 3.3.4
* http://jstree.com/
*
* Copyright (c) 2014 Ivan Bozhanov (http://vakata.com)
*
* Licensed same as jquery - under the terms of the MIT License
@@ -52,11 +52,11 @@
$.jstree = {
/**
* 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
*/
defaults : {
@@ -336,11 +336,11 @@
* __Examples__
*
* $('#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;
* }
* }
* });
@@ -513,11 +513,11 @@
return this.nodeType === 3 && (!this.nodeValue || /^\s+$/.test(this.nodeValue));
})
.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
* @event
* @name loading.jstree
@@ -529,10 +529,16 @@
* destroy an instance
* @name destroy()
* @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);
this._wrk = null;
}
@@ -1398,16 +1404,22 @@
this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id, 'xhr' : x }) };
this.settings.core.error.call(this, this._data.core.last_error);
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);
});
}
@@ -3384,10 +3396,13 @@
* @param {Function} callback an optional function to execute once the state is restored.
* @trigger set_state.jstree
*/
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(!$.isArray(state.core.open) || !state.core.open.length) {
delete state.core.open;
@@ -3413,14 +3428,19 @@
this.set_state(state, callback);
return false;
}
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;
}
for(i in state) {
@@ -3638,11 +3658,11 @@
'text' : obj.text,
'icon' : this.get_icon(obj),
'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) {
tmp.parent = obj.parent;
}
@@ -3706,11 +3726,15 @@
pos = pos === undefined ? "last" : pos;
if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {
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;
if(par.id === $.jstree.root) {
if(pos === "before") { pos = "first"; }
@@ -3764,20 +3788,20 @@
}
tmp[pos] = node.id;
par.children = tmp;
this.redraw_node(par, true);
- if(callback) { callback.call(this, this.get_node(node)); }
/**
* triggered when a node is created
* @event
* @name create_node.jstree
* @param {Object} node
* @param {String} parent the parent's ID
* @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;
},
/**
* set the text value of a node
* @name rename_node(obj, val)
@@ -4357,12 +4381,11 @@
*/
edit : function (obj, default_text, callback) {
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;
}
tmp = obj;
default_text = typeof default_text === 'string' ? default_text : obj.text;
@@ -4913,11 +4936,25 @@
/**
* 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.
* @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 () {
parent.bind.call(this);
this._data.checkbox.uto = false;
@@ -4974,10 +5011,11 @@
// apply down
if(p.state[ t ? 'selected' : 'checked' ]) {
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 {
for(i = 0, j = dpc.length; i < j; i++) {
if(m[dpc[i]].state[ t ? 'selected' : 'checked' ]) {
@@ -5022,31 +5060,33 @@
}
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
if(s.indexOf('up') !== -1) {
while(par && par.id !== $.jstree.root) {
@@ -5075,15 +5115,10 @@
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').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),
m = this._model.data,
i, j, tmp;
@@ -5093,31 +5128,30 @@
tmp.original.state.undetermined = false;
}
}
}, 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;
if(tmp && tmp.original && tmp.original.state && tmp.original.state.undetermined) {
tmp.original.state.undetermined = false;
@@ -5125,33 +5159,17 @@
tmp = this.get_node(obj.parents[i], true);
if(tmp && tmp.length) {
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.element
.on('delete_node.jstree', $.proxy(function (e, data) {
@@ -5238,10 +5256,11 @@
p = this.get_node(p.parent);
}
}, this));
}
};
+
/**
* set the undetermined state where and if necessary. Used internally.
* @private
* @name _undetermined()
* @plugin checkbox
@@ -5264,10 +5283,13 @@
}
// attempt for server side undetermined state
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) {
o[tmp.id] = true;
p.push(tmp.id);
@@ -5466,10 +5488,93 @@
}
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)
* @param {mixed} obj an array can be used to check multiple nodes
* @trigger check_node.jstree
* @plugin checkbox
@@ -5545,10 +5650,11 @@
* @plugin checkbox
*/
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()
* @trigger check_all.jstree, changed.jstree
* @plugin checkbox
@@ -5715,10 +5821,11 @@
};
// include the checkbox plugin by default
// $.jstree.defaults.plugins.push("checkbox");
+
/**
* ### Conditionalselect plugin
*
* This plugin allows defining a callback to allow or deny node selection by user input (activate node method).
*/
@@ -5791,11 +5898,15 @@
"label" : "Create",
"action" : function (data) {
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);
+ }
});
}
},
"rename" : {
"separator_before" : false,
@@ -5892,10 +6003,13 @@
this.bind = function () {
parent.bind.call(this);
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;
}
e.preventDefault();
@@ -6358,11 +6472,11 @@
}
});
$(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();
}
})
.on("context_show.vakata.jstree", function (e, data) {
vakata_context.element.find("li:has(ul)").children("a").addClass("vakata-context-parent");
@@ -6549,12 +6663,13 @@
lastev = false;
if(!data || !data.data || !data.data.jstree) { return; }
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(!data || !data.data || !data.data.jstree) { return; }
@@ -6649,10 +6764,13 @@
if(ins && ins.last_error) { laster = ins.last_error(); }
break;
}
}
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);
if (!pn.hasClass('.jstree-dnd-parent')) {
$('.jstree-dnd-parent').removeClass('jstree-dnd-parent');
\ No newline at end of file