diff --git a/bower.json b/bower.json index 121ffa4b..129cf33c 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "jquery.fancytree", "description": "JavaScript tree view / tree grid plugin with support for keyboard, inline editing, filtering, checkboxes, drag'n'drop, and lazy loading", - "version": "2.30.3-0", + "version": "2.31.0", "main": [ "dist/jquery.fancytree-all-deps.min.js" ], @@ -47,4 +47,4 @@ "jquery-ui": ">=1.8.6" }, "devDependencies": {} -} +} \ No newline at end of file diff --git a/dist/LICENSE.txt b/dist/LICENSE.txt index f19d80a7..959cf1c1 100644 --- a/dist/LICENSE.txt +++ b/dist/LICENSE.txt @@ -1,5 +1,5 @@ Copyright 2008-2019 Martin Wendt, -http://wwWendt.de/ +https://wwWendt.de/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/dist/jquery.fancytree-all-deps.js b/dist/jquery.fancytree-all-deps.js index 718394c8..d2bcec78 100644 --- a/dist/jquery.fancytree-all-deps.js +++ b/dist/jquery.fancytree-all-deps.js @@ -1,4 +1,4 @@ -/*! jQuery Fancytree Plugin - 2.30.2 - 2019-01-13T08:17:01Z +/*! jQuery Fancytree Plugin - 2.31.0 - 2019-05-31T11:32:38Z * https://github.com/mar10/fancytree * Copyright (c) 2019 Martin Wendt; Licensed MIT */ @@ -1361,12 +1361,12 @@ var uniqueId = $.fn.extend( { * Tree view control with support for lazy loading and much more. * https://github.com/mar10/fancytree/ * - * Copyright (c) 2008-2019, Martin Wendt (http://wwWendt.de) + * Copyright (c) 2008-2019, Martin Wendt (https://wwWendt.de) * Released under the MIT license * https://github.com/mar10/fancytree/wiki/LicenseInfo * - * @version 2.30.2 - * @date 2019-01-13T08:17:01Z + * @version 2.31.0 + * @date 2019-05-31T11:32:38Z */ /** Core Fancytree module. @@ -1402,8 +1402,8 @@ var uniqueId = $.fn.extend( { attr, FT = null, // initialized below TEST_IMG = new RegExp(/\.|\//), // strings are considered image urls if they contain '.' or '/' - REX_HTML = /[&<>"'\/]/g, // Escape those characters - REX_TOOLTIP = /[<>"'\/]/g, // Don't escape `&` in tooltips + REX_HTML = /[&<>"'/]/g, // Escape those characters + REX_TOOLTIP = /[<>"'/]/g, // Don't escape `&` in tooltips RECURSIVE_REQUEST_ERROR = "$recursive_request", ENTITY_MAP = { "&": "&", @@ -1623,28 +1623,30 @@ var uniqueId = $.fn.extend( { target = {}; } if (i === length) { - throw "need at least two args"; + throw Error("need at least two args"); } for (; i < length; i++) { // Only deal with non-null/undefined values if ((options = arguments[i]) != null) { // Extend the base object for (name in options) { - src = target[name]; - copy = options[name]; - // Prevent never-ending loop - if (target === copy) { - continue; - } - // Recurse if we're merging plain objects - // (NOTE: unlike $.extend, we don't merge arrays, but relace them) - if (copy && $.isPlainObject(copy)) { - clone = src && $.isPlainObject(src) ? src : {}; - // Never move original objects, clone them - target[name] = _simpleDeepMerge(clone, copy); - // Don't bring in undefined values - } else if (copy !== undefined) { - target[name] = copy; + if (options.hasOwnProperty(name)) { + src = target[name]; + copy = options[name]; + // Prevent never-ending loop + if (target === copy) { + continue; + } + // Recurse if we're merging plain objects + // (NOTE: unlike $.extend, we don't merge arrays, but replace them) + if (copy && $.isPlainObject(copy)) { + clone = src && $.isPlainObject(src) ? src : {}; + // Never move original objects, clone them + target[name] = _simpleDeepMerge(clone, copy); + // Don't bring in undefined values + } else if (copy !== undefined) { + target[name] = copy; + } } } } @@ -1747,11 +1749,10 @@ var uniqueId = $.fn.extend( { return $.Deferred(function() { this.resolve(); }).promise(); - } else { - return $.Deferred(function() { - this.resolveWith(context, argArray); - }).promise(); } + return $.Deferred(function() { + this.resolveWith(context, argArray); + }).promise(); } function _getRejectedPromise(context, argArray) { @@ -1759,11 +1760,10 @@ var uniqueId = $.fn.extend( { return $.Deferred(function() { this.reject(); }).promise(); - } else { - return $.Deferred(function() { - this.rejectWith(context, argArray); - }).promise(); } + return $.Deferred(function() { + this.rejectWith(context, argArray); + }).promise(); } function _makeResolveFunc(deferred, context) { @@ -1969,6 +1969,11 @@ var uniqueId = $.fn.extend( { for (var i = 0, l = children.length; i < l; i++) { this.children.push(new FancytreeNode(this, children[i])); } + this.tree._callHook( + "treeStructureChanged", + this.tree, + "setChildren" + ); }, /** * Append (or insert) a list of child nodes. @@ -2146,12 +2151,14 @@ var uniqueId = $.fn.extend( { IGNORE_MAP = { children: true, expanded: true, parent: true }; // TODO: should be global for (name in patch) { - v = patch[name]; - if (!IGNORE_MAP[name] && !$.isFunction(v)) { - if (NODE_ATTR_MAP[name]) { - this[name] = v; - } else { - this.data[name] = v; + if (patch.hasOwnProperty(name)) { + v = patch[name]; + if (!IGNORE_MAP[name] && !$.isFunction(v)) { + if (NODE_ATTR_MAP[name]) { + this[name] = v; + } else { + this.data[name] = v; + } } } } @@ -2247,7 +2254,7 @@ var uniqueId = $.fn.extend( { * @param {*} msg string or object or array of such */ error: function(msg) { - if (this.options.debugLevel >= 1) { + if (this.tree.options.debugLevel >= 1) { Array.prototype.unshift.call(arguments, this.toString()); consoleApply("error", arguments); } @@ -2286,6 +2293,16 @@ var uniqueId = $.fn.extend( { }); return res; }, + /** Find a node relative to self. + * + * @param {number|string} where The keyCode that would normally trigger this move, + * or a keyword ('down', 'first', 'last', 'left', 'parent', 'right', 'up'). + * @returns {FancytreeNode} + * @since v2.31 + */ + findRelatedNode: function(where, includeHidden) { + return this.tree.findRelatedNode(this, where, includeHidden); + }, /* Apply selection state (internal use only) */ _changeSelectStatusAttrs: function(state) { var changed = false, @@ -2344,6 +2361,10 @@ var uniqueId = $.fn.extend( { this.visit(function(node) { node._changeSelectStatusAttrs(flag); + if (node.radiogroup) { + // #931: don't (de)select this branch + return "skip"; + } }); this.fixSelection3FromEndNodes(callOpts); }, @@ -2399,11 +2420,12 @@ var uniqueId = $.fn.extend( { } } } + // eslint-disable-next-line no-nested-ternary state = allSelected ? true : someSelected - ? undefined - : false; + ? undefined + : false; } else { // This is an end-node: simply report the status unselState = FT.evalOption( @@ -2415,6 +2437,15 @@ var uniqueId = $.fn.extend( { ); state = unselState == null ? !!node.selected : !!unselState; } + // #939: Keep a `partsel` flag that was explicitly set on a lazy node + if ( + node.partsel && + !node.selected && + node.lazy && + node.children == null + ) { + state = undefined; + } node._changeSelectStatusAttrs(state); return state; } @@ -2463,6 +2494,7 @@ var uniqueId = $.fn.extend( { } } } + // eslint-disable-next-line no-nested-ternary state = allSelected ? true : someSelected ? undefined : false; node._changeSelectStatusAttrs(state); }); @@ -2555,19 +2587,18 @@ var uniqueId = $.fn.extend( { }); return res.join(separator); }, - /** Return the parent keys separated by options.keyPathSeparator, e.g. "id_1/id_17/id_32". + /** Return the parent keys separated by options.keyPathSeparator, e.g. "/id_1/id_17/id_32". + * + * (Unlike `node.getPath()`, this method prepends a "/" and inverts the first argument.) + * + * @see FancytreeNode#getPath * @param {boolean} [excludeSelf=false] * @returns {string} */ getKeyPath: function(excludeSelf) { - var path = [], - sep = this.tree.options.keyPathSeparator; - this.visitParents(function(n) { - if (n.parent) { - path.unshift(n.key); - } - }, !excludeSelf); - return sep + path.join(sep); + var sep = this.tree.options.keyPathSeparator; + + return sep + this.getPath(!excludeSelf, "key", sep); }, /** Return the last child of this node or null. * @returns {FancytreeNode | null} @@ -2631,6 +2662,30 @@ var uniqueId = $.fn.extend( { } return l; }, + /** Return a string representing the hierachical node path, e.g. "a/b/c". + * @param {boolean} [includeSelf=true] + * @param {string | function} [part="title"] node property name or callback + * @param {string} [separator="/"] + * @returns {string} + * @since v2.31 + */ + getPath: function(includeSelf, part, separator) { + includeSelf = includeSelf !== false; + part = part || "title"; + separator = separator || "/"; + + var val, + path = [], + isFunc = $.isFunction(part); + + this.visitParents(function(n) { + if (n.parent) { + val = isFunc ? part(n) : n[part]; + path.unshift(val); + } + }, includeSelf); + return path.join(separator); + }, /** Return the predecessor node (under the same parent) or null. * @returns {FancytreeNode | null} */ @@ -2857,13 +2912,39 @@ var uniqueId = $.fn.extend( { isVisible: function() { var i, l, + n, + hasFilter = this.tree.enableFilter, parents = this.getParentList(false, false); + // TODO: check $(n.span).is(":visible") + // i.e. return false for nodes (but not parents) that are hidden + // by a filter + if (hasFilter && !this.match && !this.subMatchCount) { + this.debug( + "isVisible: HIDDEN (" + + hasFilter + + ", " + + this.match + + ", " + + this.match + + ")" + ); + return false; + } + for (i = 0, l = parents.length; i < l; i++) { - if (!parents[i].expanded) { + n = parents[i]; + + if (!n.expanded) { + this.debug("isVisible: HIDDEN (parent collapsed)"); return false; } + // if (hasFilter && !n.match && !n.subMatchCount) { + // this.debug("isVisible: HIDDEN (" + hasFilter + ", " + this.match + ", " + this.match + ")"); + // return false; + // } } + this.debug("isVisible: VISIBLE"); return true; }, /** Deprecated. @@ -2883,7 +2964,7 @@ var uniqueId = $.fn.extend( { load: function(forceReload) { var res, source, - that = this, + self = this, wasExpanded = this.isExpanded(); _assert(this.isLazy(), "load() requires a lazy node"); @@ -2909,11 +2990,11 @@ var uniqueId = $.fn.extend( { if (wasExpanded) { this.expanded = true; res.always(function() { - that.render(); + self.render(); }); } else { res.always(function() { - that.renderStatus(); // fix expander icon to 'loaded' + self.renderStatus(); // fix expander icon to 'loaded' }); } return res; @@ -2926,7 +3007,7 @@ var uniqueId = $.fn.extend( { */ makeVisible: function(opts) { var i, - that = this, + self = this, deferreds = [], dfd = new $.Deferred(), parents = this.getParentList(false, false), @@ -2936,15 +3017,15 @@ var uniqueId = $.fn.extend( { // Expand bottom-up, so only the top node is animated for (i = len - 1; i >= 0; i--) { - // that.debug("pushexpand" + parents[i]); + // self.debug("pushexpand" + parents[i]); deferreds.push(parents[i].setExpanded(true, opts)); } $.when.apply($, deferreds).done(function() { // All expands have finished - // that.debug("expand DONE", scroll); + // self.debug("expand DONE", scroll); if (scroll) { - that.scrollIntoView(effects).done(function() { - // that.debug("scroll DONE"); + self.scrollIntoView(effects).done(function() { + // self.debug("scroll DONE"); dfd.resolve(); }); } else { @@ -2976,6 +3057,7 @@ var uniqueId = $.fn.extend( { } } var pos, + tree = this.tree, prevParent = this.parent, targetParent = mode === "child" ? targetNode : targetNode.parent; @@ -3057,10 +3139,10 @@ var uniqueId = $.fn.extend( { targetParent.triggerModifyChild("add", this); } // Handle cross-tree moves - if (this.tree !== targetNode.tree) { + if (tree !== targetNode.tree) { // Fix node.tree for all source nodes // _assert(false, "Cross-tree move is not yet implemented."); - this.warn("Cross-tree moveTo is experimantal!"); + this.warn("Cross-tree moveTo is experimental!"); this.visit(function(n) { // TODO: fix selection state and activation, ... n.tree = targetNode.tree; @@ -3071,6 +3153,7 @@ var uniqueId = $.fn.extend( { // if( !targetParent.expanded ){ // prevParent.ul.removeChild(this.li); // } + tree._callHook("treeStructureChanged", tree, "moveTo"); // Update HTML markup if (!prevParent.isDescendantOf(targetParent)) { @@ -3126,117 +3209,38 @@ var uniqueId = $.fn.extend( { * @returns {$.Promise} */ navigate: function(where, activate) { - var i, - parents, - res, - handled = true, - KC = $.ui.keyCode, - sib = null; - - // Navigate to node - function _goto(n) { - if (n) { - // setFocus/setActive will scroll later (if autoScroll is specified) - try { - n.makeVisible({ scrollIntoView: false }); - } catch (e) {} // #272 - // Node may still be hidden by a filter - if (!$(n.span).is(":visible")) { - n.debug("Navigate: skipping hidden node"); - n.navigate(where, activate); - return; - } - return activate === false ? n.setFocus() : n.setActive(); - } - } + var node, + KC = $.ui.keyCode; + // Handle optional expand/collapse action for LEFT/RIGHT switch (where) { - case KC.BACKSPACE: - if (this.parent && this.parent.parent) { - res = _goto(this.parent); - } - break; - case KC.HOME: - this.tree.visit(function(n) { - // goto first visible node - if ($(n.span).is(":visible")) { - res = _goto(n); - return false; - } - }); - break; - case KC.END: - this.tree.visit(function(n) { - // goto last visible node - if ($(n.span).is(":visible")) { - res = n; - } - }); - if (res) { - res = _goto(res); - } - break; + case "left": case KC.LEFT: if (this.expanded) { - this.setExpanded(false); - res = _goto(this); - } else if (this.parent && this.parent.parent) { - res = _goto(this.parent); + return this.setExpanded(false); } break; + case "right": case KC.RIGHT: if (!this.expanded && (this.children || this.lazy)) { - this.setExpanded(); - res = _goto(this); - } else if (this.children && this.children.length) { - res = _goto(this.children[0]); - } - break; - case KC.UP: - sib = this.getPrevSibling(); - // #359: skip hidden sibling nodes, preventing a _goto() recursion - while (sib && !$(sib.span).is(":visible")) { - sib = sib.getPrevSibling(); - } - while ( - sib && - sib.expanded && - sib.children && - sib.children.length - ) { - sib = sib.children[sib.children.length - 1]; - } - if (!sib && this.parent && this.parent.parent) { - sib = this.parent; - } - res = _goto(sib); - break; - case KC.DOWN: - if ( - this.expanded && - this.children && - this.children.length - ) { - sib = this.children[0]; - } else { - parents = this.getParentList(false, true); - for (i = parents.length - 1; i >= 0; i--) { - sib = parents[i].getNextSibling(); - // #359: skip hidden sibling nodes, preventing a _goto() recursion - while (sib && !$(sib.span).is(":visible")) { - sib = sib.getNextSibling(); - } - if (sib) { - break; - } - } + return this.setExpanded(); } - res = _goto(sib); break; - default: - handled = false; } - return res || _getResolvedPromise(); + // Otherwise activate or focus the related node + node = this.findRelatedNode(where); + if (node) { + // setFocus/setActive will scroll later (if autoScroll is specified) + try { + node.makeVisible({ scrollIntoView: false }); + } catch (e) {} // #272 + if (activate === false) { + return node.setFocus(); + } + return node.setActive(); + } + this.warn("Could not find related node '" + where + "'."); + return _getResolvedPromise(); }, /** * Remove this node (not allowed for system root). @@ -3312,7 +3316,7 @@ var uniqueId = $.fn.extend( { var res, parent = this.parent, pos = $.inArray(this, parent.children), - that = this; + self = this; _assert( this.isPagingNode(), @@ -3322,7 +3326,7 @@ var uniqueId = $.fn.extend( { res = this.tree._callHook("nodeLoadChildren", this, source); res.done(function(data) { // New nodes are currently children of `this`. - var children = that.children; + var children = self.children; // Prepend newly loaded child nodes to `this` // Move new children after self for (i = 0; i < children.length; i++) { @@ -3334,14 +3338,14 @@ var uniqueId = $.fn.extend( { ); // Remove self - that.children = null; - that.remove(); + self.children = null; + self.remove(); // Redraw new nodes parent.render(); // TODO: set node.partload = false if this was tha last paging node? // parent.addPagingNode(false); }).fail(function() { - that.setExpanded(); + self.setExpanded(); }); return res; // $.error("Not implemented: replaceWith()"); @@ -3399,7 +3403,9 @@ var uniqueId = $.fn.extend( { */ scrollIntoView: function(effects, options) { if (options !== undefined && _isNode(options)) { - throw "scrollIntoView() with 'topNode' option is deprecated since 2014-05-08. Use 'options.topNode' instead."; + throw Error( + "scrollIntoView() with 'topNode' option is deprecated since 2014-05-08. Use 'options.topNode' instead." + ); } // The scroll parent is typically the plain tree's