vendor/assets/javascripts/jstree.js in jstree-rails-4-3.2.1 vs vendor/assets/javascripts/jstree.js in jstree-rails-4-3.3.3

- old
+ new

@@ -11,22 +11,23 @@ factory(jQuery); } }(function ($, undefined) { "use strict"; /*! - * jsTree 3.2.1 + * jsTree 3.3.3 * http://jstree.com/ * * Copyright (c) 2014 Ivan Bozhanov (http://vakata.com) * * Licensed same as jquery - under the terms of the MIT License * http://www.opensource.org/licenses/mit-license.php */ /*! * if using jslint please allow for the jQuery global and use following options: - * jslint: browser: true, ass: true, bitwise: true, continue: true, nomen: true, plusplus: true, regexp: true, unparam: true, todo: true, white: true + * jslint: loopfunc: true, browser: true, ass: true, bitwise: true, continue: true, nomen: true, plusplus: true, regexp: true, unparam: true, todo: true, white: true */ +/*jshint -W083 */ // prevent another load? maybe there is a better way? if($.jstree) { return; } @@ -40,40 +41,22 @@ ccp_node = false, ccp_mode = false, ccp_inst = false, themes_loaded = [], src = $('script:last').attr('src'), - document = window.document, // local variable is always faster to access then a global - _node = document.createElement('LI'), _temp1, _temp2; + document = window.document; // local variable is always faster to access then a global - _node.setAttribute('role', 'treeitem'); - _temp1 = document.createElement('I'); - _temp1.className = 'jstree-icon jstree-ocl'; - _temp1.setAttribute('role', 'presentation'); - _node.appendChild(_temp1); - _temp1 = document.createElement('A'); - _temp1.className = 'jstree-anchor'; - _temp1.setAttribute('href','#'); - _temp1.setAttribute('tabindex','-1'); - _temp2 = document.createElement('I'); - _temp2.className = 'jstree-icon jstree-themeicon'; - _temp2.setAttribute('role', 'presentation'); - _temp1.appendChild(_temp2); - _node.appendChild(_temp1); - _temp1 = _temp2 = null; - - /** * holds all jstree related functions and variables, including the actual class and methods to create, access and manipulate instances. * @name $.jstree */ $.jstree = { /** * specifies the jstree version in use * @name $.jstree.version */ - version : '3.2.1', + version : '3.3.3', /** * holds all the default options used when creating new instances * @name $.jstree.defaults */ defaults : { @@ -90,10 +73,11 @@ plugins : {}, path : src && src.indexOf('/') !== -1 ? src.replace(/\/[^\/]+$/,'') : '', idregex : /[\\:&!^|()\[\]<>@*'+~#";.,=\- \/${}%?`]/g, root : '#' }; + /** * creates a jstree instance * @name $.jstree.create(el [, options]) * @param {DOMElement|jQuery|String} el the element to create the instance on, can be jQuery extended or a selector * @param {Object} options options for this instance (extends `$.jstree.defaults`) @@ -136,11 +120,12 @@ this._data = { core : { themes : { name : false, dots : false, - icons : false + icons : false, + ellipsis : false }, selected : [], last_error : {}, working : false, worker_queue : [], @@ -258,11 +243,11 @@ * }); * * @name $(':jstree') * @return {jQuery} */ - $.expr[':'].jstree = $.expr.createPseudo(function(search) { + $.expr.pseudos.jstree = $.expr.createPseudo(function(search) { return function(a) { return $(a).hasClass('jstree') && $(a).data('jstree') !== undefined; }; }); @@ -307,11 +292,12 @@ * 'text' : 'Root node with options', * 'state' : { 'opened' : true, 'selected' : true }, * 'children' : [ { 'text' : 'Child 1' }, 'Child 2'] * } * ] - * }); + * } + * }); * * // function * $('#tree').jstree({ * 'core' : { * 'data' : function (obj, callback) { @@ -406,10 +392,15 @@ * a boolean indicating if node icons are shown * @name $.jstree.defaults.core.themes.icons */ icons : true, /** + * a boolean indicating if node ellipsis should be shown - this only works with a fixed with on the container + * @name $.jstree.defaults.core.themes.ellipsis + */ + ellipsis : false, + /** * a boolean indicating if the tree background is striped * @name $.jstree.defaults.core.themes.stripes */ stripes : false, /** @@ -523,10 +514,11 @@ }) .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.node = this._create_prototype_node(); /** * triggered after the loading text is shown and before loading starts * @event * @name loading.jstree */ @@ -548,10 +540,33 @@ } if(!keep_html) { this.element.empty(); } this.teardown(); }, /** + * Create prototype node + */ + _create_prototype_node : function () { + var _node = document.createElement('LI'), _temp1, _temp2; + _node.setAttribute('role', 'treeitem'); + _temp1 = document.createElement('I'); + _temp1.className = 'jstree-icon jstree-ocl'; + _temp1.setAttribute('role', 'presentation'); + _node.appendChild(_temp1); + _temp1 = document.createElement('A'); + _temp1.className = 'jstree-anchor'; + _temp1.setAttribute('href','#'); + _temp1.setAttribute('tabindex','-1'); + _temp2 = document.createElement('I'); + _temp2.className = 'jstree-icon jstree-themeicon'; + _temp2.setAttribute('role', 'presentation'); + _temp1.appendChild(_temp2); + _node.appendChild(_temp1); + _temp1 = _temp2 = null; + + return _node; + }, + /** * part of the destroying of an instance. Used internally. * @private * @name teardown() */ teardown : function () { @@ -629,11 +644,11 @@ break; case 13: // enter e.type = "click"; $(e.currentTarget).trigger(e); break; - case 37: // right + case 37: // left e.preventDefault(); if(this.is_open(e.currentTarget)) { this.close_node(e.currentTarget); } else { @@ -644,11 +659,11 @@ case 38: // up e.preventDefault(); o = this.get_prev_dom(e.currentTarget); if(o && o.length) { o.children('.jstree-anchor').focus(); } break; - case 39: // left + case 39: // right e.preventDefault(); if(this.is_closed(e.currentTarget)) { this.open_node(e.currentTarget, function (o) { this.get_node(o, true).children('.jstree-anchor').focus(); }); } else if (this.is_open(e.currentTarget)) { @@ -671,31 +686,27 @@ break; case 35: // end e.preventDefault(); this.element.find('.jstree-anchor').filter(':visible').last().focus(); break; + case 113: // f2 - safe to include - if check_callback is false it will fail + e.preventDefault(); + this.edit(e.currentTarget); + break; + default: + break; /*! // delete case 46: e.preventDefault(); o = this.get_node(e.currentTarget); if(o && o.id && o.id !== $.jstree.root) { o = this.is_selected(o) ? this.get_selected() : o; this.delete_node(o); } break; - // f2 - case 113: - e.preventDefault(); - o = this.get_node(e.currentTarget); - if(o && o.id && o.id !== $.jstree.root) { - // this.edit(o); - } - break; - default: - // console.log(e.which); - break; + */ } }, this)) .on("load_node.jstree", $.proxy(function (e, data) { if(data.status) { @@ -801,17 +812,19 @@ .on("init.jstree", $.proxy(function () { var s = this.settings.core.themes; this._data.core.themes.dots = s.dots; this._data.core.themes.stripes = s.stripes; this._data.core.themes.icons = s.icons; + this._data.core.themes.ellipsis = s.ellipsis; this.set_theme(s.name || "default", s.url); this.set_theme_variant(s.variant); }, this)) .on("loading.jstree", $.proxy(function () { this[ this._data.core.themes.dots ? "show_dots" : "hide_dots" ](); this[ this._data.core.themes.icons ? "show_icons" : "hide_icons" ](); this[ this._data.core.themes.stripes ? "show_stripes" : "hide_stripes" ](); + this[ this._data.core.themes.ellipsis ? "show_ellipsis" : "hide_ellipsis" ](); }, this)) .on('blur.jstree', '.jstree-anchor', $.proxy(function (e) { this._data.core.focused = null; $(e.currentTarget).filter('.jstree-hovered').mouseleave(); this.element.attr('tabindex', '0'); @@ -1189,20 +1202,26 @@ return false; } // if(obj.state.loading) { } // the node is already loading - just wait for it to load and invoke callback? but if called implicitly it should be loaded again? if(obj.state.loaded) { obj.state.loaded = false; + for(i = 0, j = obj.parents.length; i < j; i++) { + this._model.data[obj.parents[i]].children_d = $.vakata.array_filter(this._model.data[obj.parents[i]].children_d, function (v) { + return $.inArray(v, obj.children_d) === -1; + }); + } for(k = 0, l = obj.children_d.length; k < l; k++) { - for(i = 0, j = obj.parents.length; i < j; i++) { - this._model.data[obj.parents[i]].children_d = $.vakata.array_remove_item(this._model.data[obj.parents[i]].children_d, obj.children_d[k]); - } if(this._model.data[obj.children_d[k]].state.selected) { c = true; - this._data.core.selected = $.vakata.array_remove_item(this._data.core.selected, obj.children_d[k]); } delete this._model.data[obj.children_d[k]]; } + if (c) { + this._data.core.selected = $.vakata.array_filter(this._data.core.selected, function (v) { + return $.inArray(v, obj.children_d) === -1; + }); + } obj.children = []; obj.children_d = []; if(c) { this.trigger('changed', { 'action' : 'load_node', 'node' : obj, 'selected' : this._data.core.selected }); } @@ -1220,12 +1239,20 @@ if(m[obj.children[i]] && !m[obj.children[i]].state.hidden) { has_children = true; break; } } - if(obj.state.loaded && !has_children && dom && dom.length && !dom.hasClass('jstree-leaf')) { - dom.removeClass('jstree-closed jstree-open').addClass('jstree-leaf'); + if(obj.state.loaded && dom && dom.length) { + dom.removeClass('jstree-closed jstree-open jstree-leaf'); + if (!has_children) { + dom.addClass('jstree-leaf'); + } + else { + if (obj.id !== '#') { + dom.addClass(obj.state.opened ? 'jstree-open' : 'jstree-closed'); + } + } } dom.removeClass("jstree-loading").attr('aria-busy',false); /** * triggered after a node is loaded * @event @@ -1245,16 +1272,16 @@ * @private * @name _load_nodes(nodes [, callback]) * @param {array} nodes * @param {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives one argument - the array passed to _load_nodes */ - _load_nodes : function (nodes, callback, is_callback) { + _load_nodes : function (nodes, callback, is_callback, force_reload) { var r = true, c = function () { this._load_nodes(nodes, callback, true); }, m = this._model.data, i, j, tmp = []; for(i = 0, j = nodes.length; i < j; i++) { - if(m[nodes[i]] && ( (!m[nodes[i]].state.loaded && !m[nodes[i]].state.failed) || !is_callback)) { + if(m[nodes[i]] && ( (!m[nodes[i]].state.loaded && !m[nodes[i]].state.failed) || (!is_callback && force_reload) )) { if(!this.is_loading(nodes[i])) { this.load_node(nodes[i], c); } r = false; } @@ -1318,10 +1345,13 @@ * @param {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives one argument - a boolean status * @return {Boolean} */ _load_node : function (obj, callback) { var s = this.settings.core.data, t; + var notTextOrCommentNode = function notTextOrCommentNode () { + return this.nodeType !== 3 && this.nodeType !== 8; + }; // use original HTML if(!s) { if(obj.id === $.jstree.root) { return this._append_html_data(obj, this._data.core.original_container_html.clone(true), function (status) { callback.call(this, status); @@ -1335,13 +1365,15 @@ if($.isFunction(s)) { return s.call(this, obj, $.proxy(function (d) { if(d === false) { callback.call(this, false); } - this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $($.parseHTML(d)).filter(function () { return this.nodeType !== 3; }) : d, function (status) { - callback.call(this, status); - }); + else { + this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $($.parseHTML(d)).filter(notTextOrCommentNode) : d, function (status) { + callback.call(this, status); + }); + } // return d === false ? callback.call(this, false) : callback.call(this, this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $(d) : d)); }, this)); } if(typeof s === 'object') { if(s.url) { @@ -1358,11 +1390,11 @@ if((type && type.indexOf('json') !== -1) || typeof d === "object") { return this._append_json_data(obj, d, function (status) { callback.call(this, status); }); //return callback.call(this, this._append_json_data(obj, d)); } if((type && type.indexOf('html') !== -1) || typeof d === "string") { - return this._append_html_data(obj, $($.parseHTML(d)).filter(function () { return this.nodeType !== 3; }), function (status) { callback.call(this, status); }); + return this._append_html_data(obj, $($.parseHTML(d)).filter(notTextOrCommentNode), function (status) { callback.call(this, status); }); // return callback.call(this, this._append_html_data(obj, $(d))); } 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); @@ -1386,11 +1418,11 @@ } //return callback.call(this, (obj.id === $.jstree.root ? this._append_json_data(obj, t) : false) ); } if(typeof s === 'string') { if(obj.id === $.jstree.root) { - return this._append_html_data(obj, $($.parseHTML(s)).filter(function () { return this.nodeType !== 3; }), function (status) { + return this._append_html_data(obj, $($.parseHTML(s)).filter(notTextOrCommentNode), function (status) { callback.call(this, status); }); } else { this._data.core.last_error = { 'error' : 'nodata', 'plugin' : 'core', 'id' : 'core_06', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id }) }; @@ -1773,14 +1805,21 @@ } }, rslt = function (rslt, worker) { if(this.element === null) { return; } this._cnt = rslt.cnt; + var i, m = this._model.data; + for (i in m) { + if (m.hasOwnProperty(i) && m[i].state && m[i].state.loading && rslt.mod[i]) { + rslt.mod[i].state.loading = true; + } + } this._model.data = rslt.mod; // breaks the reference in load_node - careful if(worker) { - var i, j, a = rslt.add, r = rslt.sel, s = this._data.core.selected.slice(), m = this._model.data; + var j, a = rslt.add, r = rslt.sel, s = this._data.core.selected.slice(); + m = this._model.data; // if selection was changed while calculating in worker if(r.length !== s.length || $.vakata.array_unique(r.concat(s)).length !== r.length) { // deselect nodes that are no longer selected for(i = 0, j = r.length; i < j; i++) { if($.inArray(r[i], a) === -1 && $.inArray(r[i], s) === -1) { @@ -2332,11 +2371,11 @@ f = node.children('.jstree-anchor')[0] === document.activeElement; node.remove(); //node = d.createElement('LI'); //node = node[0]; } - node = _node.cloneNode(true); + node = this._data.core.node.cloneNode(true); // node is DOM, deep is boolean c = 'jstree-node '; for(i in obj.li_attr) { if(obj.li_attr.hasOwnProperty(i)) { @@ -2417,11 +2456,11 @@ } else if(obj.icon.indexOf('/') === -1 && obj.icon.indexOf('.') === -1) { node.childNodes[1].childNodes[0].className += ' ' + obj.icon + ' jstree-themeicon-custom'; } else { - node.childNodes[1].childNodes[0].style.backgroundImage = 'url('+obj.icon+')'; + node.childNodes[1].childNodes[0].style.backgroundImage = 'url("'+obj.icon+'")'; node.childNodes[1].childNodes[0].style.backgroundPosition = 'center center'; node.childNodes[1].childNodes[0].style.backgroundSize = 'auto'; node.childNodes[1].childNodes[0].className += ' jstree-themeicon-custom'; } } @@ -2547,11 +2586,13 @@ .children(".jstree-children").css("display","none").end() .removeClass("jstree-closed").addClass("jstree-open").attr("aria-expanded", true) .children(".jstree-children").stop(true, true) .slideDown(animation, function () { this.style.display = ""; - t.trigger("after_open", { "node" : obj }); + if (t.element) { + t.trigger("after_open", { "node" : obj }); + } }); } } obj.state.opened = true; if(callback) { @@ -2628,43 +2669,47 @@ return false; } animation = animation === undefined ? this.settings.core.animation : animation; t = this; d = this.get_node(obj, true); - if(d.length) { - if(!animation) { - d[0].className = d[0].className.replace('jstree-open', 'jstree-closed'); - d.attr("aria-expanded", false).children('.jstree-children').remove(); - } - else { - d - .children(".jstree-children").attr("style","display:block !important").end() - .removeClass("jstree-open").addClass("jstree-closed").attr("aria-expanded", false) - .children(".jstree-children").stop(true, true).slideUp(animation, function () { - this.style.display = ""; - d.children('.jstree-children').remove(); - t.trigger("after_close", { "node" : obj }); - }); - } - } + obj.state.opened = false; /** * triggered when a node is closed (if there is an animation it will not be complete yet) * @event * @name close_node.jstree * @param {Object} node the closed node */ this.trigger('close_node',{ "node" : obj }); - if(!animation || !d.length) { + if(!d.length) { /** * triggered when a node is closed and the animation is complete * @event * @name after_close.jstree * @param {Object} node the closed node */ this.trigger("after_close", { "node" : obj }); } + else { + if(!animation) { + d[0].className = d[0].className.replace('jstree-open', 'jstree-closed'); + d.attr("aria-expanded", false).children('.jstree-children').remove(); + this.trigger("after_close", { "node" : obj }); + } + else { + d + .children(".jstree-children").attr("style","display:block !important").end() + .removeClass("jstree-open").addClass("jstree-closed").attr("aria-expanded", false) + .children(".jstree-children").stop(true, true).slideUp(animation, function () { + this.style.display = ""; + d.children('.jstree-children').remove(); + if (t.element) { + t.trigger("after_close", { "node" : obj }); + } + }); + } + } }, /** * toggles a node - closing it if it is open, opening it if it is closed * @name toggle_node(obj) * @param {mixed} obj the node to toggle @@ -2821,24 +2866,35 @@ * @param {Object} node the disabled node */ this.trigger('disable_node', { 'node' : obj }); }, /** + * determines if a node is hidden + * @name is_hidden(obj) + * @param {mixed} obj the node + */ + is_hidden : function (obj) { + obj = this.get_node(obj); + return obj.state.hidden === true; + }, + /** * hides a node - it is still in the structure but will not be visible * @name hide_node(obj) * @param {mixed} obj the node to hide - * @param {Boolean} redraw internal parameter controlling if redraw is called + * @param {Boolean} skip_redraw internal parameter controlling if redraw is called * @trigger hide_node.jstree */ hide_node : function (obj, skip_redraw) { var t1, t2; if($.isArray(obj)) { obj = obj.slice(); for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { this.hide_node(obj[t1], true); } - this.redraw(); + if (!skip_redraw) { + this.redraw(); + } return true; } obj = this.get_node(obj); if(!obj || obj.id === $.jstree.root) { return false; @@ -2870,11 +2926,13 @@ if($.isArray(obj)) { obj = obj.slice(); for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { this.show_node(obj[t1], true); } - this.redraw(); + if (!skip_redraw) { + this.redraw(); + } return true; } obj = this.get_node(obj); if(!obj || obj.id === $.jstree.root) { return false; @@ -2991,11 +3049,13 @@ } if(p[i] === l) { c = !c; } if(!this.is_disabled(p[i]) && (c || p[i] === o || p[i] === l)) { - this.select_node(p[i], true, false, e); + if (!this.is_hidden(p[i])) { + this.select_node(p[i], true, false, e); + } } else { this.deselect_node(p[i], true, e); } } @@ -3336,11 +3396,11 @@ else { this._load_nodes(state.core.open, function (nodes) { this.open_node(nodes, false, 0); delete state.core.open; this.set_state(state, callback); - }, true); + }); } return false; } if(state.core.scroll) { if(state.core.scroll && state.core.scroll.left !== undefined) { @@ -3446,35 +3506,36 @@ obj = this.get_node(obj); if(!obj || obj.id === $.jstree.root) { return false; } var opened = [], to_load = [], s = this._data.core.selected.concat([]); to_load.push(obj.id); if(obj.state.opened === true) { opened.push(obj.id); } - this.get_node(obj, true).find('.jstree-open').each(function() { opened.push(this.id); }); + this.get_node(obj, true).find('.jstree-open').each(function() { to_load.push(this.id); opened.push(this.id); }); this._load_nodes(to_load, $.proxy(function (nodes) { this.open_node(opened, false, 0); - this.select_node(this._data.core.selected); + this.select_node(s); /** * triggered when a node is refreshed * @event * @name refresh_node.jstree * @param {Object} node - the refreshed node * @param {Array} nodes - an array of the IDs of the nodes that were reloaded */ this.trigger('refresh_node', { 'node' : obj, 'nodes' : nodes }); - }, this)); + }, this), false, true); }, /** * set (change) the ID of a node * @name set_id(obj, id) * @param {mixed} obj the node * @param {String} id the new ID * @return {Boolean} + * @trigger set_id.jstree */ set_id : function (obj, id) { obj = this.get_node(obj); if(!obj || obj.id === $.jstree.root) { return false; } - var i, j, m = this._model.data; + var i, j, m = this._model.data, old = obj.id; id = id.toString(); // update parents (replace current ID with new one in children and children_d) m[obj.parent].children[$.inArray(obj.id, m[obj.parent].children)] = id; for(i = 0, j = obj.parents.length; i < j; i++) { m[obj.parents[i]].children_d[$.inArray(obj.id, m[obj.parents[i]].children_d)] = id; @@ -3489,19 +3550,27 @@ i = $.inArray(obj.id, this._data.core.selected); if(i !== -1) { this._data.core.selected[i] = id; } // update model and obj itself (obj.id, this._model.data[KEY]) i = this.get_node(obj.id, true); if(i) { - i.attr('id', id).children('.jstree-anchor').attr('id', id + '_anchor').end().attr('aria-labelledby', id + '_anchor'); + i.attr('id', id); //.children('.jstree-anchor').attr('id', id + '_anchor').end().attr('aria-labelledby', id + '_anchor'); if(this.element.attr('aria-activedescendant') === obj.id) { this.element.attr('aria-activedescendant', id); } } delete m[obj.id]; obj.id = id; obj.li_attr.id = id; m[id] = obj; + /** + * triggered when a node id value is changed + * @event + * @name set_id.jstree + * @param {Object} node + * @param {String} old the old id + */ + this.trigger('set_id',{ "node" : obj, "new" : obj.id, "old" : old }); return true; }, /** * get the text value of a node * @name get_text(obj) @@ -3553,10 +3622,12 @@ * @param {Object} options * @param {Boolean} options.no_state do not return state information * @param {Boolean} options.no_id do not return ID * @param {Boolean} options.no_children do not include children * @param {Boolean} options.no_data do not include node data + * @param {Boolean} options.no_li_attr do not include LI attributes + * @param {Boolean} options.no_a_attr do not include A attributes * @param {Boolean} options.flat return flat JSON instead of nested * @return {Object} */ get_json : function (obj, options, flat) { obj = this.get_node(obj || $.jstree.root); @@ -3582,11 +3653,19 @@ for(i in obj.state) { if(obj.state.hasOwnProperty(i)) { tmp.state[i] = obj.state[i]; } } + } else { + delete tmp.state; } + if(options && options.no_li_attr) { + delete tmp.li_attr; + } + if(options && options.no_a_attr) { + delete tmp.a_attr; + } if(options && options.no_id) { delete tmp.id; if(tmp.li_attr && tmp.li_attr.id) { delete tmp.li_attr.id; } @@ -3609,11 +3688,11 @@ } return options && options.flat ? flat : (obj.id === $.jstree.root ? tmp.children : tmp); }, /** * create a new node (do not confuse with load_node) - * @name create_node([obj, node, pos, callback, is_loaded]) + * @name create_node([par, node, pos, callback, is_loaded]) * @param {mixed} par the parent node (to create a root node use either "#" (string) or `null`) * @param {mixed} node the data for the new node (a valid JSON object, or a simple string with the name) * @param {mixed} pos the index at which to insert the node, "first" and "last" are also supported, default is "last" * @param {Function} callback a function to be called once the node is created * @param {Boolean} is_loaded internal argument indicating if the parent node was succesfully loaded @@ -3763,25 +3842,26 @@ if(pos !== -1) { par.children = $.vakata.array_remove(par.children, pos); } tmp = obj.children_d.concat([]); tmp.push(obj.id); + for(i = 0, j = obj.parents.length; i < j; i++) { + this._model.data[obj.parents[i]].children_d = $.vakata.array_filter(this._model.data[obj.parents[i]].children_d, function (v) { + return $.inArray(v, tmp) === -1; + }); + } for(k = 0, l = tmp.length; k < l; k++) { - for(i = 0, j = obj.parents.length; i < j; i++) { - pos = $.inArray(tmp[k], this._model.data[obj.parents[i]].children_d); - if(pos !== -1) { - this._model.data[obj.parents[i]].children_d = $.vakata.array_remove(this._model.data[obj.parents[i]].children_d, pos); - } - } if(this._model.data[tmp[k]].state.selected) { c = true; - pos = $.inArray(tmp[k], this._data.core.selected); - if(pos !== -1) { - this._data.core.selected = $.vakata.array_remove(this._data.core.selected, pos); - } + break; } } + if (c) { + this._data.core.selected = $.vakata.array_filter(this._data.core.selected, function (v) { + return $.inArray(v, tmp) === -1; + }); + } /** * triggered when a node is deleted * @event * @name delete_node.jstree * @param {Object} node @@ -3797,11 +3877,13 @@ if($.inArray(this._data.core.focused, tmp) !== -1) { this._data.core.focused = null; top = this.element[0].scrollTop; lft = this.element[0].scrollLeft; if(par.id === $.jstree.root) { - this.get_node(this._model.data[$.jstree.root].children[0], true).children('.jstree-anchor').focus(); + if (this._model.data[$.jstree.root].children[0]) { + this.get_node(this._model.data[$.jstree.root].children[0], true).children('.jstree-anchor').focus(); + } } else { this.get_node(par, true).children('.jstree-anchor').focus(); } this.element[0].scrollTop = top; @@ -3825,11 +3907,11 @@ obj = obj && obj.id ? obj : this.get_node(obj); par = par && par.id ? par : this.get_node(par); var tmp = chk.match(/^move_node|copy_node|create_node$/i) ? par : obj, chc = this.settings.core.check_callback; if(chk === "move_node" || chk === "copy_node") { - if((!more || !more.is_multi) && (obj.id === par.id || $.inArray(obj.id, par.children) === pos || $.inArray(par.id, obj.children_d) !== -1)) { + if((!more || !more.is_multi) && (obj.id === par.id || (chk === "move_node" && $.inArray(obj.id, par.children) === pos) || $.inArray(par.id, obj.children_d) !== -1)) { this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_01', 'reason' : 'Moving parent inside child', 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) }; return false; } } if(tmp && tmp.data) { tmp = tmp.data; } @@ -4340,10 +4422,11 @@ } }, this), 0); if(callback) { callback.call(this, tmp, nv, cancel); } + h2 = null; }, this), "keydown" : function (e) { var key = e.which; if(key === 27) { cancel = true; @@ -4378,10 +4461,15 @@ }; s.attr('class', a.attr('class')).append(a.contents().clone()).append(h2); a.replaceWith(s); h1.css(fn); h2.css(fn).width(Math.min(h1.text("pW" + h2[0].value).width(),w))[0].select(); + $(document).one('mousedown.jstree touchstart.jstree dnd_start.vakata', function (e) { + if (h2 && e.target !== h2) { + $(h2).blur(); + } + }); }, /** * changes the theme @@ -4443,52 +4531,139 @@ get_theme_variant : function () { return this._data.core.themes.variant; }, /** * shows a striped background on the container (if the theme supports it) * @name show_stripes() */ - show_stripes : function () { this._data.core.themes.stripes = true; this.get_container_ul().addClass("jstree-striped"); }, + show_stripes : function () { + this._data.core.themes.stripes = true; + this.get_container_ul().addClass("jstree-striped"); + /** + * triggered when stripes are shown + * @event + * @name show_stripes.jstree + */ + this.trigger('show_stripes'); + }, /** * hides the striped background on the container * @name hide_stripes() */ - hide_stripes : function () { this._data.core.themes.stripes = false; this.get_container_ul().removeClass("jstree-striped"); }, + hide_stripes : function () { + this._data.core.themes.stripes = false; + this.get_container_ul().removeClass("jstree-striped"); + /** + * triggered when stripes are hidden + * @event + * @name hide_stripes.jstree + */ + this.trigger('hide_stripes'); + }, /** * toggles the striped background on the container * @name toggle_stripes() */ toggle_stripes : function () { if(this._data.core.themes.stripes) { this.hide_stripes(); } else { this.show_stripes(); } }, /** * shows the connecting dots (if the theme supports it) * @name show_dots() */ - show_dots : function () { this._data.core.themes.dots = true; this.get_container_ul().removeClass("jstree-no-dots"); }, + show_dots : function () { + this._data.core.themes.dots = true; + this.get_container_ul().removeClass("jstree-no-dots"); + /** + * triggered when dots are shown + * @event + * @name show_dots.jstree + */ + this.trigger('show_dots'); + }, /** * hides the connecting dots * @name hide_dots() */ - hide_dots : function () { this._data.core.themes.dots = false; this.get_container_ul().addClass("jstree-no-dots"); }, + hide_dots : function () { + this._data.core.themes.dots = false; + this.get_container_ul().addClass("jstree-no-dots"); + /** + * triggered when dots are hidden + * @event + * @name hide_dots.jstree + */ + this.trigger('hide_dots'); + }, /** * toggles the connecting dots * @name toggle_dots() */ toggle_dots : function () { if(this._data.core.themes.dots) { this.hide_dots(); } else { this.show_dots(); } }, /** * show the node icons * @name show_icons() */ - show_icons : function () { this._data.core.themes.icons = true; this.get_container_ul().removeClass("jstree-no-icons"); }, + show_icons : function () { + this._data.core.themes.icons = true; + this.get_container_ul().removeClass("jstree-no-icons"); + /** + * triggered when icons are shown + * @event + * @name show_icons.jstree + */ + this.trigger('show_icons'); + }, /** * hide the node icons * @name hide_icons() */ - hide_icons : function () { this._data.core.themes.icons = false; this.get_container_ul().addClass("jstree-no-icons"); }, + hide_icons : function () { + this._data.core.themes.icons = false; + this.get_container_ul().addClass("jstree-no-icons"); + /** + * triggered when icons are hidden + * @event + * @name hide_icons.jstree + */ + this.trigger('hide_icons'); + }, /** * toggle the node icons * @name toggle_icons() */ toggle_icons : function () { if(this._data.core.themes.icons) { this.hide_icons(); } else { this.show_icons(); } }, /** + * show the node ellipsis + * @name show_icons() + */ + show_ellipsis : function () { + this._data.core.themes.ellipsis = true; + this.get_container_ul().addClass("jstree-ellipsis"); + /** + * triggered when ellisis is shown + * @event + * @name show_ellipsis.jstree + */ + this.trigger('show_ellipsis'); + }, + /** + * hide the node ellipsis + * @name hide_ellipsis() + */ + hide_ellipsis : function () { + this._data.core.themes.ellipsis = false; + this.get_container_ul().removeClass("jstree-ellipsis"); + /** + * triggered when ellisis is hidden + * @event + * @name hide_ellipsis.jstree + */ + this.trigger('hide_ellipsis'); + }, + /** + * toggle the node ellipsis + * @name toggle_icons() + */ + toggle_ellipsis : function () { if(this._data.core.themes.ellipsis) { this.hide_ellipsis(); } else { this.show_ellipsis(); } }, + /** * set the node icon for a node * @name set_icon(obj, icon) * @param {mixed} obj * @param {String} icon the new icon - can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class */ @@ -4605,21 +4780,35 @@ } } return a; }; // remove item from array - $.vakata.array_remove = function(array, from, to) { - var rest = array.slice((to || from) + 1 || array.length); - array.length = from < 0 ? array.length + from : from; - array.push.apply(array, rest); + $.vakata.array_remove = function(array, from) { + array.splice(from, 1); return array; + //var rest = array.slice((to || from) + 1 || array.length); + //array.length = from < 0 ? array.length + from : from; + //array.push.apply(array, rest); + //return array; }; // remove item from array $.vakata.array_remove_item = function(array, item) { var tmp = $.inArray(item, array); return tmp !== -1 ? $.vakata.array_remove(array, tmp) : array; }; + $.vakata.array_filter = function(c,a,b,d,e) { + if (c.filter) { + return c.filter(a, b); + } + d=[]; + for (e in c) { + if (~~e+''===e+'' && e>=0 && a.call(b,c[e],+e,c)) { + d.push(c[e]); + } + } + return d; + }; /** * ### Changed plugin * @@ -4837,16 +5026,21 @@ .on(this.settings.checkbox.tie_selection ? 'select_node.jstree' : 'check_node.jstree', $.proxy(function (e, data) { var 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; + 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)); + //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; } @@ -4860,11 +5054,12 @@ for(i = 0, j = par.children.length; i < j; i++) { c += m[par.children[i]].state[ t ? 'selected' : 'checked' ]; } if(c === j) { par.state[ t ? 'selected' : 'checked' ] = true; - this._data[ t ? 'core' : 'checkbox' ].selected.push(par.id); + sel[par.id] = true; + //this._data[ t ? 'core' : 'checkbox' ].selected.push(par.id); tmp = this.get_node(par, true); if(tmp && tmp.length) { tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked'); } } @@ -4873,10 +5068,18 @@ } par = this.get_node(par.parent); } } + 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').addClass(t ? 'jstree-clicked' : 'jstree-checked').parent().attr('aria-selected', true); } }, this)) @@ -4892,11 +5095,12 @@ } }, this)) .on(this.settings.checkbox.tie_selection ? 'deselect_node.jstree' : 'uncheck_node.jstree', $.proxy(function (e, data) { var obj = data.node, dom = this.get_node(obj, true), - i, j, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection; + 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; } // apply down @@ -4922,22 +5126,28 @@ if(tmp && tmp.length) { tmp.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked'); } } } - tmp = []; - for(i = 0, j = this._data[ t ? 'core' : 'checkbox' ].selected.length; i < j; i++) { + sel = {}; + for(i = 0, j = cur.length; i < j; i++) { // apply down + apply up if( - (s.indexOf('down') === -1 || $.inArray(this._data[ t ? 'core' : 'checkbox' ].selected[i], obj.children_d) === -1) && - (s.indexOf('up') === -1 || $.inArray(this._data[ t ? 'core' : 'checkbox' ].selected[i], obj.parents) === -1) + (s.indexOf('down') === -1 || $.inArray(cur[i], obj.children_d) === -1) && + (s.indexOf('up') === -1 || $.inArray(cur[i], obj.parents) === -1) ) { - tmp.push(this._data[ t ? 'core' : 'checkbox' ].selected[i]); + sel[cur[i]] = true; } } - this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_unique(tmp); - + 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)); @@ -5040,11 +5250,14 @@ if(this.element === null) { return; } 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; for(i = 0, j = s.length; i < j; i++) { if(m[s[i]] && m[s[i]].parents) { for(k = 0, l = m[s[i]].parents.length; k < l; k++) { - if(o[m[s[i]].parents[k]] === undefined && m[s[i]].parents[k] !== $.jstree.root) { + if(o[m[s[i]].parents[k]] !== undefined) { + break; + } + if(m[s[i]].parents[k] !== $.jstree.root) { o[m[s[i]].parents[k]] = true; p.push(m[s[i]].parents[k]); } } } @@ -5551,20 +5764,22 @@ */ show_at_node : true, /** * an object of actions, or a function that accepts a node and a callback function and calls the callback function with an object of actions available for that node (you can also return the items too). * - * Each action consists of a key (a unique name) and a value which is an object with the following properties (only label and action are required): + * Each action consists of a key (a unique name) and a value which is an object with the following properties (only label and action are required). Once a menu item is activated the `action` function will be invoked with an object containing the following keys: item - the contextmenu item definition as seen below, reference - the DOM node that was used (the tree node), element - the contextmenu DOM element, position - an object with x/y properties indicating the position of the menu. * * * `separator_before` - a boolean indicating if there should be a separator before this item * * `separator_after` - a boolean indicating if there should be a separator after this item * * `_disabled` - a boolean indicating if this action should be disabled * * `label` - a string - the name of the action (could be a function returning a string) - * * `action` - a function to be executed if this item is chosen + * * `title` - a string - an optional tooltip for the item + * * `action` - a function to be executed if this item is chosen, the function will receive * * `icon` - a string, can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class * * `shortcut` - keyCode which will trigger the action if the menu is open (for example `113` for rename, which equals F2) * * `shortcut_label` - shortcut label (like for example `F2` for rename) + * * `submenu` - an object with the same structure as $.jstree.defaults.contextmenu.items which can be used to create a submenu - each key will be rendered as a separate option in a submenu that will appear once the current item is hovered * * @name $.jstree.defaults.contextmenu.items * @plugin contextmenu */ items : function (o, cb) { // Could be an object directly @@ -5678,10 +5893,13 @@ parent.bind.call(this); var last_ts = 0, cto = null, ex, ey; this.element .on("contextmenu.jstree", ".jstree-anchor", $.proxy(function (e, data) { + if (e.target.tagName.toLowerCase() === 'input') { + return; + } e.preventDefault(); last_ts = e.ctrlKey ? +new Date() : 0; if(data || cto) { last_ts = (+new Date()) + 10000; } @@ -5700,18 +5918,18 @@ }, this)) .on("touchstart.jstree", ".jstree-anchor", function (e) { if(!e.originalEvent || !e.originalEvent.changedTouches || !e.originalEvent.changedTouches[0]) { return; } - ex = e.pageX; - ey = e.pageY; + ex = e.originalEvent.changedTouches[0].clientX; + ey = e.originalEvent.changedTouches[0].clientY; cto = setTimeout(function () { $(e.currentTarget).trigger('contextmenu', true); }, 750); }) .on('touchmove.vakata.jstree', function (e) { - if(cto && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0] && (Math.abs(ex - e.pageX) > 50 || Math.abs(ey - e.pageY) > 50)) { + if(cto && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0] && (Math.abs(ex - e.originalEvent.changedTouches[0].clientX) > 50 || Math.abs(ey - e.originalEvent.changedTouches[0].clientY) > 50)) { clearTimeout(cto); } }) .on('touchend.vakata.jstree', function (e) { if(cto) { @@ -5738,11 +5956,14 @@ tm = null; }); }); } */ - $(document).on("context_hide.vakata.jstree", $.proxy(function () { this._data.contextmenu.visible = false; }, this)); + $(document).on("context_hide.vakata.jstree", $.proxy(function (e, data) { + this._data.contextmenu.visible = false; + $(data.reference).removeClass('jstree-context'); + }, this)); }; this.teardown = function () { if(this._data.contextmenu.visible) { $.vakata.context.hide(); } @@ -5801,10 +6022,11 @@ var d = this.get_node(obj, true), a = d.children(".jstree-anchor"); $(document).one("context_show.vakata.jstree", $.proxy(function (e, data) { var cls = 'jstree-contextmenu jstree-' + this.get_theme() + '-contextmenu'; $(data.element).addClass(cls); + a.addClass('jstree-context'); }, this)); this._data.contextmenu.visible = true; $.vakata.context.show(a, { 'x' : x, 'y' : y }, i); /** * triggered when the contextmenu is shown for a node @@ -5876,11 +6098,11 @@ if(!sep && val.separator_before) { str += "<"+"li class='vakata-context-separator'><"+"a href='#' " + ($.vakata.context.settings.icons ? '' : 'style="margin-left:0px;"') + ">&#160;<"+"/a><"+"/li>"; } sep = false; str += "<"+"li class='" + (val._class || "") + (val._disabled === true || ($.isFunction(val._disabled) && val._disabled({ "item" : val, "reference" : vakata_context.reference, "element" : vakata_context.element })) ? " vakata-contextmenu-disabled " : "") + "' "+(val.shortcut?" data-shortcut='"+val.shortcut+"' ":'')+">"; - str += "<"+"a href='#' rel='" + (vakata_context.items.length - 1) + "'>"; + str += "<"+"a href='#' rel='" + (vakata_context.items.length - 1) + "' " + (val.title ? "title='" + val.title + "'" : "") + ">"; if($.vakata.context.settings.icons) { str += "<"+"i "; if(val.icon) { if(val.icon.indexOf("/") !== -1 || val.icon.indexOf(".") !== -1) { str += " style='background:url(\"" + val.icon + "\") center center no-repeat' "; } else { str += " class='" + val.icon + "' "; } @@ -5914,26 +6136,39 @@ }, _show_submenu : function (o) { o = $(o); if(!o.length || !o.children("ul").length) { return; } var e = o.children("ul"), - x = o.offset().left + o.outerWidth(), + xl = o.offset().left, + x = xl + o.outerWidth(), y = o.offset().top, w = e.width(), h = e.height(), dw = $(window).width() + $(window).scrollLeft(), dh = $(window).height() + $(window).scrollTop(); // може да се спести е една проверка - дали няма някой от класовете вече нагоре if(right_to_left) { o[x - (w + 10 + o.outerWidth()) < 0 ? "addClass" : "removeClass"]("vakata-context-left"); } else { - o[x + w + 10 > dw ? "addClass" : "removeClass"]("vakata-context-right"); + o[x + w > dw && xl > dw - x ? "addClass" : "removeClass"]("vakata-context-right"); } if(y + h + 10 > dh) { e.css("bottom","-1px"); } + + //if does not fit - stick it to the side + if (o.hasClass('vakata-context-right')) { + if (xl < w) { + e.css("margin-right", xl - w); + } + } else { + if (dw - x < w) { + e.css("margin-left", dw - x - w); + } + } + e.show(); }, show : function (reference, position, data) { var o, e, x, y, w, h, dw, dh, cond = true; if(vakata_context.element && vakata_context.element.length) { @@ -6068,11 +6303,11 @@ .on('keydown', 'a', function (e) { var o = null; switch(e.which) { case 13: case 32: - e.type = "mouseup"; + e.type = "click"; e.preventDefault(); $(e.currentTarget).trigger(e); break; case 37: if(vakata_context.is_visible) { @@ -6139,10 +6374,11 @@ }); }); }($)); // $.jstree.defaults.plugins.push("contextmenu"); + /** * ### Drag'n'drop plugin * * Enables dragging and dropping of nodes in the tree, resulting in a move or copy operations. */ @@ -6210,38 +6446,94 @@ /** * controls whether a drag can be initiated from any part of the node and not just the text/icon part, works best with the wholerow plugin. Keep in mind it can cause problems with tree scrolling on mobile depending on the interface - in that case set the touch option to "selected". * @name $.jstree.defaults.dnd.large_drag_target * @plugin dnd */ - large_drag_target : false + large_drag_target : false, + /** + * controls whether use HTML5 dnd api instead of classical. That will allow better integration of dnd events with other HTML5 controls. + * @reference http://caniuse.com/#feat=dragndrop + * @name $.jstree.defaults.dnd.use_html5 + * @plugin dnd + */ + use_html5: false }; + var drg, elm; // TODO: now check works by checking for each node individually, how about max_children, unique, etc? $.jstree.plugins.dnd = function (options, parent) { + this.init = function (el, options) { + parent.init.call(this, el, options); + this.settings.dnd.use_html5 = this.settings.dnd.use_html5 && ('draggable' in document.createElement('span')); + }; this.bind = function () { parent.bind.call(this); this.element - .on('mousedown.jstree touchstart.jstree', this.settings.dnd.large_drag_target ? '.jstree-node' : '.jstree-anchor', $.proxy(function (e) { - if(this.settings.dnd.large_drag_target && $(e.target).closest('.jstree-node')[0] !== e.currentTarget) { - return true; + .on(this.settings.dnd.use_html5 ? 'dragstart.jstree' : 'mousedown.jstree touchstart.jstree', this.settings.dnd.large_drag_target ? '.jstree-node' : '.jstree-anchor', $.proxy(function (e) { + if(this.settings.dnd.large_drag_target && $(e.target).closest('.jstree-node')[0] !== e.currentTarget) { + return true; + } + if(e.type === "touchstart" && (!this.settings.dnd.touch || (this.settings.dnd.touch === 'selected' && !$(e.currentTarget).closest('.jstree-node').children('.jstree-anchor').hasClass('jstree-clicked')))) { + return true; + } + var obj = this.get_node(e.target), + mlt = this.is_selected(obj) && this.settings.dnd.drag_selection ? this.get_top_selected().length : 1, + txt = (mlt > 1 ? mlt + ' ' + this.get_string('nodes') : this.get_text(e.currentTarget)); + if(this.settings.core.force_text) { + txt = $.vakata.html.escape(txt); + } + if(obj && obj.id && obj.id !== $.jstree.root && (e.which === 1 || e.type === "touchstart" || e.type === "dragstart") && + (this.settings.dnd.is_draggable === true || ($.isFunction(this.settings.dnd.is_draggable) && this.settings.dnd.is_draggable.call(this, (mlt > 1 ? this.get_top_selected(true) : [obj]), e))) + ) { + drg = { 'jstree' : true, 'origin' : this, 'obj' : this.get_node(obj,true), 'nodes' : mlt > 1 ? this.get_top_selected() : [obj.id] }; + elm = e.currentTarget; + if (this.settings.dnd.use_html5) { + $.vakata.dnd._trigger('start', e, { 'helper': $(), 'element': elm, 'data': drg }); + } else { + this.element.trigger('mousedown.jstree'); + return $.vakata.dnd.start(e, drg, '<div id="jstree-dnd" class="jstree-' + this.get_theme() + ' jstree-' + this.get_theme() + '-' + this.get_theme_variant() + ' ' + ( this.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ) + '"><i class="jstree-icon jstree-er"></i>' + txt + '<ins class="jstree-copy" style="display:none;">+</ins></div>'); + } + } + }, this)); + if (this.settings.dnd.use_html5) { + this.element + .on('dragover.jstree', function (e) { + e.preventDefault(); + $.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg }); + return false; + }) + //.on('dragenter.jstree', this.settings.dnd.large_drop_target ? '.jstree-node' : '.jstree-anchor', $.proxy(function (e) { + // e.preventDefault(); + // $.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg }); + // return false; + // }, this)) + .on('drop.jstree', $.proxy(function (e) { + e.preventDefault(); + $.vakata.dnd._trigger('stop', e, { 'helper': $(), 'element': elm, 'data': drg }); + return false; + }, this)); + } + }; + this.redraw_node = function(obj, deep, callback, force_render) { + obj = parent.redraw_node.apply(this, arguments); + if (obj && this.settings.dnd.use_html5) { + if (this.settings.dnd.large_drag_target) { + obj.setAttribute('draggable', true); + } else { + var i, j, tmp = null; + for(i = 0, j = obj.childNodes.length; i < j; i++) { + if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) { + tmp = obj.childNodes[i]; + break; + } } - if(e.type === "touchstart" && (!this.settings.dnd.touch || (this.settings.dnd.touch === 'selected' && !$(e.currentTarget).closest('.jstree-node').children('.jstree-anchor').hasClass('jstree-clicked')))) { - return true; + if(tmp) { + tmp.setAttribute('draggable', true); } - var obj = this.get_node(e.target), - mlt = this.is_selected(obj) && this.settings.dnd.drag_selection ? this.get_top_selected().length : 1, - txt = (mlt > 1 ? mlt + ' ' + this.get_string('nodes') : this.get_text(e.currentTarget)); - if(this.settings.core.force_text) { - txt = $.vakata.html.escape(txt); - } - if(obj && obj.id && obj.id !== $.jstree.root && (e.which === 1 || e.type === "touchstart") && - (this.settings.dnd.is_draggable === true || ($.isFunction(this.settings.dnd.is_draggable) && this.settings.dnd.is_draggable.call(this, (mlt > 1 ? this.get_top_selected(true) : [obj]), e))) - ) { - this.element.trigger('mousedown.jstree'); - return $.vakata.dnd.start(e, { 'jstree' : true, 'origin' : this, 'obj' : this.get_node(obj,true), 'nodes' : mlt > 1 ? this.get_top_selected() : [obj.id] }, '<div id="jstree-dnd" class="jstree-' + this.get_theme() + ' jstree-' + this.get_theme() + '-' + this.get_theme_variant() + ' ' + ( this.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ) + '"><i class="jstree-icon jstree-er"></i>' + txt + '<ins class="jstree-copy" style="display:none;">+</ins></div>'); - } - }, this)); + } + } + return obj; }; }; $(function() { // bind only once for all instances @@ -6257,11 +6549,15 @@ lastev = false; if(!data || !data.data || !data.data.jstree) { return; } marker.appendTo('body'); //.show(); }) .on('dnd_move.vakata.jstree', function (e, data) { - if(opento) { clearTimeout(opento); } + if(opento) { + if (!data.event || data.event.type !== 'dragover' || data.event.target !== lastev.target) { + clearTimeout(opento); + } + } if(!data || !data.data || !data.data.jstree) { return; } // if we are hovering the marker image do nothing (can happen on "inside" drags) if(data.event.target.id && data.event.target.id === 'jstree-marker') { return; @@ -6270,39 +6566,43 @@ var ins = $.jstree.reference(data.event.target), ref = false, off = false, rel = false, - tmp, l, t, h, p, i, o, ok, t1, t2, op, ps, pr, ip, tm; + tmp, l, t, h, p, i, o, ok, t1, t2, op, ps, pr, ip, tm, is_copy, pn; // if we are over an instance if(ins && ins._data && ins._data.dnd) { marker.attr('class', 'jstree-' + ins.get_theme() + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' )); + is_copy = data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))); data.helper .children().attr('class', 'jstree-' + ins.get_theme() + ' jstree-' + ins.get_theme() + '-' + ins.get_theme_variant() + ' ' + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' )) - .find('.jstree-copy').first()[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))) ? 'show' : 'hide' ](); + .find('.jstree-copy').first()[ is_copy ? 'show' : 'hide' ](); - // if are hovering the container itself add a new root node + //console.log(data.event); if( (data.event.target === ins.element[0] || data.event.target === ins.get_container_ul()[0]) && ins.get_container_ul().children().length === 0) { ok = true; for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) { ok = ok && ins.check( (data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey)) ) ? "copy_node" : "move_node"), (data.data.origin && data.data.origin !== ins ? data.data.origin.get_node(data.data.nodes[t1]) : data.data.nodes[t1]), $.jstree.root, 'last', { 'dnd' : true, 'ref' : ins.get_node($.jstree.root), 'pos' : 'i', 'origin' : data.data.origin, 'is_multi' : (data.data.origin && data.data.origin !== ins), 'is_foreign' : (!data.data.origin) }); if(!ok) { break; } } if(ok) { lastmv = { 'ins' : ins, 'par' : $.jstree.root, 'pos' : 'last' }; marker.hide(); data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok'); + if (data.event.originalEvent && data.event.originalEvent.dataTransfer) { + data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move'; + } return; } } else { // if we are hovering a tree node ref = ins.settings.dnd.large_drop_target ? $(data.event.target).closest('.jstree-node').children('.jstree-anchor') : $(data.event.target).closest('.jstree-anchor'); if(ref && ref.length && ref.parent().is('.jstree-closed, .jstree-open, .jstree-leaf')) { off = ref.offset(); - rel = data.event.pageY - off.top; + rel = (data.event.pageY !== undefined ? data.event.pageY : data.event.originalEvent.pageY) - off.top; h = ref.outerHeight(); if(rel < h / 3) { o = ['b', 'i', 'a']; } else if(rel > h - h / 3) { @@ -6352,34 +6652,47 @@ } 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(ok) { + pn = ins.get_node(p, true); + if (!pn.hasClass('.jstree-dnd-parent')) { + $('.jstree-dnd-parent').removeClass('jstree-dnd-parent'); + pn.addClass('jstree-dnd-parent'); + } lastmv = { 'ins' : ins, 'par' : p, 'pos' : v === 'i' && ip === 'last' && i === 0 && !ins.is_loaded(tm) ? 'last' : i }; marker.css({ 'left' : l + 'px', 'top' : t + 'px' }).show(); data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok'); + if (data.event.originalEvent && data.event.originalEvent.dataTransfer) { + data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move'; + } laster = {}; o = true; return false; } }); if(o === true) { return; } } } } + $('.jstree-dnd-parent').removeClass('jstree-dnd-parent'); lastmv = false; data.helper.find('.jstree-icon').removeClass('jstree-ok').addClass('jstree-er'); + if (data.event.originalEvent && data.event.originalEvent.dataTransfer) { + data.event.originalEvent.dataTransfer.dropEffect = 'none'; + } marker.hide(); }) .on('dnd_scroll.vakata.jstree', function (e, data) { if(!data || !data.data || !data.data.jstree) { return; } marker.hide(); lastmv = false; lastev = false; data.helper.find('.jstree-icon').first().removeClass('jstree-ok').addClass('jstree-er'); }) .on('dnd_stop.vakata.jstree', function (e, data) { + $('.jstree-dnd-parent').removeClass('jstree-dnd-parent'); if(opento) { clearTimeout(opento); } if(!data || !data.data || !data.data.jstree) { return; } marker.hide().detach(); var i, j, nodes = []; if(lastmv) { @@ -6401,15 +6714,25 @@ lastmv = false; }) .on('keyup.jstree keydown.jstree', function (e, data) { data = $.vakata.dnd._get(); if(data && data.data && data.data.jstree) { - data.helper.find('.jstree-copy').first()[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (e.metaKey || e.ctrlKey))) ? 'show' : 'hide' ](); - if(lastev) { - lastev.metaKey = e.metaKey; - lastev.ctrlKey = e.ctrlKey; - $.vakata.dnd._trigger('move', lastev); + if (e.type === "keyup" && e.which === 27) { + if (opento) { clearTimeout(opento); } + lastmv = false; + laster = false; + lastev = false; + opento = false; + marker.hide().detach(); + $.vakata.dnd._clean(); + } else { + data.helper.find('.jstree-copy').first()[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (e.metaKey || e.ctrlKey))) ? 'show' : 'hide' ](); + if(lastev) { + lastev.metaKey = e.metaKey; + lastev.ctrlKey = e.ctrlKey; + $.vakata.dnd._trigger('move', lastev); + } } } }); }); @@ -6448,12 +6771,14 @@ helper_left : 5, helper_top : 10, threshold : 5, threshold_touch : 50 }, - _trigger : function (event_name, e) { - var data = $.vakata.dnd._get(); + _trigger : function (event_name, e, data) { + if (data === undefined) { + data = $.vakata.dnd._get(); + } data.event = e; $(document).triggerHandler("dnd_" + event_name + ".vakata", data); }, _get : function () { return { @@ -6521,11 +6846,15 @@ } if(vakata_dnd.is_drag) { $.vakata.dnd.stop({}); } try { e.currentTarget.unselectable = "on"; e.currentTarget.onselectstart = function() { return false; }; - if(e.currentTarget.style) { e.currentTarget.style.MozUserSelect = "none"; } + if(e.currentTarget.style) { + e.currentTarget.style.touchAction = "none"; + e.currentTarget.style.msTouchAction = "none"; + e.currentTarget.style.MozUserSelect = "none"; + } } catch(ignore) { } vakata_dnd.init_x = e.pageX; vakata_dnd.init_y = e.pageY; vakata_dnd.data = data; vakata_dnd.is_down = true; @@ -6562,10 +6891,11 @@ if(vakata_dnd.helper) { vakata_dnd.helper.appendTo("body"); vakata_dnd.helper_w = vakata_dnd.helper.outerWidth(); } vakata_dnd.is_drag = true; + $(vakata_dnd.target).one('click.vakata', false); /** * triggered on the document when a drag starts * @event * @plugin dnd * @name dnd_start.vakata @@ -6662,10 +6992,13 @@ * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start * @param {DOM} element the DOM element being dragged * @param {jQuery} helper the helper shown next to the mouse * @param {Object} event the event that caused the stop */ + if (e.target !== vakata_dnd.target) { + $(vakata_dnd.target).off('click.vakata'); + } $.vakata.dnd._trigger("stop", e); } else { if(e.type === "touchend" && e.target === vakata_dnd.target) { var to = setTimeout(function () { $(e.target).click(); }, 100); @@ -6707,62 +7040,99 @@ * @plugin massload */ $.jstree.defaults.massload = null; $.jstree.plugins.massload = function (options, parent) { this.init = function (el, options) { - parent.init.call(this, el, options); this._data.massload = {}; + parent.init.call(this, el, options); }; - this._load_nodes = function (nodes, callback, is_callback) { - var s = this.settings.massload; - if(is_callback && !$.isEmptyObject(this._data.massload)) { - return parent._load_nodes.call(this, nodes, callback, is_callback); - } - if($.isFunction(s)) { - return s.call(this, nodes, $.proxy(function (data) { - if(data) { - for(var i in data) { - if(data.hasOwnProperty(i)) { - this._data.massload[i] = data[i]; - } + this._load_nodes = function (nodes, callback, is_callback, force_reload) { + var s = this.settings.massload, + nodesString = JSON.stringify(nodes), + toLoad = [], + m = this._model.data, + i, j, dom; + if (!is_callback) { + for(i = 0, j = nodes.length; i < j; i++) { + if(!m[nodes[i]] || ( (!m[nodes[i]].state.loaded && !m[nodes[i]].state.failed) || force_reload) ) { + toLoad.push(nodes[i]); + dom = this.get_node(nodes[i], true); + if (dom && dom.length) { + dom.addClass("jstree-loading").attr('aria-busy',true); } } - parent._load_nodes.call(this, nodes, callback, is_callback); - }, this)); - } - if(typeof s === 'object' && s && s.url) { - s = $.extend(true, {}, s); - if($.isFunction(s.url)) { - s.url = s.url.call(this, nodes); } - if($.isFunction(s.data)) { - s.data = s.data.call(this, nodes); - } - return $.ajax(s) - .done($.proxy(function (data,t,x) { + this._data.massload = {}; + if (toLoad.length) { + if($.isFunction(s)) { + return s.call(this, toLoad, $.proxy(function (data) { + var i, j; if(data) { - for(var i in data) { + for(i in data) { if(data.hasOwnProperty(i)) { this._data.massload[i] = data[i]; } } } - parent._load_nodes.call(this, nodes, callback, is_callback); - }, this)) - .fail($.proxy(function (f) { - parent._load_nodes.call(this, nodes, callback, is_callback); + for(i = 0, j = nodes.length; i < j; i++) { + dom = this.get_node(nodes[i], true); + if (dom && dom.length) { + dom.removeClass("jstree-loading").attr('aria-busy',false); + } + } + parent._load_nodes.call(this, nodes, callback, is_callback, force_reload); }, this)); + } + if(typeof s === 'object' && s && s.url) { + s = $.extend(true, {}, s); + if($.isFunction(s.url)) { + s.url = s.url.call(this, toLoad); + } + if($.isFunction(s.data)) { + s.data = s.data.call(this, toLoad); + } + return $.ajax(s) + .done($.proxy(function (data,t,x) { + var i, j; + if(data) { + for(i in data) { + if(data.hasOwnProperty(i)) { + this._data.massload[i] = data[i]; + } + } + } + for(i = 0, j = nodes.length; i < j; i++) { + dom = this.get_node(nodes[i], true); + if (dom && dom.length) { + dom.removeClass("jstree-loading").attr('aria-busy',false); + } + } + parent._load_nodes.call(this, nodes, callback, is_callback, force_reload); + }, this)) + .fail($.proxy(function (f) { + parent._load_nodes.call(this, nodes, callback, is_callback, force_reload); + }, this)); + } + } } - return parent._load_nodes.call(this, nodes, callback, is_callback); + return parent._load_nodes.call(this, nodes, callback, is_callback, force_reload); }; this._load_node = function (obj, callback) { - var d = this._data.massload[obj.id]; - if(d) { - return this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $($.parseHTML(d)).filter(function () { return this.nodeType !== 3; }) : d, function (status) { - callback.call(this, status); - delete this._data.massload[obj.id]; - }); + var data = this._data.massload[obj.id], + rslt = null, dom; + if(data) { + rslt = this[typeof data === 'string' ? '_append_html_data' : '_append_json_data']( + obj, + typeof data === 'string' ? $($.parseHTML(data)).filter(function () { return this.nodeType !== 3; }) : data, + function (status) { callback.call(this, status); } + ); + dom = this.get_node(obj.id, true); + if (dom && dom.length) { + dom.removeClass("jstree-loading").attr('aria-busy',false); + } + delete this._data.massload[obj.id]; + return rslt; } return parent._load_node.call(this, obj, callback); }; }; @@ -6777,14 +7147,14 @@ * @name $.jstree.defaults.search * @plugin search */ $.jstree.defaults.search = { /** - * a jQuery-like AJAX config, which jstree uses if a server should be queried for results. - * + * a jQuery-like AJAX config, which jstree uses if a server should be queried for results. + * * A `str` (which is the search string) parameter will be added with the request, an optional `inside` parameter will be added if the search is limited to a node id. The expected result is a JSON array with nodes that need to be opened so that matching nodes will be revealed. - * Leave this setting as `false` to not query the server. You can also set this to a function, which will be invoked in the instance's scope and receive 3 parameters - the search string, the callback to call with the array of nodes to load, and the optional node ID to limit the search to + * Leave this setting as `false` to not query the server. You can also set this to a function, which will be invoked in the instance's scope and receive 3 parameters - the search string, the callback to call with the array of nodes to load, and the optional node ID to limit the search to * @name $.jstree.defaults.search.ajax * @plugin search */ ajax : false, /** @@ -6798,11 +7168,11 @@ * @name $.jstree.defaults.search.case_sensitive * @plugin search */ case_sensitive : false, /** - * Indicates if the tree should be filtered (by default) to show only matching nodes (keep in mind this can be a heavy on large trees in old browsers). + * Indicates if the tree should be filtered (by default) to show only matching nodes (keep in mind this can be a heavy on large trees in old browsers). * This setting can be changed at runtime when calling the search method. Default is `false`. * @name $.jstree.defaults.search.show_only_matches * @plugin search */ show_only_matches : false, @@ -6847,28 +7217,34 @@ this._data.search.hdn = []; this.element .on("search.jstree", $.proxy(function (e, data) { if(this._data.search.som && data.res.length) { - var m = this._model.data, i, j, p = []; + var m = this._model.data, i, j, p = [], k, l; for(i = 0, j = data.res.length; i < j; i++) { if(m[data.res[i]] && !m[data.res[i]].state.hidden) { p.push(data.res[i]); p = p.concat(m[data.res[i]].parents); if(this._data.search.smc) { - p = p.concat(m[data.res[i]].children_d); + for (k = 0, l = m[data.res[i]].children_d.length; k < l; k++) { + if (m[m[data.res[i]].children_d[k]] && !m[m[data.res[i]].children_d[k]].state.hidden) { + p.push(m[data.res[i]].children_d[k]); + } + } } } } p = $.vakata.array_remove_item($.vakata.array_unique(p), $.jstree.root); this._data.search.hdn = this.hide_all(true); - this.show_node(p); + this.show_node(p, true); + this.redraw(true); } }, this)) .on("clear_search.jstree", $.proxy(function (e, data) { if(this._data.search.som && data.res.length) { - this.show_node(this._data.search.hdn); + this.show_node(this._data.search.hdn, true); + this.redraw(true); } }, this)); }; /** * used to search the tree nodes for a given string @@ -6906,32 +7282,36 @@ if(!skip_async && a !== false) { if($.isFunction(a)) { return a.call(this, str, $.proxy(function (d) { if(d && d.d) { d = d.d; } this._load_nodes(!$.isArray(d) ? [] : $.vakata.array_unique(d), function () { - this.search(str, true, show_only_matches, inside, append); - }, true); + this.search(str, true, show_only_matches, inside, append, show_only_matches_children); + }); }, this), inside); } else { a = $.extend({}, a); if(!a.data) { a.data = {}; } a.data.str = str; if(inside) { a.data.inside = inside; } - return $.ajax(a) + if (this._data.search.lastRequest) { + this._data.search.lastRequest.abort(); + } + this._data.search.lastRequest = $.ajax(a) .fail($.proxy(function () { this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'search', 'id' : 'search_01', 'reason' : 'Could not load search parents', 'data' : JSON.stringify(a) }; this.settings.core.error.call(this, this._data.core.last_error); }, this)) .done($.proxy(function (d) { if(d && d.d) { d = d.d; } this._load_nodes(!$.isArray(d) ? [] : $.vakata.array_unique(d), function () { - this.search(str, true, show_only_matches, inside, append); - }, true); + this.search(str, true, show_only_matches, inside, append, show_only_matches_children); + }); }, this)); + return this._data.search.lastRequest; } } if(!append) { this._data.search.str = str; this._data.search.dom = $(); @@ -6942,11 +7322,11 @@ } f = new $.vakata.search(str, true, { caseSensitive : s.case_sensitive, fuzzy : s.fuzzy }); $.each(m[inside ? inside : $.jstree.root].children_d, function (ii, i) { var v = m[i]; - if(v.text && (!s.search_leaves_only || (v.state.loaded && v.children.length === 0)) && ( (s.search_callback && s.search_callback.call(this, str, v)) || (!s.search_callback && f.search(v.text).isMatch) ) ) { + if(v.text && !v.state.hidden && (!s.search_leaves_only || (v.state.loaded && v.children.length === 0)) && ( (s.search_callback && s.search_callback.call(this, str, v)) || (!s.search_callback && f.search(v.text).isMatch) ) ) { r.push(i); p = p.concat(v.parents); } }); if(r.length) { @@ -7340,10 +7720,12 @@ * * * `max_children` the maximum number of immediate children this node type can have. Do not specify or set to `-1` for unlimited. * * `max_depth` the maximum number of nesting this node type can have. A value of `1` would mean that the node can have children, but no grandchildren. Do not specify or set to `-1` for unlimited. * * `valid_children` an array of node type strings, that nodes of this type can have as children. Do not specify or set to `-1` for no limits. * * `icon` a string - can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class. Omit to use the default icon from your theme. + * * `li_attr` an object of values which will be used to add HTML attributes on the resulting LI DOM node (merged with the node's own data) + * * `a_attr` an object of values which will be used to add HTML attributes on the resulting A DOM node (merged with the node's own data) * * There are two predefined types: * * * `#` represents the root of the tree, for example `max_children` would control the maximum number of root nodes. * * `default` represents the default node - any settings here will be applied to all nodes that do not have a type specified. @@ -7381,11 +7763,11 @@ this.element .on('model.jstree', $.proxy(function (e, data) { var m = this._model.data, dpc = data.nodes, t = this.settings.types, - i, j, c = 'default'; + i, j, c = 'default', k; for(i = 0, j = dpc.length; i < j; i++) { c = 'default'; if(m[dpc[i]].original && m[dpc[i]].original.type && t[m[dpc[i]].original.type]) { c = m[dpc[i]].original.type; } @@ -7394,10 +7776,43 @@ } m[dpc[i]].type = c; if(m[dpc[i]].icon === true && t[c].icon !== undefined) { m[dpc[i]].icon = t[c].icon; } + if(t[c].li_attr !== undefined && typeof t[c].li_attr === 'object') { + for (k in t[c].li_attr) { + if (t[c].li_attr.hasOwnProperty(k)) { + if (k === 'id') { + continue; + } + else if (m[dpc[i]].li_attr[k] === undefined) { + m[dpc[i]].li_attr[k] = t[c].li_attr[k]; + } + else if (k === 'class') { + m[dpc[i]].li_attr['class'] = t[c].li_attr['class'] + ' ' + m[dpc[i]].li_attr['class']; + } + } + } + } + if(t[c].a_attr !== undefined && typeof t[c].a_attr === 'object') { + for (k in t[c].a_attr) { + if (t[c].a_attr.hasOwnProperty(k)) { + if (k === 'id') { + continue; + } + else if (m[dpc[i]].a_attr[k] === undefined) { + m[dpc[i]].a_attr[k] = t[c].a_attr[k]; + } + else if (k === 'href' && m[dpc[i]].a_attr[k] === '#') { + m[dpc[i]].a_attr['href'] = t[c].a_attr['href']; + } + else if (k === 'class') { + m[dpc[i]].a_attr['class'] = t[c].a_attr['class'] + ' ' + m[dpc[i]].a_attr['class']; + } + } + } + } } m[$.jstree.root].type = $.jstree.root; }, this)); parent.bind.call(this); }; @@ -7524,33 +7939,135 @@ * @param {mixed} obj the node to change * @param {String} type the new type * @plugin types */ this.set_type = function (obj, type) { - var t, t1, t2, old_type, old_icon; + var m = this._model.data, t, t1, t2, old_type, old_icon, k, d, a; if($.isArray(obj)) { obj = obj.slice(); for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { this.set_type(obj[t1], type); } return true; } t = this.settings.types; obj = this.get_node(obj); if(!t[type] || !obj) { return false; } + d = this.get_node(obj, true); + if (d && d.length) { + a = d.children('.jstree-anchor'); + } old_type = obj.type; old_icon = this.get_icon(obj); obj.type = type; - if(old_icon === true || (t[old_type] && t[old_type].icon !== undefined && old_icon === t[old_type].icon)) { + if(old_icon === true || !t[old_type] || (t[old_type].icon !== undefined && old_icon === t[old_type].icon)) { this.set_icon(obj, t[type].icon !== undefined ? t[type].icon : true); } + + // remove old type props + if(t[old_type] && t[old_type].li_attr !== undefined && typeof t[old_type].li_attr === 'object') { + for (k in t[old_type].li_attr) { + if (t[old_type].li_attr.hasOwnProperty(k)) { + if (k === 'id') { + continue; + } + else if (k === 'class') { + m[obj.id].li_attr['class'] = (m[obj.id].li_attr['class'] || '').replace(t[old_type].li_attr[k], ''); + if (d) { d.removeClass(t[old_type].li_attr[k]); } + } + else if (m[obj.id].li_attr[k] === t[old_type].li_attr[k]) { + m[obj.id].li_attr[k] = null; + if (d) { d.removeAttr(k); } + } + } + } + } + if(t[old_type] && t[old_type].a_attr !== undefined && typeof t[old_type].a_attr === 'object') { + for (k in t[old_type].a_attr) { + if (t[old_type].a_attr.hasOwnProperty(k)) { + if (k === 'id') { + continue; + } + else if (k === 'class') { + m[obj.id].a_attr['class'] = (m[obj.id].a_attr['class'] || '').replace(t[old_type].a_attr[k], ''); + if (a) { a.removeClass(t[old_type].a_attr[k]); } + } + else if (m[obj.id].a_attr[k] === t[old_type].a_attr[k]) { + if (k === 'href') { + m[obj.id].a_attr[k] = '#'; + if (a) { a.attr('href', '#'); } + } + else { + delete m[obj.id].a_attr[k]; + if (a) { a.removeAttr(k); } + } + } + } + } + } + + // add new props + if(t[type].li_attr !== undefined && typeof t[type].li_attr === 'object') { + for (k in t[type].li_attr) { + if (t[type].li_attr.hasOwnProperty(k)) { + if (k === 'id') { + continue; + } + else if (m[obj.id].li_attr[k] === undefined) { + m[obj.id].li_attr[k] = t[type].li_attr[k]; + if (d) { + if (k === 'class') { + d.addClass(t[type].li_attr[k]); + } + else { + d.attr(k, t[type].li_attr[k]); + } + } + } + else if (k === 'class') { + m[obj.id].li_attr['class'] = t[type].li_attr[k] + ' ' + m[obj.id].li_attr['class']; + if (d) { d.addClass(t[type].li_attr[k]); } + } + } + } + } + if(t[type].a_attr !== undefined && typeof t[type].a_attr === 'object') { + for (k in t[type].a_attr) { + if (t[type].a_attr.hasOwnProperty(k)) { + if (k === 'id') { + continue; + } + else if (m[obj.id].a_attr[k] === undefined) { + m[obj.id].a_attr[k] = t[type].a_attr[k]; + if (a) { + if (k === 'class') { + a.addClass(t[type].a_attr[k]); + } + else { + a.attr(k, t[type].a_attr[k]); + } + } + } + else if (k === 'href' && m[obj.id].a_attr[k] === '#') { + m[obj.id].a_attr['href'] = t[type].a_attr['href']; + if (a) { a.attr('href', t[type].a_attr['href']); } + } + else if (k === 'class') { + m[obj.id].a_attr['class'] = t[type].a_attr['class'] + ' ' + m[obj.id].a_attr['class']; + if (a) { a.addClass(t[type].a_attr[k]); } + } + } + } + } + return true; }; }; // include the types plugin by default // $.jstree.defaults.plugins.push("types"); + /** * ### Unique plugin * * Enforces that no nodes with the same name can coexist as siblings. */ @@ -7697,13 +8214,15 @@ .on("hover_node.jstree dehover_node.jstree", $.proxy(function (e, data) { if(e.type === "hover_node" && this.is_disabled(data.node)) { return; } this.get_node(data.node, true).children('.jstree-wholerow')[e.type === "hover_node"?"addClass":"removeClass"]('jstree-wholerow-hovered'); }, this)) .on("contextmenu.jstree", ".jstree-wholerow", $.proxy(function (e) { - e.preventDefault(); - var tmp = $.Event('contextmenu', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey, pageX : e.pageX, pageY : e.pageY }); - $(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp); + if (this._data.contextmenu) { + e.preventDefault(); + var tmp = $.Event('contextmenu', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey, pageX : e.pageX, pageY : e.pageY }); + $(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp); + } }, this)) /*! .on("mousedown.jstree touchstart.jstree", ".jstree-wholerow", function (e) { if(e.target === e.currentTarget) { var a = $(e.currentTarget).closest(".jstree-node").children(".jstree-anchor"); @@ -7713,9 +8232,14 @@ }) */ .on("click.jstree", ".jstree-wholerow", function (e) { e.stopImmediatePropagation(); var tmp = $.Event('click', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey }); + $(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp).focus(); + }) + .on("dblclick.jstree", ".jstree-wholerow", function (e) { + e.stopImmediatePropagation(); + var tmp = $.Event('dblclick', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey }); $(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp).focus(); }) .on("click.jstree", ".jstree-leaf > .jstree-ocl", $.proxy(function (e) { e.stopImmediatePropagation(); var tmp = $.Event('click', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey }); \ No newline at end of file