Swift mode
- +CodeMirror

CodeMirror

CodeMirror

CodeMirror

CodeMirror

CodeMirror

CodeMirror

CodeMirror

CodeMirror

CodeMirror

CodeMirror

- container.
// For ext-table, we choose the nearest parent that has `position: relative`
@@ -3438,7 +3444,7 @@ var uniqueId = $.fn.extend( {
$scrollParent[0] === document ||
$scrollParent[0] === document.body
) {
- // `document` may returned by $().scrollParent(), if nothing is found,
+ // `document` may be returned by $().scrollParent(), if nothing is found,
// but would not work: (see #894)
this.debug(
"scrollIntoView(): normalizing scrollParent to 'window':",
@@ -3446,13 +3452,13 @@ var uniqueId = $.fn.extend( {
);
$scrollParent = $(window);
}
-
+ // eslint-disable-next-line one-var
var topNodeY,
nodeY,
horzScrollbarHeight,
containerOffsetTop,
dfd = new $.Deferred(),
- that = this,
+ self = this,
nodeHeight = $(this.span).height(),
topOfs = opts.scrollOfs.top || 0,
bottomOfs = opts.scrollOfs.bottom || 0,
@@ -3465,7 +3471,7 @@ var uniqueId = $.fn.extend( {
// this.debug("scrollIntoView(), scrollTop=" + scrollTop, opts.scrollOfs);
// _assert($(this.span).is(":visible"), "scrollIntoView node is invisible"); // otherwise we cannot calc offsets
- if (!$(this.span).is(":visible")) {
+ if (!this.isVisible()) {
// We cannot calc offsets for hidden elements
this.warn("scrollIntoView(): node is invisible.");
return _getResolvedPromise();
@@ -3482,11 +3488,9 @@ var uniqueId = $.fn.extend( {
"scrollParent should be a simple element or `window`, not document or body."
);
- (containerOffsetTop = $scrollParent.offset().top),
- (nodeY =
- $(this.span).offset().top -
- containerOffsetTop +
- scrollTop); // relative to scroll parent
+ containerOffsetTop = $scrollParent.offset().top;
+ nodeY =
+ $(this.span).offset().top - containerOffsetTop + scrollTop; // relative to scroll parent
topNodeY = topNode
? $(topNode.span).offset().top -
containerOffsetTop +
@@ -3514,7 +3518,7 @@ var uniqueId = $.fn.extend( {
// outside the upper border
if (topNode) {
_assert(
- topNode.isRootNode() || $(topNode.span).is(":visible"),
+ topNode.isRootNode() || topNode.isVisible(),
"topNode must be visible"
);
if (topNodeY < newScrollTop) {
@@ -3524,11 +3528,13 @@ var uniqueId = $.fn.extend( {
}
}
- if (newScrollTop !== null) {
+ if (newScrollTop === null) {
+ dfd.resolveWith(this);
+ } else {
// this.debug(" scrollIntoView(), SET newScrollTop=" + newScrollTop);
if (opts.effects) {
opts.effects.complete = function() {
- dfd.resolveWith(that);
+ dfd.resolveWith(self);
};
$animateTarget.stop(true).animate(
{
@@ -3540,8 +3546,6 @@ var uniqueId = $.fn.extend( {
$animateTarget[0].scrollTop = newScrollTop;
dfd.resolveWith(this);
}
- } else {
- dfd.resolveWith(this);
}
return dfd.promise();
},
@@ -3619,6 +3623,8 @@ var uniqueId = $.fn.extend( {
function(a, b) {
var x = a.title.toLowerCase(),
y = b.title.toLowerCase();
+
+ // eslint-disable-next-line no-nested-ternary
return x === y ? 0 : x > y ? 1 : -1;
};
cl.sort(cmp);
@@ -3944,6 +3950,7 @@ var uniqueId = $.fn.extend( {
* @property {string} statusClassPropName Property name of FancytreeNode that contains the element which will receive the status classes.
* Typically "span", but "tr" for table extension.
* @property {object} types Map for shared type specific meta data, used with node.type attribute. @since 2.27
+ * @property {object} viewport See ext-vieport. @since v2.31
* @property {object} widget Base widget instance.
*/
function Fancytree(widget) {
@@ -3951,24 +3958,18 @@ var uniqueId = $.fn.extend( {
this.$div = widget.element;
this.options = widget.options;
if (this.options) {
- if (
- $.isFunction(this.options.lazyload) &&
- !$.isFunction(this.options.lazyLoad)
- ) {
- this.options.lazyLoad = function() {
- FT.warn(
- "The 'lazyload' event is deprecated since 2014-02-25. Use 'lazyLoad' (with uppercase L) instead."
- );
- return widget.options.lazyload.apply(this, arguments);
- };
+ if (this.options.lazyload !== undefined) {
+ $.error(
+ "The 'lazyload' event is deprecated since 2014-02-25. Use 'lazyLoad' (with uppercase L) instead."
+ );
}
- if ($.isFunction(this.options.loaderror)) {
+ if (this.options.loaderror !== undefined) {
$.error(
"The 'loaderror' event was renamed since 2014-07-03. Use 'loadError' (with uppercase E) instead."
);
}
if (this.options.fx !== undefined) {
- FT.warn(
+ $.error(
"The 'fx' option was replaced by 'toggleEffect' since 2014-11-30."
);
}
@@ -3984,7 +3985,7 @@ var uniqueId = $.fn.extend( {
// allow to init tree.data.foo from
- // node.debug("render..."); - if (!node.li) { - // node.debug("render... really"); + if (node.li) { + // this.nodeRenderTitle(ctx); + this.nodeRenderStatus(ctx); + } else { + // node.debug("render... really"); firstTime = true; node.li = document.createElement("li"); node.li.ftnode = node; @@ -5868,9 +5950,6 @@ var uniqueId = $.fn.extend( { ctx ); } - } else { - // this.nodeRenderTitle(ctx); - this.nodeRenderStatus(ctx); } // Allow tweaking after node state was rendered if (opts.renderNode) { @@ -5927,14 +6006,14 @@ var uniqueId = $.fn.extend( { for (i = 0, l = children.length - 1; i < l; i++) { childNode1 = children[i]; childNode2 = childLI.ftnode; - if (childNode1 !== childNode2) { + if (childNode1 === childNode2) { + childLI = childLI.nextSibling; + } else { // node.debug("_fixOrder: mismatch at index " + i + ": " + childNode1 + " != " + childNode2); node.ul.insertBefore( childNode1.li, childNode2.li ); - } else { - childLI = childLI.nextSibling; } } } @@ -6219,10 +6298,10 @@ var uniqueId = $.fn.extend( { cnList.push(cn.expanded); } if (aria) { - if (hasChildren !== false) { - $ariaElem.attr("aria-expanded", Boolean(node.expanded)); - } else { + if (hasChildren === false) { $ariaElem.removeAttr("aria-expanded"); + } else { + $ariaElem.attr("aria-expanded", Boolean(node.expanded)); } } if (node.folder) { @@ -6492,6 +6571,11 @@ var uniqueId = $.fn.extend( { effect = opts.toggleEffect; node.expanded = flag; + tree._callHook( + "treeStructureChanged", + ctx, + flag ? "expand" : "collapse" + ); // Create required markup, but make sure the top UL is hidden, so we // can animate later tree._callHook("nodeRender", ctx, false, false, true); @@ -6516,7 +6600,20 @@ var uniqueId = $.fn.extend( { // See #716, #717 $(node.li).addClass(cn.animating); // #717 - if (!$.isFunction($(node.ul)[effect.effect])) { + if ($.isFunction($(node.ul)[effect.effect])) { + tree.debug( + "use jquery." + effect.effect + " method" + ); + $(node.ul)[effect.effect]({ + duration: effect.duration, + always: function() { + // node.debug("fancytree-animating end: " + node.li.className); + $(this).removeClass(cn.animating); // #716 + $(node.li).removeClass(cn.animating); // #717 + callback(); + }, + }); + } else { // The UI toggle() effect works with the ext-wide extension, // while jQuery.animate() has problems when the title span // has positon: absolute. @@ -6545,22 +6642,7 @@ var uniqueId = $.fn.extend( { callback(); } ); - } else { - tree.debug( - "use jquery." + effect.effect + " method" - ); - - $(node.ul)[effect.effect]({ - duration: effect.duration, - always: function() { - // node.debug("fancytree-animating end: " + node.li.className); - $(this).removeClass(cn.animating); // #716 - $(node.li).removeClass(cn.animating); // #717 - callback(); - }, - }); } - return; } } @@ -6657,18 +6739,6 @@ var uniqueId = $.fn.extend( { .find(".fancytree-title") .focus(); } - } else { - // We cannot set KB focus to a node, so use the tree container - // #563, #570: IE scrolls on every call to .focus(), if the container - // is partially outside the viewport. So do it only, when absolutely - // neccessary: - if ( - $(document.activeElement).closest( - ".fancytree-container" - ).length === 0 - ) { - $(tree.$container).focus(); - } } if (opts.aria) { // Set active descendant to node's span ID (create one, if needed) @@ -6682,6 +6752,20 @@ var uniqueId = $.fn.extend( { } // $(node.span).find(".fancytree-title").focus(); this._triggerNodeEvent("focus", ctx); + + // determine if we have focus on or inside tree container + var hasFancytreeFocus = + document.activeElement === tree.$container.get(0) || + $(document.activeElement, tree.$container).length >= 1; + + if (!hasFancytreeFocus) { + // We cannot set KB focus to a node, so use the tree container + // #563, #570: IE scrolls on every call to .focus(), if the container + // is partially outside the viewport. So do it only, when absolutely + // necessary. + $(tree.$container).focus(); + } + // if( opts.autoActivate ){ // tree.nodeSetActive(ctx, true); // } @@ -6725,7 +6809,7 @@ var uniqueId = $.fn.extend( { node._lastSelectIntent = flag; // Confusing use of '!' // Nothing to do? - /*jshint -W018 */ if (!!node.selected === flag) { + if (!!node.selected === flag) { if (opts.selectMode === 3 && node.partsel && !flag) { // If propagation prevented selecting this node last time, we still // want to allow to apply setSelected(false) now @@ -6733,7 +6817,6 @@ var uniqueId = $.fn.extend( { return flag; } } - /*jshint +W018 */ if ( !noEvents && @@ -6802,6 +6885,11 @@ var uniqueId = $.fn.extend( { } else { node.children.shift(); } + tree._callHook( + "treeStructureChanged", + ctx, + "clearStatusNode" + ); } } function _setStatusNode(data, type) { @@ -6815,6 +6903,11 @@ var uniqueId = $.fn.extend( { tree._callHook("nodeRenderTitle", firstChild); } else { node._setChildren([data]); + tree._callHook( + "treeStructureChanged", + ctx, + "setStatusNode" + ); node.children[0].statusNodeType = type; tree.render(); } @@ -6918,6 +7011,7 @@ var uniqueId = $.fn.extend( { tree.$div.find(">ul.fancytree-container").empty(); // TODO: call destructors and remove reference loops tree.rootNode.children = null; + tree._callHook("treeStructureChanged", ctx, "clear"); }, /** Widget was created (called only once, even it re-initialized). * @param {EventData} ctx @@ -6928,8 +7022,9 @@ var uniqueId = $.fn.extend( { */ treeDestroy: function(ctx) { this.$div.find(">ul.fancytree-container").remove(); - this.$source && + if (this.$source) { this.$source.removeClass("fancytree-helper-hidden"); + } }, /** Widget was (re-)initialized. * @param {EventData} ctx @@ -6953,6 +7048,9 @@ var uniqueId = $.fn.extend( { } }); + if (opts.checkboxAutoHide) { + tree.$container.addClass("fancytree-checkbox-auto-hide"); + } if (opts.rtl) { tree.$container .attr("DIR", "RTL") @@ -6993,7 +7091,7 @@ var uniqueId = $.fn.extend( { type = $container.data("type") || "html"; switch (type) { case "html": - $ul = $container.find(">ul:first"); + $ul = $container.find(">ul").first(); $ul.addClass( "ui-fancytree-source fancytree-helper-hidden" ); @@ -7042,9 +7140,17 @@ var uniqueId = $.fn.extend( { $.error("Not implemented"); } + // TODO: might be useful? Let's wait for a use case... + // tree._triggerTreeEvent("beforeInitLoad", null); + // Trigger fancytreeinit after nodes have been loaded dfd = this.nodeLoadChildren(rootCtx, source) .done(function() { + tree._callHook( + "treeStructureChanged", + ctx, + "loadChildren" + ); tree.render(); if (ctx.options.selectMode === 3) { tree.rootNode.fixSelection3FromEndNodes(); @@ -7065,7 +7171,13 @@ var uniqueId = $.fn.extend( { * @param {boolean} add * @param {FancytreeNode} node */ - treeRegisterNode: function(ctx, add, node) {}, + treeRegisterNode: function(ctx, add, node) { + ctx.tree._callHook( + "treeStructureChanged", + ctx, + add ? "addNode" : "removeNode" + ); + }, /** Widget got focus. * @param {EventData} ctx * @param {boolean} [flag=true] @@ -7092,7 +7204,9 @@ var uniqueId = $.fn.extend( { // #712: Use last mousedowned node ('click' event fires after focusin) targetNode = this._lastMousedownNode || this.getFirstChild(); - targetNode && targetNode.setFocus(); + if (targetNode) { + targetNode.setFocus(); + } } } }, @@ -7123,6 +7237,12 @@ var uniqueId = $.fn.extend( { callCreate = true; callRender = true; break; + case "checkboxAutoHide": + tree.$container.toggleClass( + "fancytree-checkbox-auto-hide", + !!value + ); + break; case "escapeTitles": case "tooltip": callRender = true; @@ -7174,6 +7294,10 @@ var uniqueId = $.fn.extend( { tree.render(true, false); // force, not-deep } }, + /** A Node was added, removed, moved, or it's visibility changed. + * @param {EventData} ctx + */ + treeStructureChanged: function(ctx, type) {}, } ); @@ -7214,7 +7338,7 @@ var uniqueId = $.fn.extend( { autoScroll: false, checkbox: false, clickFolderMode: 4, - debugLevel: null, // 0..4 (null: use global setting $.ui.fancytree.debugInfo) + debugLevel: null, // 0..4 (null: use global setting $.ui.fancytree.debugLevel) disabled: false, // TODO: required anymore? enableAspx: true, escapeTitles: false, @@ -7246,6 +7370,7 @@ var uniqueId = $.fn.extend( { tabindex: "0", titlesTabbable: false, tooltip: false, + treeId: null, _classNames: { node: "fancytree-node", folder: "fancytree-folder", @@ -7279,7 +7404,7 @@ var uniqueId = $.fn.extend( { this.$source = this.source || this.element.data("type") === "json" ? this.element - : this.element.find(">ul:first"); + : this.element.find(">ul").first(); // Subclass Fancytree instance with all enabled extensions var extension, extName, @@ -7330,15 +7455,15 @@ var uniqueId = $.fn.extend( { // if (opts.icons !== undefined) { // 2015-11-16 - if (opts.icon !== true) { - $.error( - "'icons' tree option is deprecated since v2.14.0: use 'icon' only instead" - ); - } else { + if (opts.icon === true) { this.tree.warn( "'icons' tree option is deprecated since v2.14.0: use 'icon' instead" ); opts.icon = opts.icons; + } else { + $.error( + "'icons' tree option is deprecated since v2.14.0: use 'icon' only instead" + ); } } if (opts.iconClass !== undefined) { @@ -7409,7 +7534,7 @@ var uniqueId = $.fn.extend( { }, /* Add mouse and kyboard handlers to the container */ _bind: function() { - var that = this, + var self = this, opts = this.options, tree = this.tree, ns = tree._ns; @@ -7520,7 +7645,7 @@ var uniqueId = $.fn.extend( { }) .on("mousedown" + ns, function(event) { var et = FT.getEventTarget(event); - // that.tree.debug("event(" + event.type + "): node: ", et.node); + // self.tree.debug("event(" + event.type + "): node: ", et.node); // #712: Store the clicked node, so we can use it when we get a focusin event // ('click' event fires after focusin) // tree.debug("event(" + event.type + "): node: ", et.node); @@ -7539,15 +7664,15 @@ var uniqueId = $.fn.extend( { var ctx, et = FT.getEventTarget(event), node = et.node, - tree = that.tree, + tree = self.tree, prevPhase = tree.phase; - // that.tree.debug("event(" + event.type + "): node: ", node); + // self.tree.debug("event(" + event.type + "): node: ", node); if (!node) { return true; // Allow bubbling of other events } ctx = tree._makeHookContext(node, event); - // that.tree.debug("event(" + event.type + "): node: ", node); + // self.tree.debug("event(" + event.type + "): node: ", node); try { tree.phase = "userEvent"; switch (event.type) { @@ -7630,7 +7755,7 @@ var uniqueId = $.fn.extend( { /** @lends Fancytree_Static# */ { /** @type {string} */ - version: "2.30.2", // Set to semver by 'grunt release' + version: "2.31.0", // Set to semver by 'grunt release' /** @type {string} */ buildType: "production", // Set to 'production' by 'grunt build' /** @type {int} */ @@ -7691,9 +7816,11 @@ var uniqueId = $.fn.extend( { return function() { var args = arguments; ctx = ctx || this; + // eslint-disable-next-line no-unused-expressions invokeAsap && !timer && fn.apply(ctx, args); clearTimeout(timer); timer = setTimeout(function() { + // eslint-disable-next-line no-unused-expressions invokeAsap || fn.apply(ctx, args); timer = null; }, timeout); @@ -7703,16 +7830,17 @@ var uniqueId = $.fn.extend( { * @param {string} msg */ debug: function(msg) { - /*jshint expr:true */ - $.ui.fancytree.debugLevel >= 4 && + if ($.ui.fancytree.debugLevel >= 4) { consoleApply("log", arguments); + } }, /** Write error message to console if debugLevel >= 1. * @param {string} msg */ error: function(msg) { - $.ui.fancytree.debugLevel >= 1 && + if ($.ui.fancytree.debugLevel >= 1) { consoleApply("error", arguments); + } }, /** Convert <, >, &, ", ', / to the equivalent entities. * @@ -7850,14 +7978,17 @@ var uniqueId = $.fn.extend( { * @param {Element | jQueryObject | Event | integer | string} [el] * @returns {Fancytree} matching tree or null * @example - * $.ui.fancytree.getTree(); // Get first Fancytree instance on page + * $.ui.fancytree.getTree(); // Get first Fancytree instance on page * $.ui.fancytree.getTree(1); // Get second Fancytree instance on page - * $.ui.fancytree.getTree("#tree"); // Get tree for this matching element + * $.ui.fancytree.getTree(event); // Get tree for this mouse- or keyboard event + * $.ui.fancytree.getTree("foo"); // Get tree for this `opts.treeId` + * $.ui.fancytree.getTree("#tree"); // Get tree for this matching element * * @since 2.13 */ getTree: function(el) { - var widget; + var widget, + orgEl = el; if (el instanceof Fancytree) { return el; // el already was a Fancytree @@ -7868,7 +7999,11 @@ var uniqueId = $.fn.extend( { if (typeof el === "number") { el = $(".fancytree-container").eq(el); // el was an integer: return nth instance } else if (typeof el === "string") { - el = $(el).eq(0); // el was a selector: use first match + // `el` may be a treeId or a selector: + el = $("#ft-id-" + orgEl).eq(0); + if (!el.length) { + el = $(orgEl).eq(0); // el was a selector: use first match + } } else if (el instanceof $) { el = el.eq(0); // el was a jQuery object: use the first DOM element } else if (el.originalEvent !== undefined) { @@ -7929,7 +8064,7 @@ var uniqueId = $.fn.extend( { res = nodeOpt; } } else { - res = nodeOpt != null ? nodeOpt : treeOpt; + res = nodeOpt == null ? treeOpt : nodeOpt; } if (res == null) { res = defaultValue; // no option set at all: return default @@ -8010,13 +8145,13 @@ var uniqueId = $.fn.extend( { if (et === "click" || et === "dblclick") { s.push(MOUSE_BUTTONS[event.button] + et); - } else { - if (!IGNORE_KEYCODES[which]) { - s.push( - SPECIAL_KEYCODES[which] || - String.fromCharCode(which).toLowerCase() - ); - } + } else if (et === "wheel") { + s.push(et); + } else if (!IGNORE_KEYCODES[which]) { + s.push( + SPECIAL_KEYCODES[which] || + String.fromCharCode(which).toLowerCase() + ); } return s.join("+"); }, @@ -8024,9 +8159,9 @@ var uniqueId = $.fn.extend( { * @param {string} msg */ info: function(msg) { - /*jshint expr:true */ - $.ui.fancytree.debugLevel >= 3 && + if ($.ui.fancytree.debugLevel >= 3) { consoleApply("info", arguments); + } }, /* @deprecated: use eventToString(event) instead. */ @@ -8075,8 +8210,6 @@ var uniqueId = $.fn.extend( { * @returns {NodeData[]} */ parseHtml: function($ul) { - // TODO: understand this: - /*jshint validthis:true */ var classes, className, extraClasses, @@ -8092,8 +8225,8 @@ var uniqueId = $.fn.extend( { var allData, lowerCaseAttr, $li = $(this), - $liSpan = $li.find(">span:first", this), - $liA = $liSpan.length ? null : $li.find(">a:first"), + $liSpan = $li.find(">span", this).first(), + $liA = $liSpan.length ? null : $li.find(">a").first(), d = { tooltip: null, data: {} }; if ($liSpan.length) { @@ -8153,8 +8286,7 @@ var uniqueId = $.fn.extend( { if (allData.hasOwnProperty(lowerCaseAttr)) { allData[ NODE_ATTR_LOWERCASE_MAP[lowerCaseAttr] - ] = - allData[lowerCaseAttr]; + ] = allData[lowerCaseAttr]; delete allData[lowerCaseAttr]; } } @@ -8171,7 +8303,7 @@ var uniqueId = $.fn.extend( { $.extend(d.data, allData); } // Recursive reading of child nodes, if LI tag contains an UL tag - $ul = $li.find(">ul:first"); + $ul = $li.find(">ul").first(); if ($ul.length) { d.children = $.ui.fancytree.parseHtml($ul); } else { @@ -8213,8 +8345,9 @@ var uniqueId = $.fn.extend( { * @param {string} msg */ warn: function(msg) { - $.ui.fancytree.debugLevel >= 2 && + if ($.ui.fancytree.debugLevel >= 2) { consoleApply("warn", arguments); + } }, } ); @@ -8227,7 +8360,7 @@ var uniqueId = $.fn.extend( { /*! Extension 'jquery.fancytree.childcounter.js' */// Extending Fancytree // =================== // -// See also the [live demo](http://wwwendt.de/tech/fancytree/demo/sample-ext-childcounter.html) of this code. +// See also the [live demo](https://wwWendt.de/tech/fancytree/demo/sample-ext-childcounter.html) of this code. // // Every extension should have a comment header containing some information // about the author, copyright and licensing. Also a pointer to the latest @@ -8240,13 +8373,13 @@ var uniqueId = $.fn.extend( { * Add a child counter bubble to tree nodes. * (Extension module for jquery.fancytree.js: 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 */ // To keep the global namespace clean, we wrap everything in a closure. @@ -8271,10 +8404,10 @@ var uniqueId = $.fn.extend( { "use strict"; // The [coding guidelines](http://contribute.jquery.org/style-guide/js/) - // require jshint compliance. + // require jshint /eslint compliance. // But for this sample, we want to allow unused variables for demonstration purpose. - /*jshint unused:false */ + /*eslint-disable no-unused-vars */ // Adding methods // -------------- @@ -8345,7 +8478,7 @@ var uniqueId = $.fn.extend( { // Register a Fancytree extension // ------------------------------ // A full blown extension, extension is available for all trees and can be - // enabled like so (see also the [live demo](http://wwwendt.de/tech/fancytree/demo/sample-ext-childcounter.html)): + // enabled like so (see also the [live demo](https://wwWendt.de/tech/fancytree/demo/sample-ext-childcounter.html)): // // // @@ -8365,7 +8498,7 @@ var uniqueId = $.fn.extend( { // Every extension must be registered by a unique name. name: "childcounter", // Version information should be compliant with [semver](http://semver.org) - version: "2.30.2", + version: "2.31.0", // Extension specific options and their defaults. // This options will be available as `tree.options.childcounter.hideExpanded` @@ -8392,13 +8525,13 @@ var uniqueId = $.fn.extend( { // **Override virtual methods for this extension.** // // Fancytree implements a number of 'hook methods', prefixed by 'node...' or 'tree...'. - // with a `ctx` argument (see [EventData](http://www.wwwendt.de/tech/fancytree/doc/jsdoc/global.html#EventData) + // with a `ctx` argument (see [EventData](https://wwWendt.de/tech/fancytree/doc/jsdoc/global.html#EventData) // for details) and an extended calling context:
this.data = _getElementDataAsDict(this.$div);
// TODO: use widget.uuid instead?
- this._id = $.ui.fancytree._nextId++;
+ this._id = "" + (this.options.treeId || $.ui.fancytree._nextId++);
// TODO: use widget.eventNamespace instead?
this._ns = ".fancytree-" + this._id; // append for namespaced events
this.activeNode = null;
@@ -3997,6 +3998,7 @@ var uniqueId = $.fn.extend( {
this.systemFocusElement = null;
this.lastQuicksearchTerm = "";
this.lastQuicksearchTime = 0;
+ this.viewport = null; // ext-grid
this.statusClassPropName = "span";
this.ariaPropName = "li";
@@ -4018,6 +4020,7 @@ var uniqueId = $.fn.extend( {
// Create root markup
$ul = $("
"),o=r.children()[0];return e("body").append(r),t=o.offsetWidth,r.css("overflow","scroll"),n=o.offsetWidth,t===n&&(n=r[0].clientWidth),r.remove(),i=t-n},getScrollInfo:function(t){var n=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),i=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),r="scroll"===n||"auto"===n&&t.width- ", {
+ id: "ft-id-" + this._id,
class: "ui-fancytree fancytree-container fancytree-plain",
}).appendTo(this.$div);
this.$container = $ul;
@@ -4139,7 +4142,10 @@ var uniqueId = $.fn.extend( {
isMissing = required && this.ext[name] == null,
badOrder = !isMissing && before != null && before !== isBefore;
- _assert(thisName && thisName !== name, "invalid or same name");
+ _assert(
+ thisName && thisName !== name,
+ "invalid or same name '" + thisName + "' (require yourself?)"
+ );
if (isMissing || badOrder) {
if (!message) {
@@ -4283,14 +4289,14 @@ var uniqueId = $.fn.extend( {
* @since 2.19
*/
enableUpdate: function(flag) {
- flag = flag !== false; // Confusing use of '!'
- /*jshint -W018 */ if (!!this._enableUpdate === !!flag) {
+ flag = flag !== false;
+ if (!!this._enableUpdate === !!flag) {
return flag;
}
- /*jshint +W018 */
this._enableUpdate = flag;
if (flag) {
this.debug("enableUpdate(true): redraw "); //, this._dirtyRoots);
+ this._callHook("treeStructureChanged", this, "enableUpdate");
this.render();
} else {
// this._dirtyRoots = null;
@@ -4298,6 +4304,16 @@ var uniqueId = $.fn.extend( {
}
return !flag; // return previous value
},
+ /** Write error to browser console if debugLevel >= 1 (prepending tree info)
+ *
+ * @param {*} msg string or object or array of such
+ */
+ error: function(msg) {
+ if (this.options.debugLevel >= 1) {
+ Array.prototype.unshift.call(arguments, this.toString());
+ consoleApply("error", arguments);
+ }
+ },
/** Expand (or collapse) all parent nodes.
*
* This convenience method uses `tree.visit()` and `tree.setExpanded()`
@@ -4308,6 +4324,8 @@ var uniqueId = $.fn.extend( {
* @since 2.30
*/
expandAll: function(flag, opts) {
+ var prev = this.enableUpdate(false);
+
flag = flag !== false;
this.visit(function(node) {
if (
@@ -4317,6 +4335,7 @@ var uniqueId = $.fn.extend( {
node.setExpanded(flag, opts);
}
});
+ this.enableUpdate(prev);
},
/**Find all nodes that matches condition.
*
@@ -4347,78 +4366,119 @@ var uniqueId = $.fn.extend( {
* @param {FancytreeNode} [startNode] defaults to first node
* @returns {FancytreeNode} matching node or null
*/
- findNextNode: function(match, startNode, visibleOnly) {
+ findNextNode: function(match, startNode) {
+ //, visibleOnly) {
+ var res = null,
+ firstNode = this.getFirstChild();
+
match =
typeof match === "string"
? _makeNodeTitleStartMatcher(match)
: match;
- startNode = startNode || this.getFirstChild();
+ startNode = startNode || firstNode;
- var stopNode = null,
- parentChildren = startNode.parent.children,
- matchingNode = null,
- walkVisible = function(parent, idx, fn) {
- var i,
- grandParent,
- parentChildren = parent.children,
- siblingCount = parentChildren.length,
- node = parentChildren[idx];
- // visit node itself
- if (node && fn(node) === false) {
- return false;
- }
- // visit descendants
- if (node && node.children && node.expanded) {
- if (walkVisible(node, 0, fn) === false) {
- return false;
- }
- }
- // visit subsequent siblings
- for (i = idx + 1; i < siblingCount; i++) {
- if (walkVisible(parent, i, fn) === false) {
- return false;
- }
- }
- // visit parent's subsequent siblings
- grandParent = parent.parent;
- if (grandParent) {
- return walkVisible(
- grandParent,
- grandParent.children.indexOf(parent) + 1,
- fn
- );
- } else {
- // wrap-around: restart with first node
- return walkVisible(parent, 0, fn);
- }
- };
-
- walkVisible(
- startNode.parent,
- parentChildren.indexOf(startNode),
- function(node) {
- // Stop iteration if we see the start node a second time
- if (node === stopNode) {
- return false;
- }
- stopNode = stopNode || node;
- // Ignore nodes hidden by a filter
- if (!$(node.span).is(":visible")) {
- node.debug("quicksearch: skipping hidden node");
- return;
- }
- // Test if we found a match, but search for a second match if this
- // was the currently active node
- if (match(node)) {
- // node.debug("quicksearch match " + node.title, startNode);
- matchingNode = node;
- if (matchingNode !== startNode) {
- return false;
- }
- }
+ function _checkNode(n) {
+ // console.log("_check " + n)
+ if (match(n)) {
+ res = n;
}
- );
- return matchingNode;
+ if (res || n === startNode) {
+ return false;
+ }
+ }
+ this.visitRows(_checkNode, {
+ start: startNode,
+ includeSelf: false,
+ });
+ // Wrap around search
+ if (!res && startNode !== firstNode) {
+ this.visitRows(_checkNode, {
+ start: firstNode,
+ includeSelf: true,
+ });
+ }
+ return res;
+ },
+ /** Find a node relative to another node.
+ *
+ * @param {FancytreeNode} node
+ * @param {number|string} where The keyCode that would normally trigger this move,
+ * or a keyword ('down', 'first', 'last', 'left', 'parent', 'right', 'up').
+ * @param {boolean} [includeHidden=false] Not yet implemented
+ * @returns {FancytreeNode|null}
+ * @since v2.31
+ */
+ findRelatedNode: function(node, where, includeHidden) {
+ var res = null,
+ KC = $.ui.keyCode;
+
+ switch (where) {
+ case "parent":
+ case KC.BACKSPACE:
+ if (node.parent && node.parent.parent) {
+ res = node.parent;
+ }
+ break;
+ case "first":
+ case KC.HOME:
+ // First visible node
+ this.visit(function(n) {
+ if (n.isVisible()) {
+ res = n;
+ return false;
+ }
+ });
+ break;
+ case "last":
+ case KC.END:
+ this.visit(function(n) {
+ // last visible node
+ if (n.isVisible()) {
+ res = n;
+ }
+ });
+ break;
+ case "left":
+ case KC.LEFT:
+ if (node.expanded) {
+ node.setExpanded(false);
+ } else if (node.parent && node.parent.parent) {
+ res = node.parent;
+ }
+ break;
+ case "right":
+ case KC.RIGHT:
+ if (!node.expanded && (node.children || node.lazy)) {
+ node.setExpanded();
+ res = node;
+ } else if (node.children && node.children.length) {
+ res = node.children[0];
+ }
+ break;
+ case "up":
+ case KC.UP:
+ this.visitRows(
+ function(n) {
+ res = n;
+ return false;
+ },
+ { start: node, reverse: true, includeSelf: false }
+ );
+ break;
+ case "down":
+ case KC.DOWN:
+ this.visitRows(
+ function(n) {
+ res = n;
+ return false;
+ },
+ { start: node, includeSelf: false }
+ );
+ break;
+ default:
+ this.tree.warn("Unknown relation '" + where + "'.");
+ }
+ return res;
},
// TODO: fromDict
/**
@@ -4708,6 +4768,8 @@ var uniqueId = $.fn.extend( {
i,
key,
node,
+ nodeKey,
+ remain,
remainMap,
tmpParent,
segList,
@@ -4804,25 +4866,27 @@ var uniqueId = $.fn.extend( {
}
// remainMap contains parent nodes, each with a list of relative sub-paths.
// We start loading all of them now, and pass the the list to each loader.
- for (var nodeKey in remainMap) {
- var remain = remainMap[nodeKey];
- // console.log("for(): remain=", remain, "remainMap=", remainMap);
- // key = remain.segList.shift();
- // node = __findChild(remain.parent, key);
- // if (node == null) { // #576
- // // Issue #576, refactored for v2.27:
- // // The root cause was, that sometimes the wrong parent was used here
- // // to find the next segment.
- // // Falling back to getNodeByKey() was a hack that no longer works if a custom
- // // matcher is used, because we cannot assume that a single segment-key is unique
- // // throughout the tree.
- // self.error("loadKeyPath: error loading child by key '" + key + "' (parent: " + target.parent + ")", target);
- // // node = self.getNodeByKey(key);
- // continue;
- // }
- subDfd = new $.Deferred();
- deferredList.push(subDfd);
- __lazyload(subDfd, remain.parent, remain.pathSegList);
+ for (nodeKey in remainMap) {
+ if (remainMap.hasOwnProperty(nodeKey)) {
+ remain = remainMap[nodeKey];
+ // console.log("for(): remain=", remain, "remainMap=", remainMap);
+ // key = remain.segList.shift();
+ // node = __findChild(remain.parent, key);
+ // if (node == null) { // #576
+ // // Issue #576, refactored for v2.27:
+ // // The root cause was, that sometimes the wrong parent was used here
+ // // to find the next segment.
+ // // Falling back to getNodeByKey() was a hack that no longer works if a custom
+ // // matcher is used, because we cannot assume that a single segment-key is unique
+ // // throughout the tree.
+ // self.error("loadKeyPath: error loading child by key '" + key + "' (parent: " + target.parent + ")", target);
+ // // node = self.getNodeByKey(key);
+ // continue;
+ // }
+ subDfd = new $.Deferred();
+ deferredList.push(subDfd);
+ __lazyload(subDfd, remain.parent, remain.pathSegList);
+ }
}
// Return a promise that is resolved, when ALL paths were loaded
return $.when.apply($, deferredList).promise();
@@ -4948,14 +5012,19 @@ var uniqueId = $.fn.extend( {
* @param {object} [options]
* Defaults:
* {start: First top node, reverse: false, includeSelf: true, includeHidden: false}
- * @returns {boolean}
+ * @returns {boolean} false if iteration was cancelled
* @since 2.28
*/
visitRows: function(fn, opts) {
+ if (!this.rootNode.children) {
+ return false;
+ }
if (opts && opts.reverse) {
delete opts.reverse;
return this._visitRowsUp(fn, opts);
}
+ opts = opts || {};
+
var i,
nextIdx,
parent,
@@ -4964,6 +5033,7 @@ var uniqueId = $.fn.extend( {
siblingOfs = 0,
skipFirstNode = opts.includeSelf === false,
includeHidden = !!opts.includeHidden,
+ checkFilter = !includeHidden && this.enableFilter,
node = opts.start || this.rootNode.children[0];
parent = node.parent;
@@ -4974,6 +5044,9 @@ var uniqueId = $.fn.extend( {
for (i = nextIdx; i < siblings.length; i++) {
node = siblings[i];
+ if (checkFilter && !node.match && !node.subMatchCount) {
+ continue;
+ }
if (!skipFirstNode && fn(node) === false) {
return false;
}
@@ -4988,6 +5061,9 @@ var uniqueId = $.fn.extend( {
// scoped variable may lead to confusing semantics:
/*jshint -W083 */
res = node.visit(function(n) {
+ if (checkFilter && !n.match && !n.subMatchCount) {
+ return "skip";
+ }
if (fn(n) === false) {
return false;
}
@@ -5024,6 +5100,9 @@ var uniqueId = $.fn.extend( {
if (children[0] === node) {
// If this is already the first sibling, goto parent
node = parent;
+ if (!node.parent) {
+ break; // first node of the tree
+ }
children = parent.children;
} else {
// Otherwise, goto prev. sibling
@@ -5031,6 +5110,8 @@ var uniqueId = $.fn.extend( {
node = children[idx - 1];
// If the prev. sibling has children, follow down to last descendant
while (
+ // See: https://github.com/eslint/eslint/issues/11302
+ // eslint-disable-next-line no-unmodified-loop-condition
(includeHidden || node.expanded) &&
node.children &&
node.children.length
@@ -5041,7 +5122,7 @@ var uniqueId = $.fn.extend( {
}
}
// Skip invisible
- if (!includeHidden && !$(node.span).is(":visible")) {
+ if (!includeHidden && !node.isVisible()) {
continue;
}
if (fn(node) === false) {
@@ -5193,7 +5274,7 @@ var uniqueId = $.fn.extend( {
// TODO: return promise?
var matchNode,
stamp,
- res,
+ _res,
focusNode,
event = ctx.originalEvent,
node = ctx.node,
@@ -5284,7 +5365,7 @@ var uniqueId = $.fn.extend( {
case "right":
case "up":
case "down":
- res = node.navigate(event.which, activate);
+ _res = node.navigate(event.which, activate);
break;
default:
handled = false;
@@ -5315,6 +5396,7 @@ var uniqueId = $.fn.extend( {
var ajax,
delay,
dfd,
+ res,
tree = ctx.tree,
node = ctx.node,
requestId = Date.now();
@@ -5466,10 +5548,7 @@ var uniqueId = $.fn.extend( {
});
}
// #383: accept and convert ECMAScript 6 Promise
- if (
- $.isFunction(source.then) &&
- $.isFunction(source["catch"])
- ) {
+ if ($.isFunction(source.then) && $.isFunction(source.catch)) {
dfd = source;
source = new $.Deferred();
dfd.then(
@@ -5544,7 +5623,7 @@ var uniqueId = $.fn.extend( {
} else {
if (ctx.options.postProcess) {
// #792: Call postProcess for non-deferred source
- var res = tree._triggerNodeEvent(
+ res = tree._triggerNodeEvent(
"postProcess",
ctx,
ctx.originalEvent,
@@ -5698,7 +5777,7 @@ var uniqueId = $.fn.extend( {
* @param {EventData} ctx
*/
nodeRemoveChildren: function(ctx) {
- var subCtx,
+ var //subCtx,
tree = ctx.tree,
node = ctx.node,
children = node.children;
@@ -5718,7 +5797,7 @@ var uniqueId = $.fn.extend( {
this.nodeRemoveChildMarkup(ctx);
// Unlink children to support GC
// TODO: also delete this.children (not possible using visit())
- subCtx = $.extend({}, ctx);
+ // subCtx = $.extend({}, ctx);
node.triggerModifyChild("remove", null);
node.visit(function(n) {
n.parent = null;
@@ -5841,8 +5920,11 @@ var uniqueId = $.fn.extend( {
}
// Create
// `this` : the Fancytree instance
// `this._local`: the namespace that contains extension attributes and private methods (same as this.ext.EXTNAME)
// `this._super`: the virtual function that was overridden (member of previous extension or Fancytree) // - // See also the [complete list of available hook functions](http://www.wwwendt.de/tech/fancytree/doc/jsdoc/Fancytree_Hooks.html). + // See also the [complete list of available hook functions](https://wwWendt.de/tech/fancytree/doc/jsdoc/Fancytree_Hooks.html). /* Init */ // `treeInit` is triggered when a tree is initalized. We can set up classes or @@ -8471,13 +8604,13 @@ var uniqueId = $.fn.extend( { * Support faster lookup of nodes by key and shared ref-ids. * (Extension module for jquery.fancytree.js: 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 */ (function(factory) { @@ -8533,7 +8666,7 @@ var uniqueId = $.fn.extend( { * @return {number} 32-bit positive integer hash */ function hashMurmur3(key, asString, seed) { - /*jshint bitwise:false */ + /*eslint-disable no-bitwise */ var h1b, k1, remainder = key.length & 3, @@ -8573,11 +8706,12 @@ var uniqueId = $.fn.extend( { k1 = 0; switch (remainder) { - /*jshint -W086:true */ case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16; + // fall through case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8; + // fall through case 1: k1 ^= key.charCodeAt(i) & 0xff; @@ -8612,19 +8746,26 @@ var uniqueId = $.fn.extend( { return ("0000000" + (h1 >>> 0).toString(16)).substr(-8); } return h1 >>> 0; + /*eslint-enable no-bitwise */ } /* - * Return a unique key for node by calculating the hash of the parents refKey-list. - */ + * Return a unique key for node by calculating the hash of the parents refKey-list. + */ function calcUniqueKey(node) { var key, + h1, path = $.map(node.getParentList(false, true), function(e) { return e.refKey || e.key; }); + path = path.join("/"); - key = "id_" + hashMurmur3(path, true); - // node.debug(path + " -> " + key); + // 32-bit has a high probability of collisions, so we pump up to 64-bit + // https://security.stackexchange.com/q/209882/207588 + + h1 = hashMurmur3(path, true); + key = "id_" + h1 + hashMurmur3(h1 + path, true); + return key; } @@ -8827,7 +8968,7 @@ var uniqueId = $.fn.extend( { */ $.ui.fancytree.registerExtension({ name: "clones", - version: "2.30.2", + version: "2.31.0", // Default options for this extension. options: { highlightActiveClones: true, // set 'fancytree-active-clone' on active clones and all peers @@ -8871,12 +9012,21 @@ var uniqueId = $.fn.extend( { if (add) { if (keyMap[node.key] != null) { - $.error( - "clones.treeRegisterNode: node.key already exists: " + - node - ); + var other = keyMap[node.key], + msg = + "clones.treeRegisterNode: duplicate key '" + + node.key + + "': /" + + node.getPath(true) + + " => " + + other.getPath(true); + // Sometimes this exception is not visible in the console, + // so we also write it: + tree.error(msg); + $.error(msg); } keyMap[key] = node; + if (refKey) { refList = refMap[refKey]; if (refList) { @@ -8975,13 +9125,13 @@ var uniqueId = $.fn.extend( { * Drag-and-drop support (native HTML5). * (Extension module for jquery.fancytree.js: 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 */ /* @@ -9027,27 +9177,45 @@ var uniqueId = $.fn.extend( { classDropTarget = "fancytree-drop-target", nodeMimeType = "application/x-fancytree-node", $dropMarker = null, + $dragImage, + $extraHelper, SOURCE_NODE = null, SOURCE_NODE_LIST = null, $sourceList = null, DRAG_ENTER_RESPONSE = null, + // SESSION_DATA = null, // plain object passed to events as `data` + SUGGESTED_DROP_EFFECT = null, + REQUESTED_DROP_EFFECT = null, + REQUESTED_EFFECT_ALLOWED = null, LAST_HIT_MODE = null, DRAG_OVER_STAMP = null; // Time when a node entered the 'over' hitmode /* */ function _clearGlobals() { + DRAG_ENTER_RESPONSE = null; + DRAG_OVER_STAMP = null; + REQUESTED_DROP_EFFECT = null; + REQUESTED_EFFECT_ALLOWED = null; + SUGGESTED_DROP_EFFECT = null; SOURCE_NODE = null; SOURCE_NODE_LIST = null; if ($sourceList) { $sourceList.removeClass(classDragSource + " " + classDragRemove); } $sourceList = null; - DRAG_ENTER_RESPONSE = null; - DRAG_OVER_STAMP = null; + if ($dropMarker) { + $dropMarker.hide(); + } + // Take this badge off of me - I can't use it anymore: + if ($extraHelper) { + $extraHelper.remove(); + $extraHelper = null; + } } /* Convert number to string and prepend +/-; return empty string for 0.*/ function offsetString(n) { + // eslint-disable-next-line no-nested-ternary return n === 0 ? "" : n > 0 ? "+" + n : "" + n; } @@ -9090,6 +9258,25 @@ var uniqueId = $.fn.extend( { return res; } + /* Convert a dataTransfer.effectAllowed to a canonical form. + * Return false or plain object + * @param {string|boolean} r + * @return {object|false} + */ + // function normalizeEffectAllowed(r) { + // if (!r || r === "none") { + // return false; + // } + // var all = r === "all", + // res = { + // copy: all || /copy/i.test(r), + // link: all || /link/i.test(r), + // move: all || /move/i.test(r), + // }; + + // return res; + // } + /* Implement auto scrolling when drag cursor is in top/bottom area of scroll parent. */ function autoScroll(tree, event) { var spOfs, @@ -9141,7 +9328,193 @@ var uniqueId = $.fn.extend( { return scrolled; } - /* Handle dragover event (fired every x ms) and return hitMode. */ + /* Guess dropEffect from modifier keys. + * Using rules suggested here: + * https://ux.stackexchange.com/a/83769 + * @returns + * 'copy', 'link', 'move', or 'none' + */ + function evalEffectModifiers(tree, event, effectDefault) { + var res = effectDefault; + + if (isMac) { + if (event.metaKey && event.altKey) { + // Mac: [Control] + [Option] + res = "link"; + } else if (event.ctrlKey) { + // Chrome on Mac: [Control] + res = "link"; + } else if (event.metaKey) { + // Mac: [Command] + res = "move"; + } else if (event.altKey) { + // Mac: [Option] + res = "copy"; + } + } else { + if (event.ctrlKey) { + // Windows: [Ctrl] + res = "copy"; + } else if (event.shiftKey) { + // Windows: [Shift] + res = "move"; + } else if (event.altKey) { + // Windows: [Alt] + res = "link"; + } + } + if (res !== SUGGESTED_DROP_EFFECT) { + tree.info( + "evalEffectModifiers: " + + event.type + + " - evalEffectModifiers(): " + + SUGGESTED_DROP_EFFECT + + " -> " + + res + ); + } + SUGGESTED_DROP_EFFECT = res; + // tree.debug("evalEffectModifiers: " + res); + return res; + } + /* + * Check if the previous callback (dragEnter, dragOver, ...) has changed + * the `data` object and apply those settings. + * + * Safari: + * It seems that `dataTransfer.dropEffect` can only be set on dragStart, and will remain + * even if the cursor changes when [Alt] or [Ctrl] are pressed (?) + * Using rules suggested here: + * https://ux.stackexchange.com/a/83769 + * @returns + * 'copy', 'link', 'move', or 'none' + */ + function prepareDropEffectCallback(event, data) { + var tree = data.tree, + dataTransfer = data.dataTransfer; + + if (event.type === "dragstart") { + data.effectAllowed = tree.options.dnd5.effectAllowed; + data.dropEffect = tree.options.dnd5.dropEffectDefault; + } else { + data.effectAllowed = REQUESTED_EFFECT_ALLOWED; + data.dropEffect = REQUESTED_DROP_EFFECT; + } + data.dropEffectSuggested = evalEffectModifiers( + tree, + event, + tree.options.dnd5.dropEffectDefault + ); + data.isMove = data.dropEffect === "move"; + data.files = dataTransfer.files || []; + + // if (REQUESTED_EFFECT_ALLOWED !== dataTransfer.effectAllowed) { + // tree.warn( + // "prepareDropEffectCallback(" + + // event.type + + // "): dataTransfer.effectAllowed changed from " + + // REQUESTED_EFFECT_ALLOWED + + // " -> " + + // dataTransfer.effectAllowed + // ); + // } + // if (REQUESTED_DROP_EFFECT !== dataTransfer.dropEffect) { + // tree.warn( + // "prepareDropEffectCallback(" + + // event.type + + // "): dataTransfer.dropEffect changed from requested " + + // REQUESTED_DROP_EFFECT + + // " to " + + // dataTransfer.dropEffect + // ); + // } + } + + function applyDropEffectCallback(event, data, allowDrop) { + var tree = data.tree, + dataTransfer = data.dataTransfer; + + if ( + event.type !== "dragstart" && + REQUESTED_EFFECT_ALLOWED !== data.effectAllowed + ) { + tree.warn( + "effectAllowed should only be changed in dragstart event: " + + event.type + + ": data.effectAllowed changed from " + + REQUESTED_EFFECT_ALLOWED + + " -> " + + data.effectAllowed + ); + } + + if (allowDrop === false) { + tree.info("applyDropEffectCallback: allowDrop === false"); + data.effectAllowed = "none"; + data.dropEffect = "none"; + } + // if (REQUESTED_DROP_EFFECT !== data.dropEffect) { + // tree.debug( + // "applyDropEffectCallback(" + + // event.type + + // "): data.dropEffect changed from previous " + + // REQUESTED_DROP_EFFECT + + // " to " + + // data.dropEffect + // ); + // } + + data.isMove = data.dropEffect === "move"; + // data.isMove = data.dropEffectSuggested === "move"; + + REQUESTED_EFFECT_ALLOWED = data.effectAllowed; + REQUESTED_DROP_EFFECT = data.dropEffect; + + // if (REQUESTED_DROP_EFFECT !== dataTransfer.dropEffect) { + // data.tree.info( + // "applyDropEffectCallback(" + + // event.type + + // "): dataTransfer.dropEffect changed from " + + // REQUESTED_DROP_EFFECT + + // " -> " + + // dataTransfer.dropEffect + // ); + // } + dataTransfer.effectAllowed = REQUESTED_EFFECT_ALLOWED; + dataTransfer.dropEffect = REQUESTED_DROP_EFFECT; + + // tree.debug( + // "applyDropEffectCallback(" + + // event.type + + // "): set " + + // dataTransfer.dropEffect + + // "/" + + // dataTransfer.effectAllowed + // ); + // if (REQUESTED_DROP_EFFECT !== dataTransfer.dropEffect) { + // data.tree.warn( + // "applyDropEffectCallback(" + + // event.type + + // "): could not set dataTransfer.dropEffect to " + + // REQUESTED_DROP_EFFECT + + // ": got " + + // dataTransfer.dropEffect + // ); + // } + return REQUESTED_DROP_EFFECT; + } + + /* Handle dragover event (fired every x ms) on valid drop targets. + * + * - Auto-scroll when cursor is in border regions + * - Apply restrictioan like 'preventVoidMoves' + * - Calculate hit mode + * - Calculate drop effect + * - Trigger dragOver() callback to let user modify hit mode and drop effect + * - Adjust the drop marker accordingly + * + * @returns hitMode + */ function handleDragOver(event, data) { // Implement auto-scrolling if (data.options.dnd5.scroll) { @@ -9172,74 +9545,69 @@ var uniqueId = $.fn.extend( { return false; } else if (typeof DRAG_ENTER_RESPONSE === "string") { $.error("assert failed: dragenter returned string"); - // Use hitMode from onEnter if provided. - // hitMode = DRAG_ENTER_RESPONSE; - } else { - // Calculate hitMode from relative cursor position. - nodeOfs = $target.offset(); - relPosY = (event.pageY - nodeOfs.top) / $target.height(); + } + // Calculate hitMode from relative cursor position. + nodeOfs = $target.offset(); + relPosY = (event.pageY - nodeOfs.top) / $target.height(); - if (DRAG_ENTER_RESPONSE.after && relPosY > 0.75) { - hitMode = "after"; + if (DRAG_ENTER_RESPONSE.after && relPosY > 0.75) { + hitMode = "after"; + } else if ( + !DRAG_ENTER_RESPONSE.over && + DRAG_ENTER_RESPONSE.after && + relPosY > 0.5 + ) { + hitMode = "after"; + } else if (DRAG_ENTER_RESPONSE.before && relPosY <= 0.25) { + hitMode = "before"; + } else if ( + !DRAG_ENTER_RESPONSE.over && + DRAG_ENTER_RESPONSE.before && + relPosY <= 0.5 + ) { + hitMode = "before"; + } else if (DRAG_ENTER_RESPONSE.over) { + hitMode = "over"; + } + // Prevent no-ops like 'before source node' + // TODO: these are no-ops when moving nodes, but not in copy mode + if (dndOpts.preventVoidMoves && data.dropEffect === "move") { + if (targetNode === sourceNode) { + targetNode.debug("Drop over source node prevented."); + hitMode = null; } else if ( - !DRAG_ENTER_RESPONSE.over && - DRAG_ENTER_RESPONSE.after && - relPosY > 0.5 + hitMode === "before" && + sourceNode && + targetNode === sourceNode.getNextSibling() ) { - hitMode = "after"; - } else if (DRAG_ENTER_RESPONSE.before && relPosY <= 0.25) { - hitMode = "before"; + targetNode.debug("Drop after source node prevented."); + hitMode = null; } else if ( - !DRAG_ENTER_RESPONSE.over && - DRAG_ENTER_RESPONSE.before && - relPosY <= 0.5 + hitMode === "after" && + sourceNode && + targetNode === sourceNode.getPrevSibling() ) { - hitMode = "before"; - } else if (DRAG_ENTER_RESPONSE.over) { - hitMode = "over"; - } - // Prevent no-ops like 'before source node' - // TODO: these are no-ops when moving nodes, but not in copy mode - if (dndOpts.preventVoidMoves) { - if (targetNode === sourceNode) { - targetNode.debug("Drop over source node prevented."); - hitMode = null; - } else if ( - hitMode === "before" && - sourceNode && - targetNode === sourceNode.getNextSibling() - ) { - targetNode.debug("Drop after source node prevented."); - hitMode = null; - } else if ( - hitMode === "after" && - sourceNode && - targetNode === sourceNode.getPrevSibling() - ) { - targetNode.debug("Drop before source node prevented."); - hitMode = null; - } else if ( - hitMode === "over" && - sourceNode && - sourceNode.parent === targetNode && - sourceNode.isLastSibling() - ) { - targetNode.debug( - "Drop last child over own parent prevented." - ); - hitMode = null; - } + targetNode.debug("Drop before source node prevented."); + hitMode = null; + } else if ( + hitMode === "over" && + sourceNode && + sourceNode.parent === targetNode && + sourceNode.isLastSibling() + ) { + targetNode.debug("Drop last child over own parent prevented."); + hitMode = null; } } // Let callback modify the calculated hitMode data.hitMode = hitMode; if (hitMode && dndOpts.dragOver) { - // TODO: http://code.google.com/p/dynatree/source/detail?r=625 + prepareDropEffectCallback(event, data); dndOpts.dragOver(targetNode, data); + var allowDrop = !!hitMode; + applyDropEffectCallback(event, data, allowDrop); hitMode = data.hitMode; } - // LAST_DROP_EFFECT = data.dataTransfer.dropEffect; - // LAST_EFFECT_ALLOWED = data.dataTransfer.effectAllowed; LAST_HIT_MODE = hitMode; // if (hitMode === "after" || hitMode === "before" || hitMode === "over") { @@ -9291,51 +9659,471 @@ var uniqueId = $.fn.extend( { return hitMode; } - /* Guess dropEffect from modifier keys. - * Safari: - * It seems that `dataTransfer.dropEffect` can only be set on dragStart, and will remain - * even if the cursor changes when [Alt] or [Ctrl] are pressed (?) - * Using rules suggested here: - * https://ux.stackexchange.com/a/83769 - * @returns - * 'copy', 'link', 'move', or 'none' + /* + * Handle dragstart drag dragend events on the container */ - function getDropEffect(event, data) { - var dndOpts = data.options.dnd5, - res = dndOpts.dropEffectDefault; - // dataTransfer = event.dataTransfer || event.originalEvent.dataTransfer, + function onDragEvent(event) { + var json, + tree = this, + dndOpts = tree.options.dnd5, + node = FT.getNode(event), + dataTransfer = + event.dataTransfer || event.originalEvent.dataTransfer, + data = { + tree: tree, + node: node, + options: tree.options, + originalEvent: event.originalEvent, + widget: tree.widget, + dataTransfer: dataTransfer, + useDefaultImage: true, + dropEffect: undefined, + dropEffectSuggested: undefined, + effectAllowed: undefined, // set by dragstart + files: undefined, // only for drop events + isCancelled: undefined, // set by dragend + isMove: undefined, + }; - // Use callback if any: - if (dndOpts.dropEffect) { - return dndOpts.dropEffect(event, data); - } + switch (event.type) { + case "dragstart": + if (!node) { + tree.info("Ignored dragstart on a non-node."); + return false; + } + // Store current source node in different formats + SOURCE_NODE = node; - if (isMac) { - if (event.metaKey && event.altKey) { - // Mac: [Control] + [Option] - return "link"; - } else if (event.metaKey) { - // Mac: [Command] - return "move"; - } else if (event.altKey) { - // Mac: [Option] - return "copy"; - } - } else { - if (event.ctrlKey) { - // Windows: [Ctrl] - return "copy"; - } else if (event.shiftKey) { - // Windows: [Shift] - return "move"; - } else if (event.altKey) { - // Windows: [Alt] - return "link"; - } + // Also optionally store selected nodes + if (dndOpts.multiSource === false) { + SOURCE_NODE_LIST = [node]; + } else if (dndOpts.multiSource === true) { + SOURCE_NODE_LIST = tree.getSelectedNodes(); + if (!node.isSelected()) { + SOURCE_NODE_LIST.unshift(node); + } + } else { + SOURCE_NODE_LIST = dndOpts.multiSource(node, data); + } + // Cache as array of jQuery objects for faster access: + $sourceList = $( + $.map(SOURCE_NODE_LIST, function(n) { + return n.span; + }) + ); + // Set visual feedback + $sourceList.addClass(classDragSource); + + // Set payload + // Note: + // Transfer data is only accessible on dragstart and drop! + // For all other events the formats and kinds in the drag + // data store list of items representing dragged data can be + // enumerated, but the data itself is unavailable and no new + // data can be added. + var nodeData = node.toDict(); + nodeData.treeId = node.tree._id; + json = JSON.stringify(nodeData); + try { + dataTransfer.setData(nodeMimeType, json); + dataTransfer.setData("text/html", $(node.span).html()); + dataTransfer.setData("text/plain", node.title); + } catch (ex) { + // IE only accepts 'text' type + tree.warn( + "Could not set data (IE only accepts 'text') - " + ex + ); + } + // We always need to set the 'text' type if we want to drag + // Because IE 11 only accepts this single type. + // If we pass JSON here, IE can can access all node properties, + // even when the source lives in another window. (D'n'd inside + // the same window will always work.) + // The drawback is, that in this case ALL browsers will see + // the JSON representation as 'text', so dragging + // to a text field will insert the JSON string instead of + // the node title. + if (dndOpts.setTextTypeJson) { + dataTransfer.setData("text", json); + } else { + dataTransfer.setData("text", node.title); + } + + // Set the allowed drag modes (combinations of move, copy, and link) + // (effectAllowed can only be set in the dragstart event.) + // This can be overridden in the dragStart() callback + prepareDropEffectCallback(event, data); + + // Let user cancel or modify above settings + // Realize potential changes by previous callback + if (dndOpts.dragStart(node, data) === false) { + // Cancel dragging + // dataTransfer.dropEffect = "none"; + _clearGlobals(); + return false; + } + applyDropEffectCallback(event, data); + + // Unless user set `data.useDefaultImage` to false in dragStart, + // generata a default drag image now: + $extraHelper = null; + + if (data.useDefaultImage) { + // Set the title as drag image (otherwise it would contain the expander) + $dragImage = $(node.span).find(".fancytree-title"); + + if (SOURCE_NODE_LIST && SOURCE_NODE_LIST.length > 1) { + // Add a counter badge to node title if dragging more than one node. + // We want this, because the element that is used as drag image + // must be *visible* in the DOM, so we cannot create some hidden + // custom markup. + // See https://kryogenix.org/code/browser/custom-drag-image.html + // Also, since IE 11 and Edge don't support setDragImage() alltogether, + // it gives som feedback to the user. + // The badge will be removed later on drag end. + $extraHelper = $( + "" + ) + .text("+" + (SOURCE_NODE_LIST.length - 1)) + .appendTo($dragImage); + } + if (dataTransfer.setDragImage) { + // IE 11 and Edge do not support this + dataTransfer.setDragImage($dragImage[0], -10, -10); + } + } + return true; + + case "drag": + // Called every few milliseconds (no matter if the + // cursor is over a valid drop target) + // data.tree.info("drag", SOURCE_NODE) + prepareDropEffectCallback(event, data); + dndOpts.dragDrag(node, data); + applyDropEffectCallback(event, data); + + $sourceList.toggleClass(classDragRemove, data.isMove); + break; + + case "dragend": + // Called at the end of a d'n'd process (after drop) + // Note caveat: If drop removed the dragged source element, + // we may not get this event, since the target does not exist + // anymore + prepareDropEffectCallback(event, data); + + _clearGlobals(); + + data.isCancelled = !LAST_HIT_MODE; + dndOpts.dragEnd(node, data, !LAST_HIT_MODE); + // applyDropEffectCallback(event, data); + break; } - // data.tree.debug("getDropEffect: " + res); - return res; } + /* + * Handle dragenter dragover dragleave drop events on the container + */ + function onDropEvent(event) { + var json, + nodeData, + isSourceFtNode, + r, + res, + tree = this, + dndOpts = tree.options.dnd5, + allowDrop = null, + node = FT.getNode(event), + dataTransfer = + event.dataTransfer || event.originalEvent.dataTransfer, + data = { + tree: tree, + node: node, + options: tree.options, + originalEvent: event.originalEvent, + widget: tree.widget, + hitMode: DRAG_ENTER_RESPONSE, + dataTransfer: dataTransfer, + otherNode: SOURCE_NODE || null, + otherNodeList: SOURCE_NODE_LIST || null, + otherNodeData: null, // set by drop event + useDefaultImage: true, + dropEffect: undefined, + dropEffectSuggested: undefined, + effectAllowed: undefined, // set by dragstart + files: null, // list of File objects (may be []) + isCancelled: undefined, // set by drop event + isMove: undefined, + }; + + // data.isMove = dropEffect === "move"; + + switch (event.type) { + case "dragenter": + // The dragenter event is fired when a dragged element or + // text selection enters a valid drop target. + + DRAG_OVER_STAMP = null; + if (!node) { + // Sometimes we get dragenter for the container element + tree.debug( + "Ignore non-node " + + event.type + + ": " + + event.target.tagName + + "." + + event.target.className + ); + DRAG_ENTER_RESPONSE = false; + break; + } + + $(node.span) + .addClass(classDropOver) + .removeClass(classDropAccept + " " + classDropReject); + + // Data is only readable in the dragstart and drop event, + // but we can check for the type: + isSourceFtNode = + $.inArray(nodeMimeType, dataTransfer.types) >= 0; + + if (dndOpts.preventNonNodes && !isSourceFtNode) { + node.debug("Reject dropping a non-node."); + DRAG_ENTER_RESPONSE = false; + break; + } else if ( + dndOpts.preventForeignNodes && + (!SOURCE_NODE || SOURCE_NODE.tree !== node.tree) + ) { + node.debug("Reject dropping a foreign node."); + DRAG_ENTER_RESPONSE = false; + break; + } else if ( + dndOpts.preventSameParent && + data.otherNode && + data.otherNode.tree === node.tree && + node.parent === data.otherNode.parent + ) { + node.debug("Reject dropping as sibling (same parent)."); + DRAG_ENTER_RESPONSE = false; + break; + } else if ( + dndOpts.preventRecursion && + data.otherNode && + data.otherNode.tree === node.tree && + node.isDescendantOf(data.otherNode) + ) { + node.debug("Reject dropping below own ancestor."); + DRAG_ENTER_RESPONSE = false; + break; + } + $dropMarker.show(); + + // Call dragEnter() to figure out if (and where) dropping is allowed + prepareDropEffectCallback(event, data); + r = dndOpts.dragEnter(node, data); + + res = normalizeDragEnterResponse(r); + DRAG_ENTER_RESPONSE = res; + + allowDrop = res && (res.over || res.before || res.after); + + applyDropEffectCallback(event, data, allowDrop); + break; + + case "dragover": + if (!node) { + tree.debug( + "Ignore non-node " + + event.type + + ": " + + event.target.tagName + + "." + + event.target.className + ); + break; + } + // The dragover event is fired when an element or text + // selection is being dragged over a valid drop target + // (every few hundred milliseconds). + // tree.debug( + // event.type + + // ": dropEffect: " + + // dataTransfer.dropEffect + // ); + LAST_HIT_MODE = handleDragOver(event, data); + + // The flag controls the preventDefault() below: + allowDrop = !!LAST_HIT_MODE; + + if ( + LAST_HIT_MODE === "over" && + !node.expanded && + node.hasChildren() !== false + ) { + if (!DRAG_OVER_STAMP) { + DRAG_OVER_STAMP = Date.now(); + } else if ( + dndOpts.autoExpandMS && + Date.now() - DRAG_OVER_STAMP > dndOpts.autoExpandMS && + (!dndOpts.dragExpand || + dndOpts.dragExpand(node, data) !== false) + ) { + node.setExpanded(); + } + } else { + DRAG_OVER_STAMP = null; + } + break; + + case "dragleave": + // NOTE: dragleave is fired AFTER the dragenter event of the + // FOLLOWING element. + if (!node) { + tree.debug( + "Ignore non-node " + + event.type + + ": " + + event.target.tagName + + "." + + event.target.className + ); + break; + } + if (!$(node.span).hasClass(classDropOver)) { + node.debug("Ignore dragleave (multi)."); + break; + } + $(node.span).removeClass( + classDropOver + + " " + + classDropAccept + + " " + + classDropReject + ); + node.scheduleAction("cancel"); + dndOpts.dragLeave(node, data); + $dropMarker.hide(); + break; + + case "drop": + // Data is only readable in the (dragstart and) drop event: + + if ($.inArray(nodeMimeType, dataTransfer.types) >= 0) { + nodeData = dataTransfer.getData(nodeMimeType); + tree.info( + event.type + + ": getData('application/x-fancytree-node'): '" + + nodeData + + "'" + ); + } + if (!nodeData) { + // 1. Source is not a Fancytree node, or + // 2. If the FT mime type was set, but returns '', this + // is probably IE 11 (which only supports 'text') + nodeData = dataTransfer.getData("text"); + tree.info( + event.type + ": getData('text'): '" + nodeData + "'" + ); + } + if (nodeData) { + try { + // 'text' type may contain JSON if IE is involved + // and setTextTypeJson option was set + json = JSON.parse(nodeData); + if (json.title !== undefined) { + data.otherNodeData = json; + } + } catch (ex) { + // assume 'text' type contains plain text, so `otherNodeData` + // should not be set + } + } + tree.debug( + event.type + + ": nodeData: '" + + nodeData + + "', otherNodeData: ", + data.otherNodeData + ); + + $(node.span).removeClass( + classDropOver + + " " + + classDropAccept + + " " + + classDropReject + ); + + // Let user implement the actual drop operation + data.hitMode = LAST_HIT_MODE; + prepareDropEffectCallback(event, data, !LAST_HIT_MODE); + data.isCancelled = !LAST_HIT_MODE; + + var orgSourceElem = SOURCE_NODE && SOURCE_NODE.span, + orgSourceTree = SOURCE_NODE && SOURCE_NODE.tree; + + dndOpts.dragDrop(node, data); + // applyDropEffectCallback(event, data); + + // Prevent browser's default drop handling, i.e. open as link, ... + event.preventDefault(); + + if (orgSourceElem && !document.body.contains(orgSourceElem)) { + // The drop handler removed the original drag source from + // the DOM, so the dragend event will probaly not fire. + if (orgSourceTree === tree) { + tree.debug( + "Drop handler removed source element: generating dragEnd." + ); + dndOpts.dragEnd(SOURCE_NODE, data); + } else { + tree.warn( + "Drop handler removed source element: dragend event may be lost." + ); + } + } + + _clearGlobals(); + + break; + } + // Dnd API madness: we must PREVENT default handling to enable dropping + if (allowDrop) { + event.preventDefault(); + return false; + } + } + + /** [ext-dnd5] Return a Fancytree instance, from element, index, event, or jQueryObject. + * + * @returns {FancytreeNode[]} List of nodes (empty if no drag operation) + * @example + * $.ui.fancytree.getDragNodeList(); + * + * @alias Fancytree_Static#getDragNodeList + * @requires jquery.fancytree.dnd5.js + * @since 2.31 + */ + $.ui.fancytree.getDragNodeList = function() { + return SOURCE_NODE_LIST || []; + }; + + /** [ext-dnd5] Return the FancytreeNode that is currently being dragged. + * + * If multiple nodes are dragged, only the first is returned. + * + * @returns {FancytreeNode | null} dragged nodes or null if no drag operation + * @example + * $.ui.fancytree.getDragNode(); + * + * @alias Fancytree_Static#getDragNode + * @requires jquery.fancytree.dnd5.js + * @since 2.31 + */ + $.ui.fancytree.getDragNode = function() { + return SOURCE_NODE; + }; /****************************************************************************** * @@ -9343,19 +10131,20 @@ var uniqueId = $.fn.extend( { $.ui.fancytree.registerExtension({ name: "dnd5", - version: "2.30.2", + version: "2.31.0", // Default options for this extension. options: { autoExpandMS: 1500, // Expand nodes after n milliseconds of hovering dropMarkerInsertOffsetX: -16, // Additional offset for drop-marker with hitMode = "before"/"after" dropMarkerOffsetX: -24, // Absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop) - multiSource: false, // true: Drag multiple (i.e. selected) nodes. - dragImage: null, // Callback(node, data) that can be used to call dataTransfer.setDragImage(). - dropEffect: null, // Callback(node, data) that returns 'copy', 'link', 'move', or 'none'. - dropEffectDefault: "move", // Default dropEffect ('copy', 'link', or 'move'). + multiSource: false, // true: Drag multiple (i.e. selected) nodes. Also a callback() is allowed + effectAllowed: "all", // Restrict the possible cursor shapes and modifier operations (can also be set in the dragStart event) + // dropEffect: "auto", // 'copy'|'link'|'move'|'auto'(calculate from `effectAllowed`+modifier keys) or callback(node, data) that returns such string. + dropEffectDefault: "move", // Default dropEffect ('copy', 'link', or 'move') when no modifier is pressed (overide in dragDrag, dragOver). preventForeignNodes: false, // Prevent dropping nodes from different Fancytrees preventNonNodes: false, // Prevent dropping items other than Fancytree nodes - preventRecursiveMoves: true, // Prevent dropping nodes on own descendants + preventRecursion: true, // Prevent dropping nodes on own descendants + preventSameParent: false, // Prevent dropping nodes under same direct parent preventVoidMoves: true, // Prevent dropping nodes 'before self', etc. scroll: true, // Enable auto-scrolling while dragging scrollSensitivity: 20, // Active top/bottom margin in pixel @@ -9374,14 +10163,11 @@ var uniqueId = $.fn.extend( { }, treeInit: function(ctx) { - var $dragImage, - $extraHelper, - $temp, + var $temp, tree = ctx.tree, opts = ctx.options, glyph = opts.glyph || null, - dndOpts = opts.dnd5, - getNode = FT.getNode; + dndOpts = opts.dnd5; if ($.inArray("dnd", opts.extensions) >= 0) { $.error("Extensions 'dnd' and 'dnd5' are mutually exclusive."); @@ -9391,6 +10177,11 @@ var uniqueId = $.fn.extend( { "dragStop is not used by ext-dnd5. Use dragEnd instead." ); } + if (dndOpts.preventRecursiveMoves != null) { + $.error( + "preventRecursiveMoves was renamed to preventRecursion." + ); + } // Implement `opts.createNode` event to add the 'draggable' attribute // #680: this must happen before calling super.treeInit() @@ -9401,7 +10192,11 @@ var uniqueId = $.fn.extend( { ) { // Default processing if any this._super.apply(this, arguments); - data.node.span.draggable = true; + if (data.node.span) { + data.node.span.draggable = true; + } else { + data.node.warn("Cannot add `draggable`: no span tag"); + } }); } this._superApply(arguments); @@ -9431,449 +10226,24 @@ var uniqueId = $.fn.extend( { glyph.map._addClass, glyph.map.dropMarker ); - // $dropMarker.addClass(glyph.map._addClass + " " + glyph.map.dropMarker); } } $dropMarker.toggleClass("fancytree-rtl", !!opts.rtl); + // Enable drag support if dragStart() is specified: if (dndOpts.dragStart) { // Bind drag event handlers - tree.$container.on("dragstart drag dragend", function(event) { - var json, - node = getNode(event), - dataTransfer = - event.dataTransfer || - event.originalEvent.dataTransfer, - data = { - node: node, - tree: tree, - options: tree.options, - originalEvent: event, - dataTransfer: dataTransfer, - // dropEffect: undefined, // set by dragend - isCancelled: undefined, // set by dragend - }, - dropEffect = getDropEffect(event, data), - isMove = dropEffect === "move"; - - // console.log(event.type, "dropEffect: " + dropEffect); - switch (event.type) { - case "dragstart": - if (!node) { - tree.info("Ignored dragstart on a non-node."); - return false; - } - // Store current source node in different formats - SOURCE_NODE = node; - - // Also optionally store selected nodes - if (dndOpts.multiSource === false) { - SOURCE_NODE_LIST = [node]; - } else if (dndOpts.multiSource === true) { - SOURCE_NODE_LIST = tree.getSelectedNodes(); - if (!node.isSelected()) { - SOURCE_NODE_LIST.unshift(node); - } - } else { - SOURCE_NODE_LIST = dndOpts.multiSource( - node, - data - ); - } - // Cache as array of jQuery objects for faster access: - $sourceList = $( - $.map(SOURCE_NODE_LIST, function(n) { - return n.span; - }) - ); - // Set visual feedback - $sourceList.addClass(classDragSource); - - // Set payload - // Note: - // Transfer data is only accessible on dragstart and drop! - // For all other events the formats and kinds in the drag - // data store list of items representing dragged data can be - // enumerated, but the data itself is unavailable and no new - // data can be added. - json = JSON.stringify(node.toDict()); - try { - dataTransfer.setData(nodeMimeType, json); - dataTransfer.setData( - "text/html", - $(node.span).html() - ); - dataTransfer.setData("text/plain", node.title); - } catch (ex) { - // IE only accepts 'text' type - tree.warn( - "Could not set data (IE only accepts 'text') - " + - ex - ); - } - // We always need to set the 'text' type if we want to drag - // Because IE 11 only accepts this single type. - // If we pass JSON here, IE can can access all node properties, - // even when the source lives in another window. (D'n'd inside - // the same window will always work.) - // The drawback is, that in this case ALL browsers will see - // the JSON representation as 'text', so dragging - // to a text field will insert the JSON string instead of - // the node title. - if (dndOpts.setTextTypeJson) { - dataTransfer.setData("text", json); - } else { - dataTransfer.setData("text", node.title); - } - - // Set the allowed and current drag mode (move, copy, or link) - dataTransfer.effectAllowed = "all"; // "copyMove" - // dropEffect = "move"; - - $extraHelper = null; - - if (dndOpts.dragImage) { - // Let caller set a custom drag image using dataTransfer.setDragImage() - // and/or modify visual feedback otherwise. - dndOpts.dragImage(node, data); - } else { - // Set the title as drag image (otherwise it would contain the expander) - $dragImage = $(node.span).find( - ".fancytree-title" - ); - - if ( - SOURCE_NODE_LIST && - SOURCE_NODE_LIST.length > 1 - ) { - // Add a counter badge to node title if dragging more than one node. - // We want this, because the element that is used as drag image - // must be *visible* in the DOM, so we cannot create some hidden - // custom markup. - // See https://kryogenix.org/code/browser/custom-drag-image.html - // Also, since IE 11 and Edge don't support setDragImage() alltogether, - // it gives som feedback to the user. - // The badge will be removed later on drag end. - $extraHelper = $( - "" - ) - .text( - "+" + (SOURCE_NODE_LIST.length - 1) - ) - .appendTo($dragImage); - } - if (dataTransfer.setDragImage) { - // IE 11 and Edge do not support this - dataTransfer.setDragImage( - $dragImage[0], - -10, - -10 - ); - } - } - // Let user modify above settings - if (dndOpts.dragStart(node, data) !== false) { - return true; - } - // Clear dragged node to be safe - _clearGlobals(); - return false; - - case "drag": - // Called every few miliseconds - // data.tree.info("drag", SOURCE_NODE) - $sourceList.toggleClass(classDragRemove, isMove); - dndOpts.dragDrag(node, data); - break; - - case "dragend": - _clearGlobals(); - // data.dropEffect = dropEffect; - data.isCancelled = dropEffect === "none"; - $dropMarker.hide(); - // Take this badge off of me - I can't use it anymore: - if ($extraHelper) { - $extraHelper.remove(); - $extraHelper = null; - } - dndOpts.dragEnd(node, data); - break; - } - }); + tree.$container.on( + "dragstart drag dragend", + onDragEvent.bind(tree) + ); } // Enable drop support if dragEnter() is specified: if (dndOpts.dragEnter) { // Bind drop event handlers tree.$container.on( "dragenter dragover dragleave drop", - function(event) { - var json, - nodeData, - isSourceFtNode, - r, - res, - allowDrop = null, - node = getNode(event), - dataTransfer = - event.dataTransfer || - event.originalEvent.dataTransfer, - // dropEffect = getDropEffect(dataTransfer, opts), - data = { - node: node, - tree: tree, - options: tree.options, - hitMode: DRAG_ENTER_RESPONSE, - originalEvent: event, - dataTransfer: dataTransfer, - otherNode: SOURCE_NODE || null, - otherNodeList: SOURCE_NODE_LIST || null, - otherNodeData: null, // set by drop event - dropEffect: undefined, // set by drop event - isCancelled: undefined, // set by drop event - }; - - switch (event.type) { - case "dragenter": - // The dragenter event is fired when a dragged element or - // text selection enters a valid drop target. - - DRAG_OVER_STAMP = null; - if (!node) { - // Sometimes we get dragenter for the container element - tree.debug( - "Ignore non-node " + - event.type + - ": " + - event.target.tagName + - "." + - event.target.className - ); - DRAG_ENTER_RESPONSE = false; - break; - } - - $(node.span) - .addClass(classDropOver) - .removeClass( - classDropAccept + " " + classDropReject - ); - - // Data is only readable in the dragstart and drop event, - // but we can check for the type: - isSourceFtNode = - $.inArray( - nodeMimeType, - dataTransfer.types - ) >= 0; - - if ( - dndOpts.preventNonNodes && - !isSourceFtNode - ) { - node.debug("Reject dropping a non-node."); - DRAG_ENTER_RESPONSE = false; - break; - } else if ( - dndOpts.preventForeignNodes && - (!SOURCE_NODE || - SOURCE_NODE.tree !== node.tree) - ) { - node.debug( - "Reject dropping a foreign node." - ); - DRAG_ENTER_RESPONSE = false; - break; - } - - $dropMarker.show(); - - // Call dragEnter() to figure out if (and where) dropping is allowed - if ( - dndOpts.preventRecursiveMoves && - node.isDescendantOf(data.otherNode) - ) { - node.debug( - "Reject dropping below own ancestor." - ); - res = false; - } else { - r = dndOpts.dragEnter(node, data); - res = normalizeDragEnterResponse(r); - } - DRAG_ENTER_RESPONSE = res; - - allowDrop = - res && - (res.over || res.before || res.after); - break; - - case "dragover": - if (!node) { - tree.debug( - "Ignore non-node " + - event.type + - ": " + - event.target.tagName + - "." + - event.target.className - ); - break; - } - // The dragover event is fired when an element or text - // selection is being dragged over a valid drop target - // (every few hundred milliseconds). - // console.log(event.type, "dropEffect: " + dataTransfer.dropEffect) - LAST_HIT_MODE = handleDragOver(event, data); - allowDrop = !!LAST_HIT_MODE; - - // console.log(event.type, LAST_HIT_MODE, DRAG_OVER_STAMP) - - if ( - LAST_HIT_MODE === "over" && - !node.expanded && - node.hasChildren() !== false - ) { - if (!DRAG_OVER_STAMP) { - DRAG_OVER_STAMP = Date.now(); - } else if ( - dndOpts.autoExpandMS && - Date.now() - DRAG_OVER_STAMP > - dndOpts.autoExpandMS && - (!dndOpts.dragExpand || - dndOpts.dragExpand(node, data) !== - false) - ) { - node.setExpanded(); - } - } else { - DRAG_OVER_STAMP = null; - } - // // NOTE: dragenter is fired BEFORE the dragleave event - // // of the previous element! - // // https://www.w3.org/Bugs/Public/show_bug.cgi?id=19041 - // setTimeout(function(){ - // node.info("DELAYED " + event.type, event.target, DRAG_ENTER_RESPONSE); - // // Auto-expand node (only when 'over' the node, not 'before', or 'after') - // if( dndOpts.autoExpandMS && - // node.hasChildren() !== false && !node.expanded && - // (!dndOpts.dragExpand || dndOpts.dragExpand(node, data) !== false) - // // res.over - // ) { - // node.scheduleAction("expand", dndOpts.autoExpandMS); - // } - // }, 0); - - break; - - case "dragleave": - // NOTE: dragleave is fired AFTER the dragenter event of the - // FOLLOWING element. - if (!node) { - tree.debug( - "Ignore non-node " + - event.type + - ": " + - event.target.tagName + - "." + - event.target.className - ); - break; - } - if (!$(node.span).hasClass(classDropOver)) { - node.debug("Ignore dragleave (multi)."); - break; - } - $(node.span).removeClass( - classDropOver + - " " + - classDropAccept + - " " + - classDropReject - ); - node.scheduleAction("cancel"); - dndOpts.dragLeave(node, data); - $dropMarker.hide(); - break; - - case "drop": - // Data is only readable in the (dragstart and) drop event: - - if ( - $.inArray( - nodeMimeType, - dataTransfer.types - ) >= 0 - ) { - nodeData = dataTransfer.getData( - nodeMimeType - ); - tree.info( - event.type + - ": getData('application/x-fancytree-node'): '" + - nodeData + - "'" - ); - } - if (!nodeData) { - // 1. Source is not a Fancytree node, or - // 2. If the FT mime type was set, but returns '', this - // is probably IE 11 (which only supports 'text') - nodeData = dataTransfer.getData("text"); - tree.info( - event.type + - ": getData('text'): '" + - nodeData + - "'" - ); - } - if (nodeData) { - try { - // 'text' type may contain JSON if IE is involved - // and setTextTypeJson option was set - json = JSON.parse(nodeData); - if (json.title !== undefined) { - data.otherNodeData = json; - } - } catch (ex) { - // assume 'text' type contains plain text, so `otherNodeData` - // should not be set - } - } - tree.debug( - event.type + - ": nodeData: '" + - nodeData + - "', otherNodeData: ", - data.otherNodeData - ); - - $(node.span).removeClass( - classDropOver + - " " + - classDropAccept + - " " + - classDropReject - ); - $dropMarker.hide(); - - data.hitMode = LAST_HIT_MODE; - data.dropEffect = dataTransfer.dropEffect; - data.isCancelled = data.dropEffect === "none"; - - // Let user implement the actual drop operation - dndOpts.dragDrop(node, data); - - // Prevent browser's default drop handling - event.preventDefault(); - _clearGlobals(); - break; - } - // Dnd API madness: we must PREVENT default handling to enable dropping - if (allowDrop) { - event.preventDefault(); - return false; - } - } + onDropEvent.bind(tree) ); } }, @@ -9889,13 +10259,13 @@ var uniqueId = $.fn.extend( { * Make node titles editable. * (Extension module for jquery.fancytree.js: 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 */ (function(factory) { @@ -9999,7 +10369,7 @@ var uniqueId = $.fn.extend( { .change(function(event) { $input.addClass("fancytree-edit-dirty"); }) - .keydown(function(event) { + .on("keydown", function(event) { switch (event.which) { case $.ui.keyCode.ESCAPE: node.editEnd(false, event); @@ -10187,7 +10557,7 @@ var uniqueId = $.fn.extend( { */ $.ui.fancytree.registerExtension({ name: "edit", - version: "2.30.2", + version: "2.31.0", // Default options for this extension. options: { adjustWidthOfs: 4, // null: don't adjust input size to content @@ -10208,8 +10578,21 @@ var uniqueId = $.fn.extend( { currentNode: null, treeInit: function(ctx) { + var tree = ctx.tree; + this._superApply(arguments); - this.$container.addClass("fancytree-ext-edit"); + + this.$container + .addClass("fancytree-ext-edit") + .on("fancytreebeforeupdateviewport", function(event, data) { + var editNode = tree.isEditing(); + // When scrolling, the TR may be re-used by another node, so the + // active cell marker an + if (editNode) { + editNode.info("Cancel edit due to scroll event."); + editNode.editEnd(false, event); + } + }); }, nodeClick: function(ctx) { var eventStr = $.ui.fancytree.eventToString(ctx.originalEvent), @@ -10280,13 +10663,13 @@ var uniqueId = $.fn.extend( { * Remove or highlight tree nodes, based on a filter. * (Extension module for jquery.fancytree.js: 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 */ (function(factory) { @@ -10312,8 +10695,7 @@ var uniqueId = $.fn.extend( { escapeHtml = $.ui.fancytree.escapeHtml; function _escapeRegex(str) { - /*jshint regexdash:true */ - return (str + "").replace(/([.?*+\^\$\[\]\\(){}|-])/g, "\\$1"); + return (str + "").replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1"); } function extractHtmlText(s) { @@ -10335,6 +10717,7 @@ var uniqueId = $.fn.extend( { re, reHighlight, temp, + prevEnableUpdate, count = 0, treeOpts = this.options, escapeTitles = treeOpts.escapeTitles, @@ -10402,6 +10785,8 @@ var uniqueId = $.fn.extend( { this.enableFilter = true; this.lastFilterArgs = arguments; + prevEnableUpdate = this.enableUpdate(false); + this.$div.addClass("fancytree-ext-filter"); if (hideMode) { this.$div.addClass("fancytree-ext-filter-hide"); @@ -10484,7 +10869,9 @@ var uniqueId = $.fn.extend( { this.getRootNode().addNode(statusNode).match = true; } // Redraw whole tree - this.render(); + this._callHook("treeStructureChanged", this, "applyFilter"); + // this.render(); + this.enableUpdate(prevEnableUpdate); return count; }; @@ -10510,16 +10897,6 @@ var uniqueId = $.fn.extend( { return this._applyFilterImpl(filter, false, opts); }; - /** - * @deprecated - */ - $.ui.fancytree._FancytreeClass.prototype.applyFilter = function(filter) { - this.warn( - "Fancytree.applyFilter() is deprecated since 2.1.0 / 2014-05-29. Use .filterNodes() instead." - ); - return this.filterNodes.apply(this, arguments); - }; - /** * [ext-filter] Dimm or hide whole branches. * @@ -10546,7 +10923,8 @@ var uniqueId = $.fn.extend( { var $title, statusNode = this.getRootNode()._findDirectChild(KeyNoData), escapeTitles = this.options.escapeTitles, - enhanceTitle = this.options.enhanceTitle; + enhanceTitle = this.options.enhanceTitle, + prevEnableUpdate = this.enableUpdate(false); if (statusNode) { statusNode.remove(); @@ -10588,7 +10966,9 @@ var uniqueId = $.fn.extend( { this.$div.removeClass( "fancytree-ext-filter fancytree-ext-filter-dimm fancytree-ext-filter-hide" ); - this.render(); + this._callHook("treeStructureChanged", this, "clearFilter"); + // this.render(); + this.enableUpdate(prevEnableUpdate); }; /** @@ -10620,7 +11000,7 @@ var uniqueId = $.fn.extend( { */ $.ui.fancytree.registerExtension({ name: "filter", - version: "2.30.2", + version: "2.31.0", // Default options for this extension. options: { autoApply: true, // Re-apply last filter if lazy data is loaded @@ -10635,28 +11015,29 @@ var uniqueId = $.fn.extend( { mode: "dimm", // Grayout unmatched nodes (pass "hide" to remove unmatched node instead) }, nodeLoadChildren: function(ctx, source) { + var tree = ctx.tree; + return this._superApply(arguments).done(function() { if ( - ctx.tree.enableFilter && - ctx.tree.lastFilterArgs && + tree.enableFilter && + tree.lastFilterArgs && ctx.options.filter.autoApply ) { - ctx.tree._applyFilterImpl.apply( - ctx.tree, - ctx.tree.lastFilterArgs - ); + tree._applyFilterImpl.apply(tree, tree.lastFilterArgs); } }); }, nodeSetExpanded: function(ctx, flag, callOpts) { - delete ctx.node._filterAutoExpanded; + var node = ctx.node; + + delete node._filterAutoExpanded; // Make sure counter badge is displayed again, when node is beeing collapsed if ( !flag && ctx.options.filter.hideExpandedCounter && - ctx.node.$subMatchBadge + node.$subMatchBadge ) { - ctx.node.$subMatchBadge.show(); + node.$subMatchBadge.show(); } return this._superApply(arguments); }, @@ -10703,7 +11084,7 @@ var uniqueId = $.fn.extend( { node.$subMatchBadge.hide(); } // node.debug("nodeRenderStatus", node.titleWithHighlight, node.title) - // #601: also chek for $title.length, because we don't need to render + // #601: also check for $title.length, because we don't need to render // if node.span is null (i.e. not rendered) if (node.span && (!node.isEditing || !node.isEditing.call(node))) { if (node.titleWithHighlight) { @@ -10734,13 +11115,13 @@ var uniqueId = $.fn.extend( { * Use glyph-fonts, ligature-fonts, or SVG icons instead of icon sprites. * (Extension module for jquery.fancytree.js: 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 */ (function(factory) { @@ -10900,12 +11281,13 @@ var uniqueId = $.fn.extend( { var map = opts.map, icon = map[type], $span = $(span), + $counter = $span.find(".fancytree-childcounter"), setClass = baseClass + " " + (map._addClass || ""); if (typeof icon === "string") { // #883: remove inner html that may be added by prev. mode span.innerHTML = ""; - $span.attr("class", setClass + " " + icon); + $span.attr("class", setClass + " " + icon).append($counter); } else if (icon) { if (icon.text) { span.textContent = "" + icon.text; @@ -10914,13 +11296,15 @@ var uniqueId = $.fn.extend( { } else { span.innerHTML = ""; } - $span.attr("class", setClass + " " + (icon.addClass || "")); + $span + .attr("class", setClass + " " + (icon.addClass || "")) + .append($counter); } } $.ui.fancytree.registerExtension({ name: "glyph", - version: "2.30.2", + version: "2.31.0", // Default options for this extension. options: { preset: null, // 'awesome3', 'awesome4', 'bootstrap3', 'material' @@ -10995,11 +11379,12 @@ var uniqueId = $.fn.extend( { icon ); } else { + // eslint-disable-next-line no-nested-ternary icon = node.selected ? "checkboxSelected" : node.partsel - ? "checkboxUnknown" - : "checkbox"; + ? "checkboxUnknown" + : "checkbox"; // span.className = "fancytree-checkbox " + map[icon]; setIcon(span, "fancytree-checkbox", opts, icon); } @@ -11068,13 +11453,13 @@ var uniqueId = $.fn.extend( { * Support keyboard navigation for trees with embedded input controls. * (Extension module for jquery.fancytree.js: 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 */ (function(factory) { @@ -11191,7 +11576,7 @@ var uniqueId = $.fn.extend( { */ $.ui.fancytree.registerExtension({ name: "gridnav", - version: "2.30.2", + version: "2.31.0", // Default options for this extension. options: { autofocusInput: false, // Focus first embedded input if node gets activated @@ -11243,7 +11628,10 @@ var uniqueId = $.fn.extend( { // Set focus to input sub input (if node was clicked, but not // when TAB was pressed ) $outer = $(node.tr || node.span); - $outer.find(":input:enabled:first").focus(); + $outer + .find(":input:enabled") + .first() + .focus(); } } }, @@ -11290,13 +11678,13 @@ var uniqueId = $.fn.extend( { * Allow multiple selection of nodes by mouse or keyboard. * (Extension module for jquery.fancytree.js: 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 */ (function(factory) { @@ -11325,7 +11713,7 @@ var uniqueId = $.fn.extend( { */ $.ui.fancytree.registerExtension({ name: "multi", - version: "2.30.2", + version: "2.31.0", // Default options for this extension. options: { allowNoSelect: false, // @@ -11422,13 +11810,13 @@ var uniqueId = $.fn.extend( { * * @depends: js-cookie or jquery-cookie * - * 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 */ (function(factory) { @@ -11504,8 +11892,8 @@ var uniqueId = $.fn.extend( { } /* Recursively load lazy nodes - * @param {string} mode 'load', 'expand', false - */ + * @param {string} mode 'load', 'expand', false + */ function _loadLazyNodes(tree, local, keyList, mode, dfd) { var i, key, @@ -11628,7 +12016,7 @@ var uniqueId = $.fn.extend( { */ $.ui.fancytree.registerExtension({ name: "persist", - version: "2.30.2", + version: "2.31.0", // Default options for this extension. options: { cookieDelimiter: "~", @@ -11916,13 +12304,13 @@ var uniqueId = $.fn.extend( { * Render tree as table (aka 'tree grid', 'table tree'). * (Extension module for jquery.fancytree.js: 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 */ (function(factory) { @@ -12003,24 +12391,14 @@ var uniqueId = $.fn.extend( { return prev; } - /* Render callback for 'wide' mode. */ - // function _renderStatusNodeWide(event, data) { - // var node = data.node, - // nodeColumnIdx = data.options.table.nodeColumnIdx, - // $tdList = $(node.tr).find(">td"); - - // $tdList.eq(nodeColumnIdx).attr("colspan", data.tree.columnCount); - // $tdList.not(":eq(" + nodeColumnIdx + ")").remove(); - // } - $.ui.fancytree.registerExtension({ name: "table", - version: "2.30.2", + version: "2.31.0", // Default options for this extension. options: { checkboxColumnIdx: null, // render the checkboxes into the this column index (default: nodeColumnIdx) - mergeStatusColumns: true, // display 'nodata', 'loading', 'error' centered in a single, merged TR indentation: 16, // indent every node level by 16px + mergeStatusColumns: true, // display 'nodata', 'loading', 'error' centered in a single, merged TR nodeColumnIdx: 0, // render node expander, icon, and title to this column (default: #0) }, // Overide virtual methods for this extension. @@ -12037,15 +12415,15 @@ var uniqueId = $.fn.extend( { $table = tree.widget.element; if (tableOpts.customStatus != null) { - if (opts.renderStatusColumns != null) { - $.error( - "The 'customStatus' option is deprecated since v2.15.0. Use 'renderStatusColumns' only instead." - ); - } else { + if (opts.renderStatusColumns == null) { tree.warn( "The 'customStatus' option is deprecated since v2.15.0. Use 'renderStatusColumns' instead." ); opts.renderStatusColumns = tableOpts.customStatus; + } else { + $.error( + "The 'customStatus' option is deprecated since v2.15.0. Use 'renderStatusColumns' only instead." + ); } } if (opts.renderStatusColumns) { @@ -12072,9 +12450,11 @@ var uniqueId = $.fn.extend( { // Prepare row templates: // Determine column count from table header if any - tree.columnCount = $("thead >tr:last >th", $table).length; + tree.columnCount = $("thead >tr", $table) + .last() + .find(">th", $table).length; // Read TR templates from tbody if any - $row = $tbody.children("tr:first"); + $row = $tbody.children("tr").first(); if ($row.length) { n = $row.children("td").length; if (tree.columnCount && n !== tree.columnCount) { @@ -12110,7 +12490,7 @@ var uniqueId = $.fn.extend( { tree.rowFragment.appendChild($row.get(0)); // // If tbody contains a second row, use this as status node template - // $row = $tbody.children("tr:eq(1)"); + // $row = $tbody.children("tr").eq(1); // if( $row.length === 0 ) { // tree.statusRowFragment = tree.rowFragment; // } else { @@ -12191,7 +12571,15 @@ var uniqueId = $.fn.extend( { if (node.tr && force) { this.nodeRemoveMarkup(ctx); } - if (!node.tr) { + if (node.tr) { + if (force) { + // Set icon, link, and title (normally this is only required on initial render) + this.nodeRenderTitle(ctx); // triggers renderColumns() + } else { + // Update element classes according to node state + this.nodeRenderStatus(ctx); + } + } else { if (ctx.hasCollapsedParents && !deep) { // #166: we assume that the parent will be (recursively) rendered // later anyway. @@ -12215,15 +12603,15 @@ var uniqueId = $.fn.extend( { newRow.style.display = "none"; // newRow.style.color = "red"; } - if (!prevNode.tr) { + if (prevNode.tr) { + insertSiblingAfter(prevNode.tr, newRow); + } else { _assert( !prevNode.parent, "prev. row must have a tr, or be system root" ); // tree.tbody.appendChild(newRow); insertFirstChild(tree.tbody, newRow); // #675 - } else { - insertSiblingAfter(prevNode.tr, newRow); } node.tr = newRow; if (node.key && opts.generateIds) { @@ -12241,14 +12629,6 @@ var uniqueId = $.fn.extend( { if (opts.createNode) { opts.createNode.call(tree, { type: "createNode" }, ctx); } - } else { - if (force) { - // Set icon, link, and title (normally this is only required on initial render) - this.nodeRenderTitle(ctx); // triggers renderColumns() - } else { - // Update element classes according to node state - this.nodeRenderStatus(ctx); - } } } // Allow tweaking after node state was rendered @@ -12329,13 +12709,13 @@ var uniqueId = $.fn.extend( { if (opts.renderStatusColumns) { // Let user code write column content opts.renderStatusColumns.call( - ctx.tree, + tree, { type: "renderStatusColumns" }, ctx ); } else if (opts.table.mergeStatusColumns && node.isTopLevel()) { - var $tdList = $(node.tr).find(">td"); - $tdList + $(node.tr) + .find(">td") .eq(0) .prop("colspan", tree.columnCount) .text(node.title) @@ -12344,11 +12724,7 @@ var uniqueId = $.fn.extend( { .remove(); } // else: default rendering for status node: leave other cells empty } else if (opts.renderColumns) { - opts.renderColumns.call( - ctx.tree, - { type: "renderColumns" }, - ctx - ); + opts.renderColumns.call(tree, { type: "renderColumns" }, ctx); } return res; }, @@ -12453,7 +12829,9 @@ var uniqueId = $.fn.extend( { }, treeDestroy: function(ctx) { this.$container.find("tbody").empty(); - this.$source && this.$source.removeClass("fancytree-helper-hidden"); + if (this.$source) { + this.$source.removeClass("fancytree-helper-hidden"); + } return this._superApply(arguments); }, /*, @@ -12476,13 +12854,13 @@ var uniqueId = $.fn.extend( { * * @see http://jqueryui.com/themeroller/ * - * 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 */ (function(factory) { @@ -12505,7 +12883,7 @@ var uniqueId = $.fn.extend( { */ $.ui.fancytree.registerExtension({ name: "themeroller", - version: "2.30.2", + version: "2.31.0", // Default options for this extension. options: { activeClass: "ui-state-active", // Class added to active node @@ -12531,9 +12909,7 @@ var uniqueId = $.fn.extend( { $el.addClass("ui-widget ui-widget-content ui-corner-all"); } - $el.delegate(".fancytree-node", "mouseenter mouseleave", function( - event - ) { + $el.on("mouseenter mouseleave", ".fancytree-node", function(event) { var node = $.ui.fancytree.getNode(event.target), flag = event.type === "mouseenter"; @@ -12598,13 +12974,13 @@ var uniqueId = $.fn.extend( { * Support for 100% wide selection bars. * (Extension module for jquery.fancytree.js: 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 */ (function(factory) { @@ -12734,7 +13110,7 @@ var uniqueId = $.fn.extend( { */ $.ui.fancytree.registerExtension({ name: "wide", - version: "2.30.2", + version: "2.31.0", // Default options for this extension. options: { iconWidth: null, // Adjust this if @fancy-icon-width != "16px" diff --git a/libraries/fancytree/jquery.fancytree-all-deps.min.js b/libraries/fancytree/jquery.fancytree-all-deps.min.js index 6a0e0acda..dc39a2852 100644 --- a/libraries/fancytree/jquery.fancytree-all-deps.min.js +++ b/libraries/fancytree/jquery.fancytree-all-deps.min.js @@ -1,2 +1,2 @@ -!function(e){e.ui=e.ui||{};e.ui.version="1.12.1";var t=0,n=Array.prototype.slice;e.cleanData=function(t){return function(n){var i,r,o;for(o=0;null!=(r=n[o]);o++)try{(i=e._data(r,"events"))&&i.remove&&e(r).triggerHandler("remove")}catch(e){}t(n)}}(e.cleanData),e.widget=function(t,n,i){var r,o,s,a={},l=t.split(".")[0],d=l+"-"+(t=t.split(".")[1]);return i||(i=n,n=e.Widget),e.isArray(i)&&(i=e.extend.apply(null,[{}].concat(i))),e.expr[":"][d.toLowerCase()]=function(t){return!!e.data(t,d)},e[l]=e[l]||{},r=e[l][t],o=e[l][t]=function(e,t){if(!this._createWidget)return new o(e,t);arguments.length&&this._createWidget(e,t)},e.extend(o,r,{version:i.version,_proto:e.extend({},i),_childConstructors:[]}),s=new n,s.options=e.widget.extend({},s.options),e.each(i,function(t,i){e.isFunction(i)?a[t]=function(){function e(){return n.prototype[t].apply(this,arguments)}function r(e){return n.prototype[t].apply(this,e)}return function(){var t,n=this._super,o=this._superApply;return this._super=e,this._superApply=r,t=i.apply(this,arguments),this._super=n,this._superApply=o,t}}():a[t]=i}),o.prototype=e.widget.extend(s,{widgetEventPrefix:r?s.widgetEventPrefix||t:t},a,{constructor:o,namespace:l,widgetName:t,widgetFullName:d}),r?(e.each(r._childConstructors,function(t,n){var i=n.prototype;e.widget(i.namespace+"."+i.widgetName,o,n._proto)}),delete r._childConstructors):n._childConstructors.push(o),e.widget.bridge(t,o),o},e.widget.extend=function(t){for(var i,r,o=n.call(arguments,1),s=0,a=o.length;s",options:{classes:{},disabled:!1,create:null},_createWidget:function(n,i){i=e(i||this.defaultElement||this)[0],this.element=e(i),this.uuid=t++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=e(),this.hoverable=e(),this.focusable=e(),this.classesElementLookup={},i!==this&&(e.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===i&&this.destroy()}}),this.document=e(i.style?i.ownerDocument:i.document||i),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this.options=e.widget.extend({},this.options,this._getCreateOptions(),n),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){var t=this;this._destroy(),e.each(this.classesElementLookup,function(e,n){t._removeClass(n,e)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:e.noop,widget:function(){return this.element},option:function(t,n){var i,r,o,s=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(s={},i=t.split("."),t=i.shift(),i.length){for(r=s[t]=e.widget.extend({},this.options[t]),o=0;o
- ",{class:"ui-fancytree fancytree-container fancytree-plain"}).appendTo(this.$div),this.$container=n,this.rootNode.ul=n[0],null==this.options.debugLevel&&(this.options.debugLevel=y.debugLevel)}{if(!e.ui||!e.ui.fancytree){var p,g,y=null,v=new RegExp(/\.|\//),m=/[&<>"'\/]/g,x=/[<>"'\/]/g,b="$recursive_request",C={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},_={16:!0,17:!0,18:!0},k={8:"backspace",9:"tab",10:"return",13:"return",19:"pause",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"del",59:";",61:"=",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",109:"-",110:".",111:"/",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:"scroll",173:"-",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},w={16:"shift",17:"ctrl",18:"alt",91:"meta",93:"meta"},N={0:"",1:"left",2:"middle",3:"right"},S="active expanded focus folder lazy radiogroup selected unselectable unselectableIgnore".split(" "),E={},T="columns types".split(" "),A="checkbox expanded extraClasses folder icon iconTooltip key lazy partsel radiogroup refKey selected statusNodeType title tooltip type unselectable unselectableIgnore unselectableStatus".split(" "),O={},F={},P={active:!0,children:!0,data:!0,focus:!0};for(p=0;p
",{id:l}).hide().insertAfter(this.$container),!1!==n&&this.activeNode&&d.append(e("",{type:"radio",name:a,value:this.activeNode.key,checked:!0})),i.filter?this.visit(function(e){var t=i.filter(e);if("skip"===t)return t;!1!==t&&r(e)}):!1!==t&&(o=this.getSelectedNodes(c),e.each(o,function(e,t){r(t)}))},getActiveNode:function(){return this.activeNode},getFirstChild:function(){return this.rootNode.getFirstChild()},getFocusNode:function(){return this.focusNode},getOption:function(e){return this.widget.option(e)},getNodeByKey:function(e,t){var n,i;return!t&&(n=document.getElementById(this.options.idPrefix+e))?n.ftnode?n.ftnode:null:(t=t||this.rootNode,i=null,t.visit(function(t){if(t.key===e)return i=t,!1},!0),i)},getRootNode:function(){return this.rootNode},getSelectedNodes:function(e){return this.rootNode.getSelectedNodes(e)},hasFocus:function(){return!!this._hasFocus},info:function(e){this.options.debugLevel>=3&&(Array.prototype.unshift.call(arguments,this.toString()),n("info",arguments))},loadKeyPath:function(t,n){var i,r,o,s=this,a=new e.Deferred,l=this.getRootNode(),d=this.options.keyPathSeparator,c=[],u=e.extend({},n);for("function"==typeof n?i=n:n&&n.callback&&(i=n.callback),u.callback=function(e,t,n){i&&i.call(e,t,n),a.notifyWith(e,[{node:t,status:n}])},null==u.matchKey&&(u.matchKey=function(e,t){return e.key===t}),e.isArray(t)||(t=[t]),r=0;r=2&&(Array.prototype.unshift.call(arguments,this.toString()),n("warn",arguments))}},e.extend(f.prototype,{nodeClick:function(e){var t,n,i=e.targetType,r=e.node;if("expander"===i){if(r.isLoading())return void r.debug("Got 2nd click while loading: ignored");this._callHook("nodeToggleExpanded",e)}else if("checkbox"===i)this._callHook("nodeToggleSelected",e),e.options.focusOnSelect&&this._callHook("nodeSetFocus",e,!0);else{if(n=!1,t=!0,r.folder)switch(e.options.clickFolderMode){case 2:n=!0,t=!1;break;case 3:t=!0,n=!0}t&&(this.nodeSetFocus(e),this._callHook("nodeSetActive",e,!0)),n&&this._callHook("nodeToggleExpanded",e)}},nodeCollapseSiblings:function(e,t){var n,i,r,o=e.node;if(o.parent)for(i=0,r=(n=o.parent.children).length;i500&&(a.lastQuicksearchTerm=""),a.lastQuicksearchTime=i,a.lastQuicksearchTerm+=c,(n=a.findNextNode(a.lastQuicksearchTerm,a.getActiveNode()))&&n.setActive(),void o.preventDefault();switch(y.eventToString(o)){case"+":case"=":a.nodeSetExpanded(t,!0);break;case"-":a.nodeSetExpanded(t,!1);break;case"space":s.isPagingNode()?a._triggerNodeEvent("clickPaging",t,o):y.evalOption("checkbox",s,s,l,!1)?a.nodeToggleSelected(t):a.nodeSetActive(t,!0);break;case"return":a.nodeSetActive(t,!0);break;case"home":case"end":case"backspace":case"left":case"right":case"up":case"down":s.navigate(o.which,g);break;default:p=!1}p&&o.preventDefault()},nodeLoadChildren:function(n,i){var r,o,s,a=n.tree,l=n.node,d=Date.now();if(e.isFunction(i)&&(i=i.call(a,{type:"source"},n),t(!e.isFunction(i),"source callback must not return another function")),i.url&&(l._requestId&&l.warn("Recursive load request #"+d+" while #"+l._requestId+" is pending."),r=e.extend({},n.options.ajax,i),l._requestId=d,r.debugDelay?(o=r.debugDelay,delete r.debugDelay,e.isArray(o)&&(o=o[0]+Math.random()*(o[1]-o[0])),l.warn("nodeLoadChildren waiting debugDelay "+Math.round(o)+" ms ..."),s=e.Deferred(function(t){setTimeout(function(){e.ajax(r).done(function(){t.resolveWith(this,arguments)}).fail(function(){t.rejectWith(this,arguments)})},o)})):s=e.ajax(r),i=new e.Deferred,s.done(function(t,r,o){var s,c;if("json"!==this.dataType&&"jsonp"!==this.dataType||"string"!=typeof t||e.error("Ajax request returned a string (did you get the JSON dataType wrong?)."),l._requestId&&l._requestId>d)i.rejectWith(this,[b]);else{if(n.options.postProcess){try{c=a._triggerNodeEvent("postProcess",n,n.originalEvent,{response:t,error:null,dataType:this.dataType})}catch(e){c={error:e,message:""+e,details:"postProcess failed"}}if(c.error)return s=e.isPlainObject(c.error)?c.error:{message:c.error},s=a._makeHookContext(l,null,s),void i.rejectWith(this,[s]);(e.isArray(c)||e.isPlainObject(c)&&e.isArray(c.children))&&(t=c)}else t&&t.hasOwnProperty("d")&&n.options.enableAspx&&(t="string"==typeof t.d?e.parseJSON(t.d):t.d);i.resolveWith(this,[t])}}).fail(function(e,t,n){var r=a._makeHookContext(l,null,{error:e,args:Array.prototype.slice.call(arguments),message:n,details:e.status+": "+n});i.rejectWith(this,[r])})),e.isFunction(i.then)&&e.isFunction(i.catch)&&(s=i,i=new e.Deferred,s.then(function(e){i.resolve(e)},function(e){i.reject(e)})),e.isFunction(i.promise))a.nodeSetStatus(n,"loading"),i.done(function(e){a.nodeSetStatus(n,"ok"),l._requestId=null}).fail(function(e){var t;e!==b?(e.node&&e.error&&e.message?t=e:"[object Object]"===(t=a._makeHookContext(l,null,{error:e,args:Array.prototype.slice.call(arguments),message:e?e.message||e.toString():""})).message&&(t.message=""),l.warn("Load children failed ("+t.message+")",t),!1!==a._triggerNodeEvent("loadError",t,null)&&a.nodeSetStatus(n,"error",t.message,t.details)):l.warn("Ignored response for obsolete load request #"+d+" (expected #"+l._requestId+")")});else if(n.options.postProcess){var c=a._triggerNodeEvent("postProcess",n,n.originalEvent,{response:i,error:null,dataType:typeof i});(e.isArray(c)||e.isPlainObject(c)&&e.isArray(c.children))&&(i=c)}return e.when(i).done(function(i){var r,o;e.isPlainObject(i)&&(t(l.isRootNode(),"source may only be an object for root nodes (expecting an array of child objects otherwise)"),t(e.isArray(i.children),"if an object is passed as source, it must contain a 'children' array (all other properties are added to 'tree.data')"),r=i,i=i.children,delete r.children,e.each(T,function(e,t){void 0!==r[t]&&(a[t]=r[t],delete r[t])}),e.extend(a.data,r)),t(e.isArray(i),"expected array of children"),l._setChildren(i),a.options.nodata&&0===i.length&&(e.isFunction(a.options.nodata)?o=a.options.nodata.call(a,{type:"nodata"},n):!0===a.options.nodata&&l.isRootNode()?o=a.options.strings.nodata:"string"==typeof a.options.nodata&&l.isRootNode()&&(o=a.options.nodata),o&&l.setStatus("nodata",o)),a._triggerNodeEvent("loadChildren",l)})},nodeLoadKeyPath:function(e,t){},nodeRemoveChild:function(n,i){var r,o=n.node,s=e.extend({},n,{node:i}),a=o.children;if(1===a.length)return t(i===a[0],"invalid single child"),this.nodeRemoveChildren(n);this.activeNode&&(i===this.activeNode||this.activeNode.isDescendantOf(i))&&this.activeNode.setActive(!1),this.focusNode&&(i===this.focusNode||this.focusNode.isDescendantOf(i))&&(this.focusNode=null),this.nodeRemoveMarkup(s),this.nodeRemoveChildren(s),t((r=e.inArray(i,a))>=0,"invalid child"),o.triggerModifyChild("remove",i),i.visit(function(e){e.parent=null},!0),this._callHook("treeRegisterNode",this,!1,i),a.splice(r,1)},nodeRemoveChildMarkup:function(t){var n=t.node;n.ul&&(n.isRootNode()?e(n.ul).empty():(e(n.ul).remove(),n.ul=null),n.visit(function(e){e.li=e.ul=null}))},nodeRemoveChildren:function(t){var n=t.tree,i=t.node;i.children&&(this.activeNode&&this.activeNode.isDescendantOf(i)&&this.activeNode.setActive(!1),this.focusNode&&this.focusNode.isDescendantOf(i)&&(this.focusNode=null),this.nodeRemoveChildMarkup(t),e.extend({},t),i.triggerModifyChild("remove",null),i.visit(function(e){e.parent=null,n._callHook("treeRegisterNode",n,!1,e)}),i.lazy?i.children=[]:i.children=null,i.isRootNode()||(i.expanded=!1),this.nodeRenderStatus(t))},nodeRemoveMarkup:function(t){var n=t.node;n.li&&(e(n.li).remove(),n.li=null),this.nodeRemoveChildMarkup(t)},nodeRender:function(n,i,r,o,s){var a,l,d,c,u,h,f,p=n.node,g=n.tree,y=n.options,v=y.aria,m=!1,x=p.parent,b=!x,C=p.children,_=null;if(!1!==g._enableUpdate&&(b||x.ul)){if(t(b||x.ul,"parent UL must exist"),b||(p.li&&(i||p.li.parentNode!==p.parent.ul)&&(p.li.parentNode===p.parent.ul?_=p.li.nextSibling:this.debug("Unlinking "+p+" (must be child of "+p.parent+")"),this.nodeRemoveMarkup(n)),p.li?this.nodeRenderStatus(n):(m=!0,p.li=document.createElement("li"),p.li.ftnode=p,p.key&&y.generateIds&&(p.li.id=y.idPrefix+p.key),p.span=document.createElement("span"),p.span.className="fancytree-node",v&&!p.tr&&e(p.li).attr("role","treeitem"),p.li.appendChild(p.span),this.nodeRenderTitle(n),y.createNode&&y.createNode.call(g,{type:"createNode"},n)),y.renderNode&&y.renderNode.call(g,{type:"renderNode"},n)),C){if(b||p.expanded||!0===r){for(p.ul||(p.ul=document.createElement("ul"),(!0!==o||s)&&p.expanded||(p.ul.style.display="none"),v&&e(p.ul).attr("role","group"),p.li?p.li.appendChild(p.ul):p.tree.$div.append(p.ul)),c=0,u=C.length;c1&&m.push("")):m.push(""),(i=y.evalOption("checkbox",u,u,f,!1))&&!u.isStatusNode()&&(a=p?" role='checkbox'":"",r="fancytree-checkbox",("radio"===i||u.parent&&u.parent.radiogroup)&&(r+=" fancytree-radio"),m.push("")),void 0!==u.data.iconClass&&(u.icon?e.error("'iconClass' node option is deprecated since v2.14.0: use 'icon' only instead"):(u.warn("'iconClass' node option is deprecated since v2.14.0: use 'icon' instead"),u.icon=u.data.iconClass)),!1!==(o=y.evalOption("icon",u,u,f,!0))&&(a=p?" role='presentation'":"",d=(d=y.evalOption("iconTooltip",u,u,f,null))?" title='"+c(d)+"'":"","string"==typeof o?v.test(o)?(o="/"===o.charAt(0)?o:(f.imagePath||"")+o,m.push("
")):m.push(""):o.text?m.push(""+y.escapeHtml(o.text)+""):o.html?m.push(""+o.html+""):m.push("")),s="",f.renderTitle&&(s=f.renderTitle.call(h,{type:"renderTitle"},t)||""),s||(!0===(l=y.evalOption("tooltip",u,u,f,null))&&(l=u.title),s=""+(f.escapeTitles?y.escapeHtml(u.title):u.title)+""),m.push(s),u.span.innerHTML=m.join(""),this.nodeRenderStatus(t),f.enhanceTitle&&(t.$title=e(">span.fancytree-title",u.span),s=f.enhanceTitle.call(h,{type:"enhanceTitle"},t)||""))},nodeRenderStatus:function(t){var n,i=t.node,r=t.tree,o=t.options,s=i.hasChildren(),a=i.isLastSibling(),l=o.aria,d=o._classNames,c=[],u=i[r.statusClassPropName];u&&!1!==r._enableUpdate&&(l&&(n=e(i.tr||i.li)),c.push(d.node),r.activeNode===i&&c.push(d.active),r.focusNode===i&&c.push(d.focused),i.expanded&&c.push(d.expanded),l&&(!1!==s?n.attr("aria-expanded",Boolean(i.expanded)):n.removeAttr("aria-expanded")),i.folder&&c.push(d.folder),!1!==s&&c.push(d.hasChildren),a&&c.push(d.lastsib),i.lazy&&null==i.children&&c.push(d.lazy),i.partload&&c.push(d.partload),i.partsel&&c.push(d.partsel),y.evalOption("unselectable",i,i,o,!1)&&c.push(d.unselectable),i._isLoading&&c.push(d.loading),i._error&&c.push(d.error),i.statusNodeType&&c.push(d.statusNodePrefix+i.statusNodeType),i.selected?(c.push(d.selected),l&&n.attr("aria-selected",!0)):l&&n.attr("aria-selected",!1),i.extraClasses&&c.push(i.extraClasses),!1===s?c.push(d.combinedExpanderPrefix+"n"+(a?"l":"")):c.push(d.combinedExpanderPrefix+(i.expanded?"e":"c")+(i.lazy&&null==i.children?"d":"")+(a?"l":"")),c.push(d.combinedIconPrefix+(i.expanded?"e":"c")+(i.folder?"f":"")),u.className=c.join(" "),i.li&&e(i.li).toggleClass(d.lastsib,a))},nodeSetActive:function(n,i,r){r=r||{};var o,l=n.node,d=n.tree,c=n.options,u=!0===r.noEvents,h=!0===r.noFocus,f=!1!==r.scrollIntoView;return i=!1!==i,l===d.activeNode===i?s(l):i&&!u&&!1===this._triggerNodeEvent("beforeActivate",l,n.originalEvent)?a(l,["rejected"]):(i?(d.activeNode&&(t(d.activeNode!==l,"node was active (inconsistency)"),o=e.extend({},n,{node:d.activeNode}),d.nodeSetActive(o,!1),t(null===d.activeNode,"deactivate was out of sync?")),c.activeVisible&&l.makeVisible({scrollIntoView:f}),d.activeNode=l,d.nodeRenderStatus(n),h||d.nodeSetFocus(n),u||d._triggerNodeEvent("activate",l,n.originalEvent)):(t(d.activeNode===l,"node was not active (inconsistency)"),d.activeNode=null,this.nodeRenderStatus(n),u||n.tree._triggerNodeEvent("deactivate",l,n.originalEvent)),s(l))},nodeSetExpanded:function(t,n,i){i=i||{};var r,o,l,d,c,u,h=t.node,f=t.tree,p=t.options,g=!0===i.noAnimation,y=!0===i.noEvents;if(n=!1!==n,h.expanded&&n||!h.expanded&&!n)return s(h);if(n&&!h.lazy&&!h.hasChildren())return s(h);if(!n&&h.getLevel()ul.fancytree-container").empty(),t.rootNode.children=null},treeCreate:function(e){},treeDestroy:function(e){this.$div.find(">ul.fancytree-container").remove(),this.$source&&this.$source.removeClass("fancytree-helper-hidden")},treeInit:function(t){var n=t.tree,i=n.options;n.$container.attr("tabindex",i.tabindex),e.each(T,function(e,t){void 0!==i[t]&&(n.info("Move option "+t+" to tree"),n[t]=i[t],delete i[t])}),i.rtl?n.$container.attr("DIR","RTL").addClass("fancytree-rtl"):n.$container.removeAttr("DIR").removeClass("fancytree-rtl"),i.aria&&(n.$container.attr("role","tree"),1!==i.selectMode&&n.$container.attr("aria-multiselectable",!0)),this.treeLoad(t)},treeLoad:function(n,i){var r,o,s,a=n.tree,l=n.widget.element,c=e.extend({},n,{node:this.rootNode});if(a.rootNode.children&&this.treeClear(n),i=i||this.options.source)"string"==typeof i&&e.error("Not implemented");else switch(o=l.data("type")||"html"){case"html":(s=l.find(">ul:first")).addClass("ui-fancytree-source fancytree-helper-hidden"),i=e.ui.fancytree.parseHtml(s),this.data=e.extend(this.data,d(s));break;case"json":i=e.parseJSON(l.text()),l.contents().filter(function(){return 3===this.nodeType}).remove(),e.isPlainObject(i)&&(t(e.isArray(i.children),"if an object is passed as source, it must contain a 'children' array (all other properties are added to 'tree.data')"),r=i,i=i.children,delete r.children,e.each(T,function(e,t){void 0!==r[t]&&(a[t]=r[t],delete r[t])}),e.extend(a.data,r));break;default:e.error("Invalid data-type: "+o)}return this.nodeLoadChildren(c,i).done(function(){a.render(),3===n.options.selectMode&&a.rootNode.fixSelection3FromEndNodes(),a.activeNode&&a.options.activeVisible&&a.activeNode.makeVisible(),a._triggerTreeEvent("init",null,{status:!0})}).fail(function(){a.render(),a._triggerTreeEvent("init",null,{status:!1})})},treeRegisterNode:function(e,t,n){},treeSetFocus:function(t,n,i){var r;(n=!1!==n)!==this.hasFocus()&&(this._hasFocus=n,!n&&this.focusNode?this.focusNode.setFocus(!1):!n||i&&i.calledByNode||e(this.$container).focus(),this.$container.toggleClass("fancytree-treefocus",n),this._triggerTreeEvent(n?"focusTree":"blurTree"),n&&!this.activeNode&&(r=this._lastMousedownNode||this.getFirstChild())&&r.setFocus())},treeSetOption:function(t,n,i){var r=t.tree,o=!0,s=!1,a=!1;switch(n){case"aria":case"checkbox":case"icon":case"minExpandLevel":case"tabindex":s=!0,a=!0;break;case"escapeTitles":case"tooltip":a=!0;break;case"rtl":!1===i?r.$container.removeAttr("DIR").removeClass("fancytree-rtl"):r.$container.attr("DIR","RTL").addClass("fancytree-rtl"),a=!0;break;case"source":o=!1,r._callHook("treeLoad",r,i),a=!0}r.debug("set option "+n+"="+i+" <"+typeof i+">"),o&&(this.widget._super?this.widget._super.call(this.widget,n,i):e.Widget.prototype._setOption.call(this.widget,n,i)),s&&r._callHook("treeCreate",r),a&&r.render(!0,!1)}}),e.widget("ui.fancytree",{options:{activeVisible:!0,ajax:{type:"GET",cache:!1,dataType:"json"},aria:!0,autoActivate:!0,autoCollapse:!1,autoScroll:!1,checkbox:!1,clickFolderMode:4,debugLevel:null,disabled:!1,enableAspx:!0,escapeTitles:!1,extensions:[],toggleEffect:{effect:"slideToggle",duration:200},generateIds:!1,icon:!0,idPrefix:"ft_",focusOnSelect:!1,keyboard:!0,keyPathSeparator:"/",minExpandLevel:1,nodata:!0,quicksearch:!1,rtl:!1,scrollOfs:{top:0,bottom:0},scrollParent:null,selectMode:2,strings:{loading:"Loading...",loadError:"Load error!",moreData:"More...",noData:"No data."},tabindex:"0",titlesTabbable:!1,tooltip:!1,_classNames:{node:"fancytree-node",folder:"fancytree-folder",animating:"fancytree-animating",combinedExpanderPrefix:"fancytree-exp-",combinedIconPrefix:"fancytree-ico-",hasChildren:"fancytree-has-children",active:"fancytree-active",selected:"fancytree-selected",expanded:"fancytree-expanded",lazy:"fancytree-lazy",focused:"fancytree-focused",partload:"fancytree-partload",partsel:"fancytree-partsel",radio:"fancytree-radio",unselectable:"fancytree-unselectable",lastsib:"fancytree-lastsib",loading:"fancytree-loading",error:"fancytree-error",statusNodePrefix:"fancytree-statusnode-"},lazyLoad:null,postProcess:null},_create:function(){this.tree=new f(this),this.$source=this.source||"json"===this.element.data("type")?this.element:this.element.find(">ul:first");var n,r,s,a=this.options,l=a.extensions;this.tree;for(s=0;s element.");else{if(r){if(i._getExpiringValue("focusin"))return void i.debug("Ignored double focusin.");i._setExpiringValue("focusin",!0,50),n||(n=i._getExpiringValue("mouseDownNode"))&&i.debug("Reconstruct mouse target for focusin from recent event.")}n?i._callHook("nodeSetFocus",i._makeHookContext(n,t),r):i.tbody&&e(t.target).parents("table.fancytree-container > thead").length?i.debug("Ignore focus event outside table body.",t):i._callHook("treeSetFocus",i,r)}}).on("selectstart"+r,"span.fancytree-title",function(e){e.preventDefault()}).on("keydown"+r,function(e){if(n.disabled||!1===n.keyboard)return!0;var t,r=i.focusNode,o=i._makeHookContext(r||i,e),s=i.phase;try{return i.phase="userEvent","preventNav"===(t=r?i._triggerNodeEvent("keydown",r,e):i._triggerTreeEvent("keydown",e))?t=!0:!1!==t&&(t=i._callHook("nodeKeydown",o)),t}finally{i.phase=s}}).on("mousedown"+r,function(e){var t=y.getEventTarget(e);i._lastMousedownNode=t?t.node:null,i._setExpiringValue("mouseDownNode",i._lastMousedownNode)}).on("click"+r+" dblclick"+r,function(e){if(n.disabled)return!0;var i,r=y.getEventTarget(e),o=r.node,s=t.tree,a=s.phase;if(!o)return!0;i=s._makeHookContext(o,e);try{switch(s.phase="userEvent",e.type){case"click":return i.targetType=r.type,o.isPagingNode()?!0===s._triggerNodeEvent("clickPaging",i,e):!1!==s._triggerNodeEvent("click",i,e)&&s._callHook("nodeClick",i);case"dblclick":return i.targetType=r.type,!1!==s._triggerNodeEvent("dblclick",i,e)&&s._callHook("nodeDblclick",i)}}finally{s.phase=a}})},getActiveNode:function(){return this.tree.activeNode},getNodeByKey:function(e){return this.tree.getNodeByKey(e)},getRootNode:function(){return this.tree.rootNode},getTree:function(){return this.tree}}),y=e.ui.fancytree,e.extend(e.ui.fancytree,{version:"2.30.2",buildType: "production",debugLevel: 3,_nextId:1,_nextNodeKey:1,_extensions:{},_FancytreeClass:f,_FancytreeNodeClass:h,jquerySupports:{positionMyOfs:function(t,n,i,r){var o,s,a,l=e.map(e.trim(t).split("."),function(e){return parseInt(e,10)}),d=e.map(Array.prototype.slice.call(arguments,1),function(e){return parseInt(e,10)});for(o=0;oa;return!0}(e.ui.version,1,9)},assert:function(e,n){return t(e,n)},createTree:function(t,n){return e(t).fancytree(n).fancytree("getTree")},debounce:function(e,t,n,i){var r;return 3===arguments.length&&"boolean"!=typeof n&&(i=n,n=!1),function(){var o=arguments;i=i||this,n&&!r&&t.apply(i,o),clearTimeout(r),r=setTimeout(function(){n||t.apply(i,o),r=null},e)}},debug:function(t){e.ui.fancytree.debugLevel>=4&&n("log",arguments)},error:function(t){e.ui.fancytree.debugLevel>=1&&n("error",arguments)},escapeHtml:function(e){return(""+e).replace(m,function(e){return C[e]})},fixPositionOptions:function(t){if((t.offset||(""+t.my+t.at).indexOf("%")>=0)&&e.error("expected new position syntax (but '%' is not supported)"),!e.ui.fancytree.jquerySupports.positionMyOfs){var n=/(\w+)([+-]?\d+)?\s+(\w+)([+-]?\d+)?/.exec(t.my),i=/(\w+)([+-]?\d+)?\s+(\w+)([+-]?\d+)?/.exec(t.at),r=(n[2]?+n[2]:0)+(i[2]?+i[2]:0),o=(n[4]?+n[4]:0)+(i[4]?+i[4]:0);t=e.extend({},t,{my:n[1]+" "+n[3],at:i[1]+" "+i[3]}),(r||o)&&(t.offset=r+" "+o)}return t},getEventTarget:function(t){var n,i=t&&t.target?t.target.className:"",r={node:this.getNode(t.target),type:void 0};return/\bfancytree-title\b/.test(i)?r.type="title":/\bfancytree-expander\b/.test(i)?r.type=!1===r.node.hasChildren()?"prefix":"expander":/\bfancytree-checkbox\b/.test(i)?r.type="checkbox":/\bfancytree(-custom)?-icon\b/.test(i)?r.type="icon":/\bfancytree-node\b/.test(i)?r.type="title":t&&t.target&&((n=e(t.target)).is("ul[role=group]")?((r.node&&r.node.tree||y).debug("Ignoring click on outer UL."),r.node=null):n.closest(".fancytree-title").length?r.type="title":n.closest(".fancytree-checkbox").length?r.type="checkbox":n.closest(".fancytree-expander").length&&(r.type="expander")),r},getEventTargetType:function(e){return this.getEventTarget(e).type},getNode:function(t){if(t instanceof h)return t;for(t instanceof e?t=t[0]:void 0!==t.originalEvent&&(t=t.target);t;){if(t.ftnode)return t.ftnode;t=t.parentNode}return null},getTree:function(t){var n;return t instanceof f?t:(void 0===t&&(t=0),"number"==typeof t?t=e(".fancytree-container").eq(t):"string"==typeof t?t=e(t).eq(0):t instanceof e?t=t.eq(0):void 0!==t.originalEvent&&(t=e(t.target)),t=t.closest(":ui-fancytree"),(n=t.data("ui-fancytree")||t.data("fancytree"))?n.tree:null)},evalOption:function(t,n,i,r,o){var s,a,l=n.tree,d=r[t],c=i[t];return e.isFunction(d)?(s={node:n,tree:l,widget:l.widget,options:l.widget.options,typeInfo:l.types[n.type]||{}},null==(a=d.call(l,{type:t},s))&&(a=c)):a=null!=c?c:d,null==a&&(a=o),a},setSpanIcon:function(t,n,i){var r=e(t);"string"==typeof i?r.attr("class",n+" "+i):(i.text?r.text(""+i.text):i.html&&(t.innerHTML=i.html),r.attr("class",n+" "+(i.addClass||"")))},eventToString:function(e){var t=e.which,n=e.type,i=[];return e.altKey&&i.push("alt"),e.ctrlKey&&i.push("ctrl"),e.metaKey&&i.push("meta"),e.shiftKey&&i.push("shift"),"click"===n||"dblclick"===n?i.push(N[e.button]+n):_[t]||i.push(k[t]||String.fromCharCode(t).toLowerCase()),i.join("+")},info:function(t){e.ui.fancytree.debugLevel>=3&&n("info",arguments)},keyEventToString:function(e){return this.warn("keyEventToString() is deprecated: use eventToString()"),this.eventToString(e)},overrideMethod:function(t,n,i,r){var o,s=t[n]||e.noop;t[n]=function(){var e=r||this;try{return o=e._super,e._super=s,i.apply(e,arguments)}finally{e._super=o}}},parseHtml:function(t){var n,i,r,o,s,a,l,c,u=[];return t.find(">li").each(function(){var h,f,p=e(this),g=p.find(">span:first",this),y=g.length?null:p.find(">a:first"),v={tooltip:null,data:{}};for(g.length?v.title=g.html():y&&y.length?(v.title=y.html(),v.data.href=y.attr("href"),v.data.target=y.attr("target"),v.tooltip=y.attr("title")):(v.title=p.html(),(s=v.title.search(/
- =0&&(v.title=v.title.substring(0,s))),v.title=e.trim(v.title),o=0,a=S.length;oul:first")).length?v.children=e.ui.fancytree.parseHtml(t):v.children=v.lazy?void 0:null,u.push(v)}),u},registerExtension:function(n){t(null!=n.name,"extensions must have a `name` property."),t(null!=n.version,"extensions must have a `version` property."),e.ui.fancytree._extensions[n.name]=n},unescapeHtml:function(e){var t=document.createElement("div");return t.innerHTML=e,0===t.childNodes.length?"":t.childNodes[0].nodeValue},warn:function(t){e.ui.fancytree.debugLevel>=2&&n("warn",arguments)}}),e.ui.fancytree}e.ui.fancytree.warn("Fancytree: ignored duplicate include")}}),function(e){"function"==typeof define&&define.amd?define(["jquery","./jquery.fancytree"],e):"object"==typeof module&&module.exports?(require("./jquery.fancytree"),module.exports=e(require("jquery"))):e(jQuery)}(function(e){"use strict";return e.ui.fancytree._FancytreeClass.prototype.countSelected=function(e){this.options;return this.getSelectedNodes(e).length},e.ui.fancytree._FancytreeNodeClass.prototype.updateCounters=function(){var t=e("span.fancytree-childcounter",this.span),n=this.tree.options.childcounter,i=this.countChildren(n.deep);this.data.childCounter=i,!i&&n.hideZeros||this.isExpanded()&&n.hideExpanded?t.remove():(t.length||(t=e("").appendTo(e("span.fancytree-icon,span.fancytree-custom-icon",this.span))),t.text(i)),!n.deep||this.isTopLevel()||this.isRoot()||this.parent.updateCounters()},e.ui.fancytree.prototype.widgetMethod1=function(e){this.tree;return e},e.ui.fancytree.registerExtension({name:"childcounter",version:"2.30.2",options:{deep:!0,hideZeros:!0,hideExpanded:!1},foo:42,_appendCounter:function(e){},treeInit:function(e){e.options,e.options.childcounter;this._superApply(arguments),this.$container.addClass("fancytree-ext-childcounter")},treeDestroy:function(e){this._superApply(arguments)},nodeRenderTitle:function(t,n){var i=t.node,r=t.options.childcounter,o=null==i.data.childCounter?i.countChildren(r.deep):+i.data.childCounter;this._super(t,n),!o&&r.hideZeros||i.isExpanded()&&r.hideExpanded||e("span.fancytree-icon,span.fancytree-custom-icon",i.span).append(e("").text(o))},nodeSetExpanded:function(e,t,n){var i=e.tree;e.node;return this._superApply(arguments).always(function(){i.nodeRenderTitle(e)})}}),e.ui.fancytree}),function(e){"function"==typeof define&&define.amd?define(["jquery","./jquery.fancytree"],e):"object"==typeof module&&module.exports?(require("./jquery.fancytree"),module.exports=e(require("jquery"))):e(jQuery)}(function(e){"use strict";function t(t,n){t||(n=n?": "+n:"",e.error("Assertion failed"+n))}return e.ui.fancytree._FancytreeNodeClass.prototype.getCloneList=function(t){var n,i=this.tree,r=i.refMap[this.refKey]||null,o=i.keyMap;return r&&(n=this.key,t?r=e.map(r,function(e){return o[e]}):(r=e.map(r,function(e){return e===n?null:o[e]})).length<1&&(r=null)),r},e.ui.fancytree._FancytreeNodeClass.prototype.isClone=function(){var e=this.refKey||null,t=e&&this.tree.refMap[e]||null;return!!(t&&t.length>1)},e.ui.fancytree._FancytreeNodeClass.prototype.reRegister=function(t,n){t=null==t?null:""+t,n=null==n?null:""+n;var i=this.tree,r=this.key,o=this.refKey,s=i.keyMap,a=i.refMap,l=a[o]||null,d=!1;return null!=t&&t!==this.key&&(s[t]&&e.error("[ext-clones] reRegister("+t+"): already exists: "+this),delete s[r],s[t]=this,l&&(a[o]=e.map(l,function(e){return e===r?t:e})),this.key=t,d=!0),null!=n&&n!==this.refKey&&(l&&(1===l.length?delete a[o]:a[o]=e.map(l,function(e){return e===r?null:e})),a[n]?a[n].append(t):a[n]=[this.key],this.refKey=n,d=!0),d},e.ui.fancytree._FancytreeNodeClass.prototype.setRefKey=function(e){return this.reRegister(null,e)},e.ui.fancytree._FancytreeClass.prototype.getNodesByRef=function(t,n){var i=this.keyMap,r=this.refMap[t]||null;return r&&(r=n?e.map(r,function(e){var t=i[e];return t.isDescendantOf(n)?t:null}):e.map(r,function(e){return i[e]})).length<1&&(r=null),r},e.ui.fancytree._FancytreeClass.prototype.changeRefKey=function(e,t){var n,i=this.keyMap,r=this.refMap[e]||null;if(r){for(n=0;n
",{id:s}).hide().insertAfter(this.$container),!1!==t&&this.activeNode&&a.append(C("",{type:"radio",name:o,value:this.activeNode.key,checked:!0})),n.filter?this.visit(function(e){var t=n.filter(e);if("skip"===t)return t;!1!==t&&d(e)}):!1!==e&&(i=this.getSelectedNodes(l),C.each(i,function(e,t){d(t)}))},getActiveNode:function(){return this.activeNode},getFirstChild:function(){return this.rootNode.getFirstChild()},getFocusNode:function(){return this.focusNode},getOption:function(e){return this.widget.option(e)},getNodeByKey:function(t,e){var n,i;return!e&&(n=document.getElementById(this.options.idPrefix+t))?n.ftnode?n.ftnode:null:(e=e||this.rootNode,i=null,e.visit(function(e){if(e.key===t)return i=e,!1},!0),i)},getRootNode:function(){return this.rootNode},getSelectedNodes:function(e){return this.rootNode.getSelectedNodes(e)},hasFocus:function(){return!!this._hasFocus},info:function(e){3<=this.options.debugLevel&&(Array.prototype.unshift.call(arguments,this.toString()),d("info",arguments))},loadKeyPath:function(e,t){var i,n,r,o=this,s=new C.Deferred,a=this.getRootNode(),l=this.options.keyPathSeparator,d=[],c=C.extend({},t);for("function"==typeof t?i=t:t&&t.callback&&(i=t.callback),c.callback=function(e,t,n){i&&i.call(e,t,n),s.notifyWith(e,[{node:t,status:n}])},null==c.matchKey&&(c.matchKey=function(e,t){return e.key===t}),C.isArray(e)||(e=[e]),n=0;nd)s.rejectWith(this,[c]);else{if(o.options.postProcess){try{r=a._triggerNodeEvent("postProcess",o,o.originalEvent,{response:e,error:null,dataType:this.dataType})}catch(e){r={error:e,message:""+e,details:"postProcess failed"}}if(r.error)return i=C.isPlainObject(r.error)?r.error:{message:r.error},i=a._makeHookContext(l,null,i),void s.rejectWith(this,[i]);(C.isArray(r)||C.isPlainObject(r)&&C.isArray(r.children))&&(e=r)}else e&&e.hasOwnProperty("d")&&o.options.enableAspx&&(e="string"==typeof e.d?C.parseJSON(e.d):e.d);s.resolveWith(this,[e])}}).fail(function(e,t,n){var i=a._makeHookContext(l,null,{error:e,args:Array.prototype.slice.call(arguments),message:n,details:e.status+": "+n});s.rejectWith(this,[i])})),C.isFunction(s.then)&&C.isFunction(s.catch)&&(e=s,s=new C.Deferred,e.then(function(e){s.resolve(e)},function(e){s.reject(e)})),C.isFunction(s.promise)?(a.nodeSetStatus(o,"loading"),s.done(function(e){a.nodeSetStatus(o,"ok"),l._requestId=null}).fail(function(e){var t;e!==c?(e.node&&e.error&&e.message?t=e:"[object Object]"===(t=a._makeHookContext(l,null,{error:e,args:Array.prototype.slice.call(arguments),message:e?e.message||e.toString():""})).message&&(t.message=""),l.warn("Load children failed ("+t.message+")",t),!1!==a._triggerNodeEvent("loadError",t,null)&&a.nodeSetStatus(o,"error",t.message,t.details)):l.warn("Ignored response for obsolete load request #"+d+" (expected #"+l._requestId+")")})):o.options.postProcess&&(i=a._triggerNodeEvent("postProcess",o,o.originalEvent,{response:s,error:null,dataType:typeof s}),(C.isArray(i)||C.isPlainObject(i)&&C.isArray(i.children))&&(s=i)),C.when(s).done(function(e){var n,t;C.isPlainObject(e)&&(w(l.isRootNode(),"source may only be an object for root nodes (expecting an array of child objects otherwise)"),w(C.isArray(e.children),"if an object is passed as source, it must contain a 'children' array (all other properties are added to 'tree.data')"),e=(n=e).children,delete n.children,C.each(u,function(e,t){void 0!==n[t]&&(a[t]=n[t],delete n[t])}),C.extend(a.data,n)),w(C.isArray(e),"expected array of children"),l._setChildren(e),a.options.nodata&&0===e.length&&(C.isFunction(a.options.nodata)?t=a.options.nodata.call(a,{type:"nodata"},o):!0===a.options.nodata&&l.isRootNode()?t=a.options.strings.nodata:"string"==typeof a.options.nodata&&l.isRootNode()&&(t=a.options.nodata),t&&l.setStatus("nodata",t)),a._triggerNodeEvent("loadChildren",l)})},nodeLoadKeyPath:function(e,t){},nodeRemoveChild:function(e,t){var n,i=e.node,r=C.extend({},e,{node:t}),o=i.children;if(1===o.length)return w(t===o[0],"invalid single child"),this.nodeRemoveChildren(e);this.activeNode&&(t===this.activeNode||this.activeNode.isDescendantOf(t))&&this.activeNode.setActive(!1),this.focusNode&&(t===this.focusNode||this.focusNode.isDescendantOf(t))&&(this.focusNode=null),this.nodeRemoveMarkup(r),this.nodeRemoveChildren(r),w(0<=(n=C.inArray(t,o)),"invalid child"),i.triggerModifyChild("remove",t),t.visit(function(e){e.parent=null},!0),this._callHook("treeRegisterNode",this,!1,t),o.splice(n,1)},nodeRemoveChildMarkup:function(e){var t=e.node;t.ul&&(t.isRootNode()?C(t.ul).empty():(C(t.ul).remove(),t.ul=null),t.visit(function(e){e.li=e.ul=null}))},nodeRemoveChildren:function(e){var t=e.tree,n=e.node;n.children&&(this.activeNode&&this.activeNode.isDescendantOf(n)&&this.activeNode.setActive(!1),this.focusNode&&this.focusNode.isDescendantOf(n)&&(this.focusNode=null),this.nodeRemoveChildMarkup(e),n.triggerModifyChild("remove",null),n.visit(function(e){e.parent=null,t._callHook("treeRegisterNode",t,!1,e)}),n.lazy?n.children=[]:n.children=null,n.isRootNode()||(n.expanded=!1),this.nodeRenderStatus(e))},nodeRemoveMarkup:function(e){var t=e.node;t.li&&(C(t.li).remove(),t.li=null),this.nodeRemoveChildMarkup(e)},nodeRender:function(e,t,n,i,r){var o,s,a,l,d,c,u,h=e.node,f=e.tree,p=e.options,g=p.aria,y=!1,v=h.parent,m=!v,b=h.children,x=null;if(!1!==f._enableUpdate&&(m||v.ul)){if(w(m||v.ul,"parent UL must exist"),m||(h.li&&(t||h.li.parentNode!==h.parent.ul)&&(h.li.parentNode===h.parent.ul?x=h.li.nextSibling:this.debug("Unlinking "+h+" (must be child of "+h.parent+")"),this.nodeRemoveMarkup(e)),h.li?this.nodeRenderStatus(e):(y=!0,h.li=document.createElement("li"),(h.li.ftnode=h).key&&p.generateIds&&(h.li.id=p.idPrefix+h.key),h.span=document.createElement("span"),h.span.className="fancytree-node",g&&!h.tr&&C(h.li).attr("role","treeitem"),h.li.appendChild(h.span),this.nodeRenderTitle(e),p.createNode&&p.createNode.call(f,{type:"createNode"},e)),p.renderNode&&p.renderNode.call(f,{type:"renderNode"},e)),b){if(m||h.expanded||!0===n){for(h.ul||(h.ul=document.createElement("ul"),(!0!==i||r)&&h.expanded||(h.ul.style.display="none"),g&&C(h.ul).attr("role","group"),h.li?h.li.appendChild(h.ul):h.tree.$div.append(h.ul)),l=0,d=b.length;l")):p.push(""),(n=g.evalOption("checkbox",d,d,u,!1))&&!d.isStatusNode()&&(s=h?" role='checkbox'":"",i="fancytree-checkbox",("radio"===n||d.parent&&d.parent.radiogroup)&&(i+=" fancytree-radio"),p.push("")),void 0!==d.data.iconClass&&(d.icon?C.error("'iconClass' node option is deprecated since v2.14.0: use 'icon' only instead"):(d.warn("'iconClass' node option is deprecated since v2.14.0: use 'icon' instead"),d.icon=d.data.iconClass)),!1!==(r=g.evalOption("icon",d,d,u,!0))&&(s=h?" role='presentation'":"",l=(l=g.evalOption("iconTooltip",d,d,u,null))?" title='"+T(l)+"'":"","string"==typeof r?y.test(r)?(r="/"===r.charAt(0)?r:(u.imagePath||"")+r,p.push("
")):p.push(""):r.text?p.push(""+g.escapeHtml(r.text)+""):r.html?p.push(""+r.html+""):p.push("")),o="",u.renderTitle&&(o=u.renderTitle.call(c,{type:"renderTitle"},e)||""),o||(!0===(a=g.evalOption("tooltip",d,d,u,null))&&(a=d.title),o=""+(u.escapeTitles?g.escapeHtml(d.title):d.title)+""),p.push(o),d.span.innerHTML=p.join(""),this.nodeRenderStatus(e),u.enhanceTitle&&(e.$title=C(">span.fancytree-title",d.span),o=u.enhanceTitle.call(c,{type:"enhanceTitle"},e)||""))},nodeRenderStatus:function(e){var t,n=e.node,i=e.tree,r=e.options,o=n.hasChildren(),s=n.isLastSibling(),a=r.aria,l=r._classNames,d=[],c=n[i.statusClassPropName];c&&!1!==i._enableUpdate&&(a&&(t=C(n.tr||n.li)),d.push(l.node),i.activeNode===n&&d.push(l.active),i.focusNode===n&&d.push(l.focused),n.expanded&&d.push(l.expanded),a&&(!1===o?t.removeAttr("aria-expanded"):t.attr("aria-expanded",Boolean(n.expanded))),n.folder&&d.push(l.folder),!1!==o&&d.push(l.hasChildren),s&&d.push(l.lastsib),n.lazy&&null==n.children&&d.push(l.lazy),n.partload&&d.push(l.partload),n.partsel&&d.push(l.partsel),g.evalOption("unselectable",n,n,r,!1)&&d.push(l.unselectable),n._isLoading&&d.push(l.loading),n._error&&d.push(l.error),n.statusNodeType&&d.push(l.statusNodePrefix+n.statusNodeType),n.selected?(d.push(l.selected),a&&t.attr("aria-selected",!0)):a&&t.attr("aria-selected",!1),n.extraClasses&&d.push(n.extraClasses),!1===o?d.push(l.combinedExpanderPrefix+"n"+(s?"l":"")):d.push(l.combinedExpanderPrefix+(n.expanded?"e":"c")+(n.lazy&&null==n.children?"d":"")+(s?"l":"")),d.push(l.combinedIconPrefix+(n.expanded?"e":"c")+(n.folder?"f":"")),c.className=d.join(" "),n.li&&C(n.li).toggleClass(l.lastsib,s))},nodeSetActive:function(e,t,n){n=n||{};var i,r=e.node,o=e.tree,s=e.options,a=!0===n.noEvents,l=!0===n.noFocus,d=!1!==n.scrollIntoView;return r===o.activeNode===(t=!1!==t)?N(r):t&&!a&&!1===this._triggerNodeEvent("beforeActivate",r,e.originalEvent)?S(r,["rejected"]):(t?(o.activeNode&&(w(o.activeNode!==r,"node was active (inconsistency)"),i=C.extend({},e,{node:o.activeNode}),o.nodeSetActive(i,!1),w(null===o.activeNode,"deactivate was out of sync?")),s.activeVisible&&r.makeVisible({scrollIntoView:d}),o.activeNode=r,o.nodeRenderStatus(e),l||o.nodeSetFocus(e),a||o._triggerNodeEvent("activate",r,e.originalEvent)):(w(o.activeNode===r,"node was not active (inconsistency)"),o.activeNode=null,this.nodeRenderStatus(e),a||e.tree._triggerNodeEvent("deactivate",r,e.originalEvent)),N(r))},nodeSetExpanded:function(i,r,e){e=e||{};var t,n,o,s,a,l,d=i.node,c=i.tree,u=i.options,h=!0===e.noAnimation,f=!0===e.noEvents;if(r=!1!==r,d.expanded&&r||!d.expanded&&!r)return N(d);if(r&&!d.lazy&&!d.hasChildren())return N(d);if(!r&&d.getLevel()ul.fancytree-container").empty(),t.rootNode.children=null,t._callHook("treeStructureChanged",e,"clear")},treeCreate:function(e){},treeDestroy:function(e){this.$div.find(">ul.fancytree-container").remove(),this.$source&&this.$source.removeClass("fancytree-helper-hidden")},treeInit:function(e){var n=e.tree,i=n.options;n.$container.attr("tabindex",i.tabindex),C.each(u,function(e,t){void 0!==i[t]&&(n.info("Move option "+t+" to tree"),n[t]=i[t],delete i[t])}),i.checkboxAutoHide&&n.$container.addClass("fancytree-checkbox-auto-hide"),i.rtl?n.$container.attr("DIR","RTL").addClass("fancytree-rtl"):n.$container.removeAttr("DIR").removeClass("fancytree-rtl"),i.aria&&(n.$container.attr("role","tree"),1!==i.selectMode&&n.$container.attr("aria-multiselectable",!0)),this.treeLoad(e)},treeLoad:function(e,t){var n,i,r,o=e.tree,s=e.widget.element,a=C.extend({},e,{node:this.rootNode});if(o.rootNode.children&&this.treeClear(e),t=t||this.options.source)"string"==typeof t&&C.error("Not implemented");else switch(i=s.data("type")||"html"){case"html":(r=s.find(">ul").first()).addClass("ui-fancytree-source fancytree-helper-hidden"),t=C.ui.fancytree.parseHtml(r),this.data=C.extend(this.data,A(r));break;case"json":t=C.parseJSON(s.text()),s.contents().filter(function(){return 3===this.nodeType}).remove(),C.isPlainObject(t)&&(w(C.isArray(t.children),"if an object is passed as source, it must contain a 'children' array (all other properties are added to 'tree.data')"),t=(n=t).children,delete n.children,C.each(u,function(e,t){void 0!==n[t]&&(o[t]=n[t],delete n[t])}),C.extend(o.data,n));break;default:C.error("Invalid data-type: "+i)}return this.nodeLoadChildren(a,t).done(function(){o._callHook("treeStructureChanged",e,"loadChildren"),o.render(),3===e.options.selectMode&&o.rootNode.fixSelection3FromEndNodes(),o.activeNode&&o.options.activeVisible&&o.activeNode.makeVisible(),o._triggerTreeEvent("init",null,{status:!0})}).fail(function(){o.render(),o._triggerTreeEvent("init",null,{status:!1})})},treeRegisterNode:function(e,t,n){e.tree._callHook("treeStructureChanged",e,t?"addNode":"removeNode")},treeSetFocus:function(e,t,n){var i;(t=!1!==t)!==this.hasFocus()&&(!(this._hasFocus=t)&&this.focusNode?this.focusNode.setFocus(!1):!t||n&&n.calledByNode||C(this.$container).focus(),this.$container.toggleClass("fancytree-treefocus",t),this._triggerTreeEvent(t?"focusTree":"blurTree"),t&&!this.activeNode&&(i=this._lastMousedownNode||this.getFirstChild())&&i.setFocus())},treeSetOption:function(e,t,n){var i=e.tree,r=!0,o=!1,s=!1;switch(t){case"aria":case"checkbox":case"icon":case"minExpandLevel":case"tabindex":s=o=!0;break;case"checkboxAutoHide":i.$container.toggleClass("fancytree-checkbox-auto-hide",!!n);break;case"escapeTitles":case"tooltip":s=!0;break;case"rtl":!1===n?i.$container.removeAttr("DIR").removeClass("fancytree-rtl"):i.$container.attr("DIR","RTL").addClass("fancytree-rtl"),s=!0;break;case"source":r=!1,i._callHook("treeLoad",i,n),s=!0}i.debug("set option "+t+"="+n+" <"+typeof n+">"),r&&(this.widget._super?this.widget._super.call(this.widget,t,n):C.Widget.prototype._setOption.call(this.widget,t,n)),o&&i._callHook("treeCreate",i),s&&i.render(!0,!1)},treeStructureChanged:function(e,t){}}),C.widget("ui.fancytree",{options:{activeVisible:!0,ajax:{type:"GET",cache:!1,dataType:"json"},aria:!0,autoActivate:!0,autoCollapse:!1,autoScroll:!1,checkbox:!1,clickFolderMode:4,debugLevel:null,disabled:!1,enableAspx:!0,escapeTitles:!1,extensions:[],toggleEffect:{effect:"slideToggle",duration:200},generateIds:!1,icon:!0,idPrefix:"ft_",focusOnSelect:!1,keyboard:!0,keyPathSeparator:"/",minExpandLevel:1,nodata:!0,quicksearch:!1,rtl:!1,scrollOfs:{top:0,bottom:0},scrollParent:null,selectMode:2,strings:{loading:"Loading...",loadError:"Load error!",moreData:"More...",noData:"No data."},tabindex:"0",titlesTabbable:!1,tooltip:!1,treeId:null,_classNames:{node:"fancytree-node",folder:"fancytree-folder",animating:"fancytree-animating",combinedExpanderPrefix:"fancytree-exp-",combinedIconPrefix:"fancytree-ico-",hasChildren:"fancytree-has-children",active:"fancytree-active",selected:"fancytree-selected",expanded:"fancytree-expanded",lazy:"fancytree-lazy",focused:"fancytree-focused",partload:"fancytree-partload",partsel:"fancytree-partsel",radio:"fancytree-radio",unselectable:"fancytree-unselectable",lastsib:"fancytree-lastsib",loading:"fancytree-loading",error:"fancytree-error",statusNodePrefix:"fancytree-statusnode-"},lazyLoad:null,postProcess:null},_create:function(){this.tree=new D(this),this.$source=this.source||"json"===this.element.data("type")?this.element:this.element.find(">ul").first();var e,t,n,i=this.options,r=i.extensions;this.tree;for(n=0;n element.");else{if(n){if(o._getExpiringValue("focusin"))return void o.debug("Ignored double focusin.");o._setExpiringValue("focusin",!0,50),t||(t=o._getExpiringValue("mouseDownNode"))&&o.debug("Reconstruct mouse target for focusin from recent event.")}t?o._callHook("nodeSetFocus",o._makeHookContext(t,e),n):o.tbody&&C(e.target).parents("table.fancytree-container > thead").length?o.debug("Ignore focus event outside table body.",e):o._callHook("treeSetFocus",o,n)}}).on("selectstart"+e,"span.fancytree-title",function(e){e.preventDefault()}).on("keydown"+e,function(e){if(a.disabled||!1===a.keyboard)return!0;var t,n=o.focusNode,i=o._makeHookContext(n||o,e),r=o.phase;try{return o.phase="userEvent","preventNav"===(t=n?o._triggerNodeEvent("keydown",n,e):o._triggerTreeEvent("keydown",e))?t=!0:!1!==t&&(t=o._callHook("nodeKeydown",i)),t}finally{o.phase=r}}).on("mousedown"+e,function(e){var t=g.getEventTarget(e);o._lastMousedownNode=t?t.node:null,o._setExpiringValue("mouseDownNode",o._lastMousedownNode)}).on("click"+e+" dblclick"+e,function(e){if(a.disabled)return!0;var t,n=g.getEventTarget(e),i=n.node,r=s.tree,o=r.phase;if(!i)return!0;t=r._makeHookContext(i,e);try{switch(r.phase="userEvent",e.type){case"click":return t.targetType=n.type,i.isPagingNode()?!0===r._triggerNodeEvent("clickPaging",t,e):!1!==r._triggerNodeEvent("click",t,e)&&r._callHook("nodeClick",t);case"dblclick":return t.targetType=n.type,!1!==r._triggerNodeEvent("dblclick",t,e)&&r._callHook("nodeDblclick",t)}}finally{r.phase=o}})},getActiveNode:function(){return this.tree.activeNode},getNodeByKey:function(e){return this.tree.getNodeByKey(e)},getRootNode:function(){return this.tree.rootNode},getTree:function(){return this.tree}}),g=C.ui.fancytree,C.extend(C.ui.fancytree,{version:"2.31.0",buildType: "production",debugLevel: 3,_nextId:1,_nextNodeKey:1,_extensions:{},_FancytreeClass:D,_FancytreeNodeClass:F,jquerySupports:{positionMyOfs:function(e,t,n,i){var r,o,s,a=C.map(C.trim(e).split("."),function(e){return parseInt(e,10)}),l=C.map(Array.prototype.slice.call(arguments,1),function(e){return parseInt(e,10)});for(r=0;rli"),g=[];return e.each(function(){var e,t,n=C(this),i=n.find(">span",this).first(),r=i.length?null:n.find(">a").first(),o={tooltip:null,data:{}};for(i.length?o.title=i.html():r&&r.length?(o.title=r.html(),o.data.href=r.attr("href"),o.data.target=r.attr("target"),o.tooltip=r.attr("title")):(o.title=n.html(),0<=(u=o.title.search(/
- ul").first()).length?o.children=C.ui.fancytree.parseHtml(s):o.children=o.lazy?void 0:null,g.push(o)}),g},registerExtension:function(e){w(null!=e.name,"extensions must have a `name` property."),w(null!=e.version,"extensions must have a `version` property."),C.ui.fancytree._extensions[e.name]=e},unescapeHtml:function(e){var t=document.createElement("div");return t.innerHTML=e,0===t.childNodes.length?"":t.childNodes[0].nodeValue},warn:function(e){2<=C.ui.fancytree.debugLevel&&d("warn",arguments)}}),C.ui.fancytree}function w(e,t){e||(t=t?": "+t:"",C.error("Fancytree assertion failed"+t))}function d(e,t){var n,i,r=window.console?window.console[e]:null;if(r)try{r.apply(window.console,t)}catch(e){for(i="",n=0;n
- ",{id:"ft-id-"+this._id,class:"ui-fancytree fancytree-container fancytree-plain"}).appendTo(this.$div),this.$container=t,this.rootNode.ul=t[0],null==this.options.debugLevel&&(this.options.debugLevel=g.debugLevel)}C.ui.fancytree.warn("Fancytree: ignored duplicate include")},"function"==typeof define&&define.amd?define(["jquery","./jquery.fancytree.ui-deps"],t):"object"==typeof module&&module.exports?(require("./jquery.fancytree.ui-deps"),module.exports=t(require("jquery"))):t(jQuery),n=function(o){"use strict";return o.ui.fancytree._FancytreeClass.prototype.countSelected=function(e){this.options;return this.getSelectedNodes(e).length},o.ui.fancytree._FancytreeNodeClass.prototype.updateCounters=function(){var e=this,t=o("span.fancytree-childcounter",e.span),n=e.tree.options.childcounter,i=e.countChildren(n.deep);!(e.data.childCounter=i)&&n.hideZeros||e.isExpanded()&&n.hideExpanded?t.remove():(t.length||(t=o("").appendTo(o("span.fancytree-icon,span.fancytree-custom-icon",e.span))),t.text(i)),!n.deep||e.isTopLevel()||e.isRoot()||e.parent.updateCounters()},o.ui.fancytree.prototype.widgetMethod1=function(e){this.tree;return e},o.ui.fancytree.registerExtension({name:"childcounter",version:"2.31.0",options:{deep:!0,hideZeros:!0,hideExpanded:!1},foo:42,_appendCounter:function(e){},treeInit:function(e){e.options,e.options.childcounter;this._superApply(arguments),this.$container.addClass("fancytree-ext-childcounter")},treeDestroy:function(e){this._superApply(arguments)},nodeRenderTitle:function(e,t){var n=e.node,i=e.options.childcounter,r=null==n.data.childCounter?n.countChildren(i.deep):+n.data.childCounter;this._super(e,t),!r&&i.hideZeros||n.isExpanded()&&i.hideExpanded||o("span.fancytree-icon,span.fancytree-custom-icon",n.span).append(o("").text(r))},nodeSetExpanded:function(e,t,n){var i=e.tree;e.node;return this._superApply(arguments).always(function(){i.nodeRenderTitle(e)})}}),o.ui.fancytree},"function"==typeof define&&define.amd?define(["jquery","./jquery.fancytree"],n):"object"==typeof module&&module.exports?(require("./jquery.fancytree"),module.exports=n(require("jquery"))):n(jQuery),i=function(h){"use strict";function f(e,t){e||(t=t?": "+t:"",h.error("Assertion failed"+t))}function r(e,t,n){for(var i,r,o=3&e.length,s=e.length-o,a=n,l=3432918353,d=461845907,c=0;c