vendor/assets/javascripts/jstree.js in jstree-rails-4-3.3.4 vs vendor/assets/javascripts/jstree.js in jstree-rails-4-3.3.8

- old
+ new

@@ -11,11 +11,11 @@ factory(jQuery); } }(function ($, undefined) { "use strict"; /*! - * jsTree 3.3.4 + * jsTree 3.3.8 * 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.4', + version : '3.3.8', /** * holds all the default options used when creating new instances * @name $.jstree.defaults */ defaults : { @@ -428,14 +428,94 @@ * Force node text to plain text (and escape HTML). Defaults to `false` * @name $.jstree.defaults.core.force_text */ force_text : false, /** - * Should the node should be toggled if the text is double clicked . Defaults to `true` + * Should the node be toggled if the text is double clicked. Defaults to `true` * @name $.jstree.defaults.core.dblclick_toggle */ - dblclick_toggle : true + dblclick_toggle : true, + /** + * Should the loaded nodes be part of the state. Defaults to `false` + * @name $.jstree.defaults.core.loaded_state + */ + loaded_state : false, + /** + * Should the last active node be focused when the tree container is blurred and the focused again. This helps working with screen readers. Defaults to `true` + * @name $.jstree.defaults.core.restore_focus + */ + restore_focus : true, + /** + * Default keyboard shortcuts (an object where each key is the button name or combo - like 'enter', 'ctrl-space', 'p', etc and the value is the function to execute in the instance's scope) + * @name $.jstree.defaults.core.keyboard + */ + keyboard : { + 'ctrl-space': function (e) { + // aria defines space only with Ctrl + e.type = "click"; + $(e.currentTarget).trigger(e); + }, + 'enter': function (e) { + // enter + e.type = "click"; + $(e.currentTarget).trigger(e); + }, + 'left': function (e) { + // left + e.preventDefault(); + if(this.is_open(e.currentTarget)) { + this.close_node(e.currentTarget); + } + else { + var o = this.get_parent(e.currentTarget); + if(o && o.id !== $.jstree.root) { this.get_node(o, true).children('.jstree-anchor').focus(); } + } + }, + 'up': function (e) { + // up + e.preventDefault(); + var o = this.get_prev_dom(e.currentTarget); + if(o && o.length) { o.children('.jstree-anchor').focus(); } + }, + 'right': function (e) { + // 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)) { + var o = this.get_node(e.currentTarget, true).children('.jstree-children')[0]; + if(o) { $(this._firstChild(o)).children('.jstree-anchor').focus(); } + } + }, + 'down': function (e) { + // down + e.preventDefault(); + var o = this.get_next_dom(e.currentTarget); + if(o && o.length) { o.children('.jstree-anchor').focus(); } + }, + '*': function (e) { + // aria defines * on numpad as open_all - not very common + this.open_all(); + }, + 'home': function (e) { + // home + e.preventDefault(); + var o = this._firstChild(this.get_container_ul()[0]); + if(o) { $(o).children('.jstree-anchor').filter(':visible').focus(); } + }, + 'end': function (e) { + // end + e.preventDefault(); + this.element.find('.jstree-anchor').filter(':visible').last().focus(); + }, + 'f2': function (e) { + // f2 - safe to include - if check_callback is false it will fail + e.preventDefault(); + this.edit(e.currentTarget); + } + } }; $.jstree.core.prototype = { /** * used to decorate an instance with a plugin. Used internally. * @private @@ -546,11 +626,13 @@ } if(!keep_html) { this.element.empty(); } this.teardown(); }, /** - * Create prototype node + * Create a prototype node + * @name _create_prototype_node() + * @return {DOMElement} */ _create_prototype_node : function () { var _node = document.createElement('LI'), _temp1, _temp2; _node.setAttribute('role', 'treeitem'); _temp1 = document.createElement('I'); @@ -568,10 +650,52 @@ _node.appendChild(_temp1); _temp1 = _temp2 = null; return _node; }, + _kbevent_to_func : function (e) { + var keys = { + 8: "Backspace", 9: "Tab", 13: "Enter", 19: "Pause", 27: "Esc", + 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 36: "Home", + 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "Print", 45: "Insert", + 46: "Delete", 96: "Numpad0", 97: "Numpad1", 98: "Numpad2", 99 : "Numpad3", + 100: "Numpad4", 101: "Numpad5", 102: "Numpad6", 103: "Numpad7", + 104: "Numpad8", 105: "Numpad9", '-13': "NumpadEnter", 112: "F1", + 113: "F2", 114: "F3", 115: "F4", 116: "F5", 117: "F6", 118: "F7", + 119: "F8", 120: "F9", 121: "F10", 122: "F11", 123: "F12", 144: "Numlock", + 145: "Scrolllock", 16: 'Shift', 17: 'Ctrl', 18: 'Alt', + 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', + 54: '6', 55: '7', 56: '8', 57: '9', 59: ';', 61: '=', 65: 'a', + 66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h', + 73: 'i', 74: 'j', 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o', + 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v', + 87: 'w', 88: 'x', 89: 'y', 90: 'z', 107: '+', 109: '-', 110: '.', + 186: ';', 187: '=', 188: ',', 189: '-', 190: '.', 191: '/', 192: '`', + 219: '[', 220: '\\',221: ']', 222: "'", 111: '/', 106: '*', 173: '-' + }; + var parts = []; + if (e.ctrlKey) { parts.push('ctrl'); } + if (e.altKey) { parts.push('alt'); } + if (e.shiftKey) { parts.push('shift'); } + parts.push(keys[e.which] || e.which); + parts = parts.sort().join('-').toLowerCase(); + + var kb = this.settings.core.keyboard, i, tmp; + for (i in kb) { + if (kb.hasOwnProperty(i)) { + tmp = i; + if (tmp !== '-' && tmp !== '+') { + tmp = tmp.replace('--', '-MINUS').replace('+-', '-MINUS').replace('++', '-PLUS').replace('-+', '-PLUS'); + tmp = tmp.split(/-|\+/).sort().join('-').replace('MINUS', '-').replace('PLUS', '+').toLowerCase(); + } + if (tmp === parts) { + return kb[i]; + } + } + } + return null; + }, /** * part of the destroying of an instance. Used internally. * @private * @name teardown() */ @@ -633,87 +757,20 @@ if(e.currentTarget !== document.activeElement) { $(e.currentTarget).focus(); } this.activate_node(e.currentTarget, e); }, this)) .on('keydown.jstree', '.jstree-anchor', $.proxy(function (e) { if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; } - if(e.which !== 32 && e.which !== 13 && (e.shiftKey || e.ctrlKey || e.altKey || e.metaKey)) { return true; } - var o = null; if(this._data.core.rtl) { if(e.which === 37) { e.which = 39; } else if(e.which === 39) { e.which = 37; } } - switch(e.which) { - case 32: // aria defines space only with Ctrl - if(e.ctrlKey) { - e.type = "click"; - $(e.currentTarget).trigger(e); - } - break; - case 13: // enter - e.type = "click"; - $(e.currentTarget).trigger(e); - break; - case 37: // left - e.preventDefault(); - if(this.is_open(e.currentTarget)) { - this.close_node(e.currentTarget); - } - else { - o = this.get_parent(e.currentTarget); - if(o && o.id !== $.jstree.root) { this.get_node(o, true).children('.jstree-anchor').focus(); } - } - break; - case 38: // up - e.preventDefault(); - o = this.get_prev_dom(e.currentTarget); - if(o && o.length) { o.children('.jstree-anchor').focus(); } - break; - 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)) { - o = this.get_node(e.currentTarget, true).children('.jstree-children')[0]; - if(o) { $(this._firstChild(o)).children('.jstree-anchor').focus(); } - } - break; - case 40: // down - e.preventDefault(); - o = this.get_next_dom(e.currentTarget); - if(o && o.length) { o.children('.jstree-anchor').focus(); } - break; - case 106: // aria defines * on numpad as open_all - not very common - this.open_all(); - break; - case 36: // home - e.preventDefault(); - o = this._firstChild(this.get_container_ul()[0]); - if(o) { $(o).children('.jstree-anchor').filter(':visible').focus(); } - 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; - - */ + var f = this._kbevent_to_func(e); + if (f) { + var r = f.call(this, e); + if (r === false || r === true) { + return r; + } } }, this)) .on("load_node.jstree", $.proxy(function (e, data) { if(data.status) { if(data.node.id === $.jstree.root && !this._data.core.loaded) { @@ -830,24 +887,24 @@ 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(); + $(e.currentTarget).filter('.jstree-hovered').trigger('mouseleave'); this.element.attr('tabindex', '0'); }, this)) .on('focus.jstree', '.jstree-anchor', $.proxy(function (e) { var tmp = this.get_node(e.currentTarget); if(tmp && tmp.id) { this._data.core.focused = tmp.id; } - this.element.find('.jstree-hovered').not(e.currentTarget).mouseleave(); - $(e.currentTarget).mouseenter(); + this.element.find('.jstree-hovered').not(e.currentTarget).trigger('mouseleave'); + $(e.currentTarget).trigger('mouseenter'); this.element.attr('tabindex', '-1'); }, this)) .on('focus.jstree', $.proxy(function () { - if(+(new Date()) - was_click > 500 && !this._data.core.focused) { + if(+(new Date()) - was_click > 500 && !this._data.core.focused && this.settings.core.restore_focus) { was_click = 0; var act = this.get_node(this.element.attr('aria-activedescendant'), true); if(act) { act.find('> .jstree-anchor').focus(); } @@ -964,10 +1021,13 @@ */ get_node : function (obj, as_dom) { if(obj && obj.id) { obj = obj.id; } + if (obj instanceof $ && obj.length && obj[0].id) { + obj = obj[0].id; + } var dom; try { if(this._model.data[obj]) { obj = this._model.data[obj]; } @@ -975,14 +1035,14 @@ obj = this._model.data[obj.replace(/^#/, '')]; } else if(typeof obj === "string" && (dom = $('#' + obj.replace($.jstree.idregex,'\\$&'), this.element)).length && this._model.data[dom.closest('.jstree-node').attr('id')]) { obj = this._model.data[dom.closest('.jstree-node').attr('id')]; } - else if((dom = $(obj, this.element)).length && this._model.data[dom.closest('.jstree-node').attr('id')]) { + else if((dom = this.element.find(obj)).length && this._model.data[dom.closest('.jstree-node').attr('id')]) { obj = this._model.data[dom.closest('.jstree-node').attr('id')]; } - else if((dom = $(obj, this.element)).length && dom.hasClass('jstree')) { + else if((dom = this.element.find(obj)).length && dom.hasClass('jstree')) { obj = this._model.data[$.jstree.root]; } else { return false; } @@ -1112,11 +1172,11 @@ return false; } return obj.parent; }, /** - * get a jQuery collection of all the children of a node (node must be rendered) + * get a jQuery collection of all the children of a node (node must be rendered), returns false on error * @name get_children_dom(obj) * @param {mixed} obj * @return {jQuery} */ get_children_dom : function (obj) { @@ -1272,11 +1332,11 @@ } }, this)); return true; }, /** - * load an array of nodes (will also load unavailable nodes as soon as the appear in the structure). Used internally. + * load an array of nodes (will also load unavailable nodes as soon as they appear in the structure). Used internally. * @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 */ @@ -1451,11 +1511,11 @@ * @name _node_changed(obj [, callback]) * @param {mixed} obj */ _node_changed : function (obj) { obj = this.get_node(obj); - if(obj) { + if (obj && $.inArray(obj.id, this._model.changed) === -1) { this._model.changed.push(obj.id); } }, /** * appends HTML content to the tree. Used internally. @@ -1543,10 +1603,11 @@ 'm' : this._model.data, 't_id' : this._id, 't_cnt' : this._cnt, 'sel' : this._data.core.selected }, + inst = this, func = function (data, undefined) { if(data.data) { data = data.data; } var dat = data.dat, par = data.par, chd = [], @@ -1753,14 +1814,25 @@ // 1) convert to object (foreach) for(i = 0, j = dat.length; i < j; i++) { if(!dat[i].children) { dat[i].children = []; } + if(!dat[i].state) { + dat[i].state = {}; + } m[dat[i].id.toString()] = dat[i]; } // 2) populate children (foreach) for(i = 0, j = dat.length; i < j; i++) { + if (!m[dat[i].parent.toString()]) { + if (typeof inst !== "undefined") { + inst._data.core.last_error = { 'error' : 'parse', 'plugin' : 'core', 'id' : 'core_07', 'reason' : 'Node with invalid parent', 'data' : JSON.stringify({ 'id' : dat[i].id.toString(), 'parent' : dat[i].parent.toString() }) }; + inst.settings.core.error.call(inst, inst._data.core.last_error); + } + continue; + } + m[dat[i].parent.toString()].children.push(dat[i].id.toString()); // populate parent.children_d p.children_d.push(dat[i].id.toString()); } // 3) normalize && populate parents and children_d with recursion @@ -2220,11 +2292,11 @@ tmp.children.push(c); if(e.children_d.length) { tmp.children_d = tmp.children_d.concat(e.children_d); } } - tmp.children_d = tmp.children_d.concat(tmp.children); + tmp.children_d = tmp.children.concat(tmp.children_d); } if(d && d.children && d.children === true) { tmp.state.loaded = false; tmp.children = []; tmp.children_d = []; @@ -2257,11 +2329,11 @@ f.className = this.get_container_ul()[0].className; f.setAttribute('role','group'); this.element.empty().append(f); //this.get_container_ul()[0].appendChild(f); } - if(fe !== null) { + if(fe !== null && this.settings.core.restore_focus) { tmp = this.get_node(fe, true); if(tmp && tmp.length && tmp.children('.jstree-anchor')[0] !== document.activeElement) { tmp.children('.jstree-anchor').focus(); } else { @@ -2433,10 +2505,13 @@ } if(obj.state.hidden) { c += ' jstree-hidden'; } + if (obj.state.loading) { + c += ' jstree-loading'; + } if(obj.state.loaded && !has_children) { c += ' jstree-leaf'; } else { c += obj.state.opened && obj.state.loaded ? ' jstree-open' : ' jstree-closed'; @@ -2537,11 +2612,11 @@ }, this), 0); } return node; }, /** - * opens a node, revaling its children. If the node is not loaded it will be loaded and opened once ready. + * opens a node, revealing its children. If the node is not loaded it will be loaded and opened once ready. * @name open_node(obj [, callback, animation]) * @param {mixed} obj the node to open * @param {Function} callback a function to execute once the node is opened * @param {Number} animation the animation duration in milliseconds when opening the node (overrides the `core.animation` setting). Use `false` for no animation. * @trigger open_node.jstree, after_open.jstree, before_open.jstree @@ -2741,11 +2816,11 @@ if(this.is_open(obj)) { return this.close_node(obj); } }, /** - * opens all nodes within a node (or the tree), revaling their children. If the node is not loaded it will be loaded and opened once ready. + * opens all nodes within a node (or the tree), revealing their children. If the node is not loaded it will be loaded and opened once ready. * @name open_all([obj, animation, original_obj]) * @param {mixed} obj the node to open recursively, omit to open all nodes in the tree * @param {Number} animation the animation duration in milliseconds when opening the nodes, the default is no animation * @param {jQuery} reference to the node that started the process (internal use) * @trigger open_all.jstree @@ -2782,11 +2857,11 @@ */ this.trigger('open_all', { "node" : this.get_node(original_obj) }); } }, /** - * closes all nodes within a node (or the tree), revaling their children + * closes all nodes within a node (or the tree), revealing their children * @name close_all([obj, animation]) * @param {mixed} obj the node to close recursively, omit to close all nodes in the tree * @param {Number} animation the animation duration in milliseconds when closing the nodes, the default is no animation * @trigger close_all.jstree */ @@ -3358,10 +3433,11 @@ */ get_state : function () { var state = { 'core' : { 'open' : [], + 'loaded' : [], 'scroll' : { 'left' : this.element.scrollLeft(), 'top' : this.element.scrollTop() }, /*! @@ -3375,10 +3451,13 @@ } }, i; for(i in this._model.data) { if(this._model.data.hasOwnProperty(i)) { if(i !== $.jstree.root) { + if(this._model.data[i].state.loaded && this.settings.core.loaded_state) { + state.core.loaded.push(i); + } if(this._model.data[i].state.opened) { state.core.open.push(i); } if(this._model.data[i].state.selected) { state.core.selected.push(i); @@ -3401,10 +3480,23 @@ 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.loaded) { + if(!this.settings.core.loaded_state || !$.isArray(state.core.loaded) || !state.core.loaded.length) { + delete state.core.loaded; + this.set_state(state, callback); + } + else { + this._load_nodes(state.core.loaded, function (nodes) { + delete state.core.loaded; + this.set_state(state, callback); + }); + } + return false; + } if(state.core.open) { if(!$.isArray(state.core.open) || !state.core.open.length) { delete state.core.open; this.set_state(state, callback); } @@ -3931,10 +4023,14 @@ 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) && (chk === "move_node" && $.inArray(obj.id, par.children) === pos)) { + this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_08', 'reason' : 'Moving node to its current position', 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) }; + return false; + } 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; } } @@ -4403,11 +4499,11 @@ ai = a.children("i:visible"), w1 = oi.width() * oi.length, w2 = ai.width() * ai.length, */ t = default_text; - h1 = $("<"+"div />", { css : { "position" : "absolute", "top" : "-200px", "left" : (rtl ? "0px" : "-1000px"), "visibility" : "hidden" } }).appendTo("body"); + h1 = $("<"+"div />", { css : { "position" : "absolute", "top" : "-200px", "left" : (rtl ? "0px" : "-1000px"), "visibility" : "hidden" } }).appendTo(document.body); h2 = $("<"+"input />", { "value" : t, "class" : "jstree-rename-input", // "size" : t.length, "css" : { @@ -4429,10 +4525,11 @@ if(v === "") { v = t; } h1.remove(); s.replaceWith(a); s.remove(); t = f ? t : $('<div></div>').append($.parseHTML(t)).html(); + obj = this.get_node(obj); this.set_text(obj, t); nv = !!this.rename_node(obj, f ? $('<div></div>').text(v).text() : $('<div></div>').append($.parseHTML(v)).html()); if(!nv) { this.set_text(obj, t); // move this up? and fix #483 } @@ -4703,10 +4800,11 @@ if(!obj || obj.id === $.jstree.root) { return false; } old = obj.icon; obj.icon = icon === true || icon === null || icon === undefined || icon === '' ? true : icon; dom = this.get_node(obj, true).children(".jstree-anchor").children(".jstree-themeicon"); if(icon === false) { + dom.removeClass('jstree-themeicon-custom ' + old).css("background","").removeAttr("rel"); this.hide_icon(obj); } else if(icon === true || icon === null || icon === undefined || icon === '') { dom.removeClass('jstree-themeicon-custom ' + old).css("background","").removeAttr("rel"); if(old === false) { this.show_icon(obj); } @@ -5075,18 +5173,19 @@ // 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)); 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]; - } - }); + var temp = obj.children_d.concat(obj.id); + for (i = 0, j = temp.length; i < j; i++) { + if (selectedIds.indexOf(temp[i]) > -1) { + sel[temp[i]] = true; + } + else { + delete sel[temp[i]]; + } + } } // apply up if(s.indexOf('up') !== -1) { while(par && par.id !== $.jstree.root) { @@ -5140,11 +5239,11 @@ // apply down if(s.indexOf('down') !== -1) { var selectedIds = this._cascade_new_checked_state(obj.id, false); - cur = cur.filter(function(id) { + cur = $.vakata.array_filter(cur, function(id) { return allIds.indexOf(id) === -1 || selectedIds.indexOf(id) > -1; }); } // only apply up if cascade up is enabled and if this node is not selected @@ -5160,11 +5259,11 @@ if(tmp && tmp.length) { tmp.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked'); } } - cur = cur.filter(function(id) { + cur = $.vakata.array_filter(cur, function(id) { return obj.parents.indexOf(id) === -1; }); } this._data[ t ? 'core' : 'checkbox' ].selected = cur; @@ -5256,20 +5355,22 @@ p = this.get_node(p.parent); } }, this)); } }; - /** - * set the undetermined state where and if necessary. Used internally. - * @private - * @name _undetermined() + * get an array of all nodes whose state is "undetermined" + * @name get_undetermined([full]) + * @param {boolean} full: if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned + * @return {Array} * @plugin checkbox */ - this._undetermined = function () { - 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; + this.get_undetermined = function (full) { + if (this.settings.checkbox.cascade.indexOf('undetermined') === -1) { + 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, r = []; 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) { break; @@ -5318,18 +5419,32 @@ } } } } }); + for (i = 0, j = p.length; i < j; i++) { + if(!m[p[i]].state[ t ? 'selected' : 'checked' ]) { + r.push(full ? m[p[i]] : p[i]); + } + } + return r; + }; + /** + * set the undetermined state where and if necessary. Used internally. + * @private + * @name _undetermined() + * @plugin checkbox + */ + this._undetermined = function () { + if(this.element === null) { return; } + var p = this.get_undetermined(false), i, j, s; this.element.find('.jstree-undetermined').removeClass('jstree-undetermined'); - for(i = 0, j = p.length; i < j; i++) { - if(!m[p[i]].state[ t ? 'selected' : 'checked' ]) { - s = this.get_node(p[i], true); - if(s && s.length) { - s.children('.jstree-anchor').children('.jstree-checkbox').addClass('jstree-undetermined'); - } + for (i = 0, j = p.length; i < j; i++) { + s = this.get_node(p[i], true); + if(s && s.length) { + s.children('.jstree-anchor').children('.jstree-checkbox').addClass('jstree-undetermined'); } } }; this.redraw_node = function(obj, deep, is_callback, force_render) { obj = parent.redraw_node.apply(this, arguments); @@ -5430,11 +5545,11 @@ this.trigger('disable_checkbox', { 'node' : obj }); } }; /** * enable a node's checkbox - * @name disable_checkbox(obj) + * @name enable_checkbox(obj) * @param {mixed} obj an array can be used too * @trigger enable_checkbox.jstree * @plugin checkbox */ this.enable_checkbox = function (obj) { @@ -5488,67 +5603,69 @@ } 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). + * Cascades checked state to 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 + * @private + * @param {string} id the node ID + * @param {bool} checkedState should the nodes be checked or not * @returns {Array} Array of all node id's (in this tree branch) that are checked. */ - this._cascade_new_checked_state = function(id, checkedState) { + 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 = []; + var selectedChildrenIds = [], i, j, selectedChildIds; 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); + //First try and check/uncheck the children + if (node.children) { + for (i = 0, j = node.children.length; i < j; i++) { + var childId = node.children[i]; + 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 . + //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 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'); + 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. + //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; + 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; + 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); + selectedChildIds = this.get_checked_descendants(id); if (node.state[ t ? 'selected' : 'checked' ]) { selectedChildIds.push(node.id); } @@ -5558,18 +5675,21 @@ return selectedNodeIds; }; /** * Gets ids of nodes selected in branch (of tree) specified by id (does not include the node specified by id) - * @param id + * @name get_checked_descendants(obj) + * @param {string} id the node ID + * @return {Array} array of IDs + * @plugin checkbox */ - this.get_checked_descendants = function(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 $.vakata.array_filter(node.children_d, function(_id) { return self._model.data[_id].state[ t ? 'selected' : 'checked' ]; }); }; /** @@ -5724,11 +5844,11 @@ * @return {Array} * @plugin checkbox */ this.get_checked = function (full) { if(this.settings.checkbox.tie_selection) { return this.get_selected(full); } - return full ? $.map(this._data.checkbox.selected, $.proxy(function (i) { return this.get_node(i); }, this)) : this._data.checkbox.selected; + return full ? $.map(this._data.checkbox.selected, $.proxy(function (i) { return this.get_node(i); }, this)) : this._data.checkbox.selected.slice(); }; /** * get an array of all top level checked nodes (ignoring children of checked nodes) (if tie_selection is on in the settings this function will return the same as get_top_selected) * @name get_top_checked([full]) * @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned @@ -5811,11 +5931,11 @@ return false; } return res; }; this.refresh = function (skip_loading, forget_state) { - if(!this.settings.checkbox.tie_selection) { + if(this.settings.checkbox.tie_selection) { this._data.checkbox.selected = []; } return parent.refresh.apply(this, arguments); }; }; @@ -5838,11 +5958,11 @@ $.jstree.defaults.conditionalselect = function () { return true; }; $.jstree.plugins.conditionalselect = function (options, parent) { // own function this.activate_node = function (obj, e) { if(this.settings.conditionalselect.call(this, this.get_node(obj), e)) { - parent.activate_node.call(this, obj, e); + return parent.activate_node.call(this, obj, e); } }; }; @@ -6039,12 +6159,13 @@ 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.originalEvent.changedTouches[0].clientX) > 50 || Math.abs(ey - e.originalEvent.changedTouches[0].clientY) > 50)) { + if(cto && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0] && (Math.abs(ex - e.originalEvent.changedTouches[0].clientX) > 10 || Math.abs(ey - e.originalEvent.changedTouches[0].clientY) > 10)) { clearTimeout(cto); + $.vakata.context.hide(); } }) .on('touchend.vakata.jstree', function (e) { if(cto) { clearTimeout(cto); @@ -6312,11 +6433,11 @@ } if($.vakata.context._parse(data)) { vakata_context.element.html(vakata_context.html); } if(vakata_context.items.length) { - vakata_context.element.appendTo("body"); + vakata_context.element.appendTo(document.body); e = vakata_context.element; x = vakata_context.position_x; y = vakata_context.position_y; w = e.width(); h = e.height(); @@ -6368,11 +6489,11 @@ $.vakata.context._trigger("hide"); } } }; $(function () { - right_to_left = $("body").css("direction") === "rtl"; + right_to_left = $(document.body).css("direction") === "rtl"; var to = false; vakata_context.element = $("<ul class='vakata-context'></ul>"); vakata_context.element .on("mouseenter", "li", function (e) { @@ -6656,15 +6777,27 @@ lastev = false, opento = false, marker = $('<div id="jstree-marker">&#160;</div>').hide(); //.appendTo('body'); $(document) + .on('dragover.vakata.jstree', function (e) { + if (elm) { + $.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg }); + } + }) + .on('drop.vakata.jstree', function (e) { + if (elm) { + $.vakata.dnd._trigger('stop', e, { 'helper': $(), 'element': elm, 'data': drg }); + elm = null; + drg = null; + } + }) .on('dnd_start.vakata.jstree', function (e, data) { lastmv = false; lastev = false; if(!data || !data.data || !data.data.jstree) { return; } - marker.appendTo('body'); //.show(); + marker.appendTo(document.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' || isDifferentNode) { @@ -6794,11 +6927,11 @@ } $('.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'; + //data.event.originalEvent.dataTransfer.dropEffect = 'none'; } marker.hide(); }) .on('dnd_scroll.vakata.jstree', function (e, data) { if(!data || !data.data || !data.data.jstree) { return; } @@ -6887,11 +7020,11 @@ scroll_speed : 10, scroll_proximity : 20, helper_left : 5, helper_top : 10, threshold : 5, - threshold_touch : 50 + threshold_touch : 10 }, _trigger : function (event_name, e, data) { if (data === undefined) { data = $.vakata.dnd._get(); } @@ -7005,11 +7138,11 @@ if( Math.abs(e.pageX - vakata_dnd.init_x) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold) || Math.abs(e.pageY - vakata_dnd.init_y) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold) ) { if(vakata_dnd.helper) { - vakata_dnd.helper.appendTo("body"); + vakata_dnd.helper.appendTo(document.body); vakata_dnd.helper_w = vakata_dnd.helper.outerWidth(); } vakata_dnd.is_drag = true; $(vakata_dnd.target).one('click.vakata', false); /** @@ -7752,11 +7885,17 @@ /** * A function that will be executed prior to restoring state with one argument - the state object. Can be used to clear unwanted parts of the state. * @name $.jstree.defaults.state.filter * @plugin state */ - filter : false + filter : false, + /** + * Should loaded nodes be restored (setting this to true means that it is possible that the whole tree will be loaded for some users - use with caution). Defaults to `false` + * @name $.jstree.defaults.state.preserve_loaded + * @plugin state + */ + preserve_loaded : false }; $.jstree.plugins.state = function (options, parent) { this.bind = function () { parent.bind.call(this); var bind = $.proxy(function () { @@ -7782,11 +7921,15 @@ * save the state * @name save_state() * @plugin state */ this.save_state = function () { - var st = { 'state' : this.get_state(), 'ttl' : this.settings.state.ttl, 'sec' : +(new Date()) }; + var tm = this.get_state(); + if (!this.settings.state.preserve_loaded) { + delete tm.core.loaded; + } + var st = { 'state' : tm, 'ttl' : this.settings.state.ttl, 'sec' : +(new Date()) }; $.vakata.storage.set(this.settings.state.key, JSON.stringify(st)); }; /** * restore the state from the user's computer * @name restore_state() @@ -7797,10 +7940,13 @@ if(!!k) { try { k = JSON.parse(k); } catch(ex) { return false; } } if(!!k && k.ttl && k.sec && +(new Date()) - k.sec > k.ttl) { return false; } if(!!k && k.state) { k = k.state; } if(!!k && $.isFunction(this.settings.state.filter)) { k = this.settings.state.filter.call(this, k); } if(!!k) { + if (!this.settings.state.preserve_loaded) { + delete k.core.loaded; + } this.element.one("set_state.jstree", function (e, data) { data.instance.trigger('restore_state', { 'state' : $.extend(true, {}, k) }); }); this.set_state(k); return true; } return false; @@ -8201,10 +8347,16 @@ * @name $.jstree.defaults.unique.case_sensitive * @plugin unique */ case_sensitive : false, /** + * Indicates if white space should be trimmed before the comparison. Default is `false`. + * @name $.jstree.defaults.unique.trim_whitespace + * @plugin unique + */ + trim_whitespace : false, + /** * A callback executed in the instance's scope when a new node is created and the name is already taken, the two arguments are the conflicting name and the counter. The default will produce results like `New node (2)`. * @name $.jstree.defaults.unique.duplicate * @plugin unique */ duplicate : function (name, counter) { @@ -8219,20 +8371,36 @@ par = par && par.id ? par : this.get_node(par); if(!par || !par.children) { return true; } var n = chk === "rename_node" ? pos : obj.text, c = [], s = this.settings.unique.case_sensitive, - m = this._model.data, i, j; + w = this.settings.unique.trim_whitespace, + m = this._model.data, i, j, t; for(i = 0, j = par.children.length; i < j; i++) { - c.push(s ? m[par.children[i]].text : m[par.children[i]].text.toLowerCase()); + t = m[par.children[i]].text; + if (!s) { + t = t.toLowerCase(); + } + if (w) { + t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); + } + c.push(t); } if(!s) { n = n.toLowerCase(); } + if (w) { n = n.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); } switch(chk) { case "delete_node": return true; case "rename_node": - i = ($.inArray(n, c) === -1 || (obj.text && obj.text[ s ? 'toString' : 'toLowerCase']() === n)); + t = obj.text || ''; + if (!s) { + t = t.toLowerCase(); + } + if (w) { + t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); + } + i = ($.inArray(n, c) === -1 || (obj.text && t === n)); if(!i) { this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_01', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) }; } return i; case "create_node": @@ -8268,19 +8436,40 @@ pos = pos === undefined ? "last" : pos; if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) { return parent.create_node.call(this, par, node, pos, callback, is_loaded); } if(!node) { node = {}; } - var tmp, n, dpc, i, j, m = this._model.data, s = this.settings.unique.case_sensitive, cb = this.settings.unique.duplicate; + var tmp, n, dpc, i, j, m = this._model.data, s = this.settings.unique.case_sensitive, w = this.settings.unique.trim_whitespace, cb = this.settings.unique.duplicate, t; n = tmp = this.get_string('New node'); dpc = []; for(i = 0, j = par.children.length; i < j; i++) { - dpc.push(s ? m[par.children[i]].text : m[par.children[i]].text.toLowerCase()); + t = m[par.children[i]].text; + if (!s) { + t = t.toLowerCase(); + } + if (w) { + t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); + } + dpc.push(t); } i = 1; - while($.inArray(s ? n : n.toLowerCase(), dpc) !== -1) { + t = n; + if (!s) { + t = t.toLowerCase(); + } + if (w) { + t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); + } + while($.inArray(t, dpc) !== -1) { n = cb.call(this, tmp, (++i)).toString(); + t = n; + if (!s) { + t = t.toLowerCase(); + } + if (w) { + t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); + } } node.text = n; } return parent.create_node.call(this, par, node, pos, callback, is_loaded); }; @@ -8393,11 +8582,11 @@ return obj; }; }; // include the wholerow plugin by default // $.jstree.defaults.plugins.push("wholerow"); - if(document.registerElement && Object && Object.create) { + if(window.customElements && Object && Object.create) { var proto = Object.create(HTMLElement.prototype); proto.createdCallback = function () { var c = { core : {}, plugins : [] }, i; for(i in $.jstree.plugins) { if($.jstree.plugins.hasOwnProperty(i) && this.attributes[i]) { @@ -8414,10 +8603,10 @@ } $(this).jstree(c); }; // proto.attributeChangedCallback = function (name, previous, value) { }; try { - document.registerElement("vakata-jstree", { prototype: proto }); - } catch(ignore) { } + window.customElements.define("vakata-jstree", function() {}, { prototype: proto }); + } catch (ignore) { } } })); \ No newline at end of file