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