504 lines
14 KiB
JavaScript
504 lines
14 KiB
JavaScript
/*!
|
|
* jquery.fancytree.persist.js
|
|
*
|
|
* Persist tree status in cookiesRemove or highlight tree nodes, based on a filter.
|
|
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
|
|
*
|
|
* @depends: js-cookie or jquery-cookie
|
|
*
|
|
* Copyright (c) 2008-2021, Martin Wendt (https://wwWendt.de)
|
|
*
|
|
* Released under the MIT license
|
|
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
|
*
|
|
* @version 2.38.2
|
|
* @date 2022-06-30T18:24:06Z
|
|
*/
|
|
|
|
(function (factory) {
|
|
if (typeof define === "function" && define.amd) {
|
|
// AMD. Register as an anonymous module.
|
|
define(["jquery", "./jquery.fancytree"], factory);
|
|
} else if (typeof module === "object" && module.exports) {
|
|
// Node/CommonJS
|
|
require("./jquery.fancytree");
|
|
module.exports = factory(require("jquery"));
|
|
} else {
|
|
// Browser globals
|
|
factory(jQuery);
|
|
}
|
|
})(function ($) {
|
|
"use strict";
|
|
/* global Cookies:false */
|
|
|
|
/*******************************************************************************
|
|
* Private functions and variables
|
|
*/
|
|
var cookieStore = null,
|
|
localStorageStore = null,
|
|
sessionStorageStore = null,
|
|
_assert = $.ui.fancytree.assert,
|
|
ACTIVE = "active",
|
|
EXPANDED = "expanded",
|
|
FOCUS = "focus",
|
|
SELECTED = "selected";
|
|
|
|
// Accessing window.xxxStorage may raise security exceptions (see #1022)
|
|
try {
|
|
_assert(window.localStorage && window.localStorage.getItem);
|
|
localStorageStore = {
|
|
get: function (key) {
|
|
return window.localStorage.getItem(key);
|
|
},
|
|
set: function (key, value) {
|
|
window.localStorage.setItem(key, value);
|
|
},
|
|
remove: function (key) {
|
|
window.localStorage.removeItem(key);
|
|
},
|
|
};
|
|
} catch (e) {
|
|
$.ui.fancytree.warn("Could not access window.localStorage", e);
|
|
}
|
|
|
|
try {
|
|
_assert(window.sessionStorage && window.sessionStorage.getItem);
|
|
sessionStorageStore = {
|
|
get: function (key) {
|
|
return window.sessionStorage.getItem(key);
|
|
},
|
|
set: function (key, value) {
|
|
window.sessionStorage.setItem(key, value);
|
|
},
|
|
remove: function (key) {
|
|
window.sessionStorage.removeItem(key);
|
|
},
|
|
};
|
|
} catch (e) {
|
|
$.ui.fancytree.warn("Could not access window.sessionStorage", e);
|
|
}
|
|
|
|
if (typeof Cookies === "function") {
|
|
// Assume https://github.com/js-cookie/js-cookie
|
|
cookieStore = {
|
|
get: Cookies.get,
|
|
set: function (key, value) {
|
|
Cookies.set(key, value, this.options.persist.cookie);
|
|
},
|
|
remove: Cookies.remove,
|
|
};
|
|
} else if ($ && typeof $.cookie === "function") {
|
|
// Fall back to https://github.com/carhartl/jquery-cookie
|
|
cookieStore = {
|
|
get: $.cookie,
|
|
set: function (key, value) {
|
|
$.cookie(key, value, this.options.persist.cookie);
|
|
},
|
|
remove: $.removeCookie,
|
|
};
|
|
}
|
|
|
|
/* Recursively load lazy nodes
|
|
* @param {string} mode 'load', 'expand', false
|
|
*/
|
|
function _loadLazyNodes(tree, local, keyList, mode, dfd) {
|
|
var i,
|
|
key,
|
|
l,
|
|
node,
|
|
foundOne = false,
|
|
expandOpts = tree.options.persist.expandOpts,
|
|
deferredList = [],
|
|
missingKeyList = [];
|
|
|
|
keyList = keyList || [];
|
|
dfd = dfd || $.Deferred();
|
|
|
|
for (i = 0, l = keyList.length; i < l; i++) {
|
|
key = keyList[i];
|
|
node = tree.getNodeByKey(key);
|
|
if (node) {
|
|
if (mode && node.isUndefined()) {
|
|
foundOne = true;
|
|
tree.debug(
|
|
"_loadLazyNodes: " + node + " is lazy: loading..."
|
|
);
|
|
if (mode === "expand") {
|
|
deferredList.push(node.setExpanded(true, expandOpts));
|
|
} else {
|
|
deferredList.push(node.load());
|
|
}
|
|
} else {
|
|
tree.debug("_loadLazyNodes: " + node + " already loaded.");
|
|
node.setExpanded(true, expandOpts);
|
|
}
|
|
} else {
|
|
missingKeyList.push(key);
|
|
tree.debug("_loadLazyNodes: " + node + " was not yet found.");
|
|
}
|
|
}
|
|
|
|
$.when.apply($, deferredList).always(function () {
|
|
// All lazy-expands have finished
|
|
if (foundOne && missingKeyList.length > 0) {
|
|
// If we read new nodes from server, try to resolve yet-missing keys
|
|
_loadLazyNodes(tree, local, missingKeyList, mode, dfd);
|
|
} else {
|
|
if (missingKeyList.length) {
|
|
tree.warn(
|
|
"_loadLazyNodes: could not load those keys: ",
|
|
missingKeyList
|
|
);
|
|
for (i = 0, l = missingKeyList.length; i < l; i++) {
|
|
key = keyList[i];
|
|
local._appendKey(EXPANDED, keyList[i], false);
|
|
}
|
|
}
|
|
dfd.resolve();
|
|
}
|
|
});
|
|
return dfd;
|
|
}
|
|
|
|
/**
|
|
* [ext-persist] Remove persistence data of the given type(s).
|
|
* Called like
|
|
* $.ui.fancytree.getTree("#tree").clearCookies("active expanded focus selected");
|
|
*
|
|
* @alias Fancytree#clearPersistData
|
|
* @requires jquery.fancytree.persist.js
|
|
*/
|
|
$.ui.fancytree._FancytreeClass.prototype.clearPersistData = function (
|
|
types
|
|
) {
|
|
var local = this.ext.persist,
|
|
prefix = local.cookiePrefix;
|
|
|
|
types = types || "active expanded focus selected";
|
|
if (types.indexOf(ACTIVE) >= 0) {
|
|
local._data(prefix + ACTIVE, null);
|
|
}
|
|
if (types.indexOf(EXPANDED) >= 0) {
|
|
local._data(prefix + EXPANDED, null);
|
|
}
|
|
if (types.indexOf(FOCUS) >= 0) {
|
|
local._data(prefix + FOCUS, null);
|
|
}
|
|
if (types.indexOf(SELECTED) >= 0) {
|
|
local._data(prefix + SELECTED, null);
|
|
}
|
|
};
|
|
|
|
$.ui.fancytree._FancytreeClass.prototype.clearCookies = function (types) {
|
|
this.warn(
|
|
"'tree.clearCookies()' is deprecated since v2.27.0: use 'clearPersistData()' instead."
|
|
);
|
|
return this.clearPersistData(types);
|
|
};
|
|
|
|
/**
|
|
* [ext-persist] Return persistence information from cookies
|
|
*
|
|
* Called like
|
|
* $.ui.fancytree.getTree("#tree").getPersistData();
|
|
*
|
|
* @alias Fancytree#getPersistData
|
|
* @requires jquery.fancytree.persist.js
|
|
*/
|
|
$.ui.fancytree._FancytreeClass.prototype.getPersistData = function () {
|
|
var local = this.ext.persist,
|
|
prefix = local.cookiePrefix,
|
|
delim = local.cookieDelimiter,
|
|
res = {};
|
|
|
|
res[ACTIVE] = local._data(prefix + ACTIVE);
|
|
res[EXPANDED] = (local._data(prefix + EXPANDED) || "").split(delim);
|
|
res[SELECTED] = (local._data(prefix + SELECTED) || "").split(delim);
|
|
res[FOCUS] = local._data(prefix + FOCUS);
|
|
return res;
|
|
};
|
|
|
|
/******************************************************************************
|
|
* Extension code
|
|
*/
|
|
$.ui.fancytree.registerExtension({
|
|
name: "persist",
|
|
version: "2.38.2",
|
|
// Default options for this extension.
|
|
options: {
|
|
cookieDelimiter: "~",
|
|
cookiePrefix: undefined, // 'fancytree-<treeId>-' by default
|
|
cookie: {
|
|
raw: false,
|
|
expires: "",
|
|
path: "",
|
|
domain: "",
|
|
secure: false,
|
|
},
|
|
expandLazy: false, // true: recursively expand and load lazy nodes
|
|
expandOpts: undefined, // optional `opts` argument passed to setExpanded()
|
|
fireActivate: true, // false: suppress `activate` event after active node was restored
|
|
overrideSource: true, // true: cookie takes precedence over `source` data attributes.
|
|
store: "auto", // 'cookie': force cookie, 'local': force localStore, 'session': force sessionStore
|
|
types: "active expanded focus selected",
|
|
},
|
|
|
|
/* Generic read/write string data to cookie, sessionStorage or localStorage. */
|
|
_data: function (key, value) {
|
|
var store = this._local.store;
|
|
|
|
if (value === undefined) {
|
|
return store.get.call(this, key);
|
|
} else if (value === null) {
|
|
store.remove.call(this, key);
|
|
} else {
|
|
store.set.call(this, key, value);
|
|
}
|
|
},
|
|
|
|
/* Append `key` to a cookie. */
|
|
_appendKey: function (type, key, flag) {
|
|
key = "" + key; // #90
|
|
var local = this._local,
|
|
instOpts = this.options.persist,
|
|
delim = instOpts.cookieDelimiter,
|
|
cookieName = local.cookiePrefix + type,
|
|
data = local._data(cookieName),
|
|
keyList = data ? data.split(delim) : [],
|
|
idx = $.inArray(key, keyList);
|
|
// Remove, even if we add a key, so the key is always the last entry
|
|
if (idx >= 0) {
|
|
keyList.splice(idx, 1);
|
|
}
|
|
// Append key to cookie
|
|
if (flag) {
|
|
keyList.push(key);
|
|
}
|
|
local._data(cookieName, keyList.join(delim));
|
|
},
|
|
|
|
treeInit: function (ctx) {
|
|
var tree = ctx.tree,
|
|
opts = ctx.options,
|
|
local = this._local,
|
|
instOpts = this.options.persist;
|
|
|
|
// // For 'auto' or 'cookie' mode, the cookie plugin must be available
|
|
// _assert((instOpts.store !== "auto" && instOpts.store !== "cookie") || cookieStore,
|
|
// "Missing required plugin for 'persist' extension: js.cookie.js or jquery.cookie.js");
|
|
|
|
local.cookiePrefix =
|
|
instOpts.cookiePrefix || "fancytree-" + tree._id + "-";
|
|
local.storeActive = instOpts.types.indexOf(ACTIVE) >= 0;
|
|
local.storeExpanded = instOpts.types.indexOf(EXPANDED) >= 0;
|
|
local.storeSelected = instOpts.types.indexOf(SELECTED) >= 0;
|
|
local.storeFocus = instOpts.types.indexOf(FOCUS) >= 0;
|
|
local.store = null;
|
|
|
|
if (instOpts.store === "auto") {
|
|
instOpts.store = localStorageStore ? "local" : "cookie";
|
|
}
|
|
if ($.isPlainObject(instOpts.store)) {
|
|
local.store = instOpts.store;
|
|
} else if (instOpts.store === "cookie") {
|
|
local.store = cookieStore;
|
|
} else if (instOpts.store === "local") {
|
|
local.store =
|
|
instOpts.store === "local"
|
|
? localStorageStore
|
|
: sessionStorageStore;
|
|
} else if (instOpts.store === "session") {
|
|
local.store =
|
|
instOpts.store === "local"
|
|
? localStorageStore
|
|
: sessionStorageStore;
|
|
}
|
|
_assert(local.store, "Need a valid store.");
|
|
|
|
// Bind init-handler to apply cookie state
|
|
tree.$div.on("fancytreeinit", function (event) {
|
|
if (
|
|
tree._triggerTreeEvent("beforeRestore", null, {}) === false
|
|
) {
|
|
return;
|
|
}
|
|
|
|
var cookie,
|
|
dfd,
|
|
i,
|
|
keyList,
|
|
node,
|
|
prevFocus = local._data(local.cookiePrefix + FOCUS), // record this before node.setActive() overrides it;
|
|
noEvents = instOpts.fireActivate === false;
|
|
|
|
// tree.debug("document.cookie:", document.cookie);
|
|
|
|
cookie = local._data(local.cookiePrefix + EXPANDED);
|
|
keyList = cookie && cookie.split(instOpts.cookieDelimiter);
|
|
|
|
if (local.storeExpanded) {
|
|
// Recursively load nested lazy nodes if expandLazy is 'expand' or 'load'
|
|
// Also remove expand-cookies for unmatched nodes
|
|
dfd = _loadLazyNodes(
|
|
tree,
|
|
local,
|
|
keyList,
|
|
instOpts.expandLazy ? "expand" : false,
|
|
null
|
|
);
|
|
} else {
|
|
// nothing to do
|
|
dfd = new $.Deferred().resolve();
|
|
}
|
|
|
|
dfd.done(function () {
|
|
if (local.storeSelected) {
|
|
cookie = local._data(local.cookiePrefix + SELECTED);
|
|
if (cookie) {
|
|
keyList = cookie.split(instOpts.cookieDelimiter);
|
|
for (i = 0; i < keyList.length; i++) {
|
|
node = tree.getNodeByKey(keyList[i]);
|
|
if (node) {
|
|
if (
|
|
node.selected === undefined ||
|
|
(instOpts.overrideSource &&
|
|
node.selected === false)
|
|
) {
|
|
// node.setSelected();
|
|
node.selected = true;
|
|
node.renderStatus();
|
|
}
|
|
} else {
|
|
// node is no longer member of the tree: remove from cookie also
|
|
local._appendKey(
|
|
SELECTED,
|
|
keyList[i],
|
|
false
|
|
);
|
|
}
|
|
}
|
|
}
|
|
// In selectMode 3 we have to fix the child nodes, since we
|
|
// only stored the selected *top* nodes
|
|
if (tree.options.selectMode === 3) {
|
|
tree.visit(function (n) {
|
|
if (n.selected) {
|
|
n.fixSelection3AfterClick();
|
|
return "skip";
|
|
}
|
|
});
|
|
}
|
|
}
|
|
if (local.storeActive) {
|
|
cookie = local._data(local.cookiePrefix + ACTIVE);
|
|
if (
|
|
cookie &&
|
|
(opts.persist.overrideSource || !tree.activeNode)
|
|
) {
|
|
node = tree.getNodeByKey(cookie);
|
|
if (node) {
|
|
node.debug("persist: set active", cookie);
|
|
// We only want to set the focus if the container
|
|
// had the keyboard focus before
|
|
node.setActive(true, {
|
|
noFocus: true,
|
|
noEvents: noEvents,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
if (local.storeFocus && prevFocus) {
|
|
node = tree.getNodeByKey(prevFocus);
|
|
if (node) {
|
|
// node.debug("persist: set focus", cookie);
|
|
if (tree.options.titlesTabbable) {
|
|
$(node.span).find(".fancytree-title").focus();
|
|
} else {
|
|
$(tree.$container).focus();
|
|
}
|
|
// node.setFocus();
|
|
}
|
|
}
|
|
tree._triggerTreeEvent("restore", null, {});
|
|
});
|
|
});
|
|
// Init the tree
|
|
return this._superApply(arguments);
|
|
},
|
|
nodeSetActive: function (ctx, flag, callOpts) {
|
|
var res,
|
|
local = this._local;
|
|
|
|
flag = flag !== false;
|
|
res = this._superApply(arguments);
|
|
|
|
if (local.storeActive) {
|
|
local._data(
|
|
local.cookiePrefix + ACTIVE,
|
|
this.activeNode ? this.activeNode.key : null
|
|
);
|
|
}
|
|
return res;
|
|
},
|
|
nodeSetExpanded: function (ctx, flag, callOpts) {
|
|
var res,
|
|
node = ctx.node,
|
|
local = this._local;
|
|
|
|
flag = flag !== false;
|
|
res = this._superApply(arguments);
|
|
|
|
if (local.storeExpanded) {
|
|
local._appendKey(EXPANDED, node.key, flag);
|
|
}
|
|
return res;
|
|
},
|
|
nodeSetFocus: function (ctx, flag) {
|
|
var res,
|
|
local = this._local;
|
|
|
|
flag = flag !== false;
|
|
res = this._superApply(arguments);
|
|
|
|
if (local.storeFocus) {
|
|
local._data(
|
|
local.cookiePrefix + FOCUS,
|
|
this.focusNode ? this.focusNode.key : null
|
|
);
|
|
}
|
|
return res;
|
|
},
|
|
nodeSetSelected: function (ctx, flag, callOpts) {
|
|
var res,
|
|
selNodes,
|
|
tree = ctx.tree,
|
|
node = ctx.node,
|
|
local = this._local;
|
|
|
|
flag = flag !== false;
|
|
res = this._superApply(arguments);
|
|
|
|
if (local.storeSelected) {
|
|
if (tree.options.selectMode === 3) {
|
|
// In selectMode 3 we only store the the selected *top* nodes.
|
|
// De-selecting a node may also de-select some parents, so we
|
|
// calculate the current status again
|
|
selNodes = $.map(tree.getSelectedNodes(true), function (n) {
|
|
return n.key;
|
|
});
|
|
selNodes = selNodes.join(
|
|
ctx.options.persist.cookieDelimiter
|
|
);
|
|
local._data(local.cookiePrefix + SELECTED, selNodes);
|
|
} else {
|
|
// beforeSelect can prevent the change - flag doesn't reflect the node.selected state
|
|
local._appendKey(SELECTED, node.key, node.selected);
|
|
}
|
|
}
|
|
return res;
|
|
},
|
|
});
|
|
// Value returned by `require('jquery.fancytree..')`
|
|
return $.ui.fancytree;
|
|
}); // End of closure
|