wunderbaum 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/wunderbaum.css +1 -1
- package/dist/wunderbaum.d.ts +125 -60
- package/dist/wunderbaum.esm.js +480 -231
- package/dist/wunderbaum.esm.min.js +26 -26
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +480 -231
- package/dist/wunderbaum.umd.min.js +34 -34
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/src/common.ts +187 -26
- package/src/types.ts +43 -19
- package/src/util.ts +10 -5
- package/src/wb_ext_edit.ts +3 -2
- package/src/wb_ext_keynav.ts +12 -13
- package/src/wb_ext_logger.ts +1 -1
- package/src/wb_node.ts +173 -58
- package/src/wunderbaum.ts +63 -63
package/dist/wunderbaum.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Wunderbaum - util
|
|
3
3
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
4
|
-
* v0.0.
|
|
4
|
+
* v0.0.8, Fri, 23 Sep 2022 20:47:29 GMT (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
6
|
/** @module util */
|
|
7
7
|
/** Readable names for `MouseEvent.button` */
|
|
@@ -281,11 +281,17 @@ function setValueToElem(elem, value) {
|
|
|
281
281
|
case "week":
|
|
282
282
|
case "datetime":
|
|
283
283
|
case "datetime-local":
|
|
284
|
-
input.valueAsDate = value;
|
|
284
|
+
input.valueAsDate = new Date(value);
|
|
285
|
+
// input.valueAsDate = value; // breaks in Edge?
|
|
285
286
|
break;
|
|
286
287
|
case "number":
|
|
287
288
|
case "range":
|
|
288
|
-
|
|
289
|
+
if (value == null) {
|
|
290
|
+
input.value = value;
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
input.valueAsNumber = value;
|
|
294
|
+
}
|
|
289
295
|
break;
|
|
290
296
|
case "radio":
|
|
291
297
|
error("Not implemented");
|
|
@@ -302,7 +308,7 @@ function setValueToElem(elem, value) {
|
|
|
302
308
|
break;
|
|
303
309
|
case "text":
|
|
304
310
|
default:
|
|
305
|
-
input.value = value
|
|
311
|
+
input.value = value !== null && value !== void 0 ? value : "";
|
|
306
312
|
}
|
|
307
313
|
}
|
|
308
314
|
else if (tag === "SELECT") {
|
|
@@ -460,7 +466,7 @@ function onEvent(rootTarget, eventNames, selectorOrHandler, handlerOrNone) {
|
|
|
460
466
|
});
|
|
461
467
|
});
|
|
462
468
|
}
|
|
463
|
-
/** Return a wrapped handler method, that provides `this._super`.
|
|
469
|
+
/** Return a wrapped handler method, that provides `this._super` and `this._superApply`.
|
|
464
470
|
*
|
|
465
471
|
* ```ts
|
|
466
472
|
// Implement `opts.createNode` event to add the 'draggable' attribute
|
|
@@ -701,7 +707,7 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
701
707
|
/*!
|
|
702
708
|
* Wunderbaum - types
|
|
703
709
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
704
|
-
* v0.0.
|
|
710
|
+
* v0.0.8, Fri, 23 Sep 2022 20:47:29 GMT (https://github.com/mar10/wunderbaum)
|
|
705
711
|
*/
|
|
706
712
|
/** Possible values for `setModified()`. */
|
|
707
713
|
var ChangeType;
|
|
@@ -753,7 +759,7 @@ var NavigationOptions;
|
|
|
753
759
|
/*!
|
|
754
760
|
* Wunderbaum - wb_extension_base
|
|
755
761
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
756
|
-
* v0.0.
|
|
762
|
+
* v0.0.8, Fri, 23 Sep 2022 20:47:29 GMT (https://github.com/mar10/wunderbaum)
|
|
757
763
|
*/
|
|
758
764
|
class WunderbaumExtension {
|
|
759
765
|
constructor(tree, id, defaults) {
|
|
@@ -1044,7 +1050,7 @@ function debounce(func, wait = 0, options = {}) {
|
|
|
1044
1050
|
/*!
|
|
1045
1051
|
* Wunderbaum - ext-filter
|
|
1046
1052
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1047
|
-
* v0.0.
|
|
1053
|
+
* v0.0.8, Fri, 23 Sep 2022 20:47:29 GMT (https://github.com/mar10/wunderbaum)
|
|
1048
1054
|
*/
|
|
1049
1055
|
const START_MARKER = "\uFFF7";
|
|
1050
1056
|
const END_MARKER = "\uFFF8";
|
|
@@ -1346,107 +1352,10 @@ function _markFuzzyMatchedChars(text, matches, escapeTitles = true) {
|
|
|
1346
1352
|
return textPoses.join("");
|
|
1347
1353
|
}
|
|
1348
1354
|
|
|
1349
|
-
/*!
|
|
1350
|
-
* Wunderbaum - common
|
|
1351
|
-
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1352
|
-
* v0.0.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
1353
|
-
*/
|
|
1354
|
-
const DEFAULT_DEBUGLEVEL = 4; // Replaced by rollup script
|
|
1355
|
-
const ROW_HEIGHT = 22;
|
|
1356
|
-
// export const HEADER_HEIGHT = ROW_HEIGHT;
|
|
1357
|
-
const ICON_WIDTH = 20;
|
|
1358
|
-
const ROW_EXTRA_PAD = 7; // 2x $col-padding-x + 3px rounding errors
|
|
1359
|
-
const RENDER_MAX_PREFETCH = 5;
|
|
1360
|
-
const TEST_IMG = new RegExp(/\.|\//); // strings are considered image urls if they contain '.' or '/'
|
|
1361
|
-
// export const RECURSIVE_REQUEST_ERROR = "$recursive_request";
|
|
1362
|
-
// export const INVALID_REQUEST_TARGET_ERROR = "$request_target_invalid";
|
|
1363
|
-
let iconMap = {
|
|
1364
|
-
error: "bi bi-exclamation-triangle",
|
|
1365
|
-
// loading: "bi bi-hourglass-split wb-busy",
|
|
1366
|
-
loading: "bi bi-chevron-right wb-busy",
|
|
1367
|
-
// loading: "bi bi-arrow-repeat wb-spin",
|
|
1368
|
-
// loading: '<div class="spinner-border spinner-border-sm" role="status"> <span class="visually-hidden">Loading...</span> </div>',
|
|
1369
|
-
// noData: "bi bi-search",
|
|
1370
|
-
noData: "bi bi-question-circle",
|
|
1371
|
-
expanderExpanded: "bi bi-chevron-down",
|
|
1372
|
-
// expanderExpanded: "bi bi-dash-square",
|
|
1373
|
-
expanderCollapsed: "bi bi-chevron-right",
|
|
1374
|
-
// expanderCollapsed: "bi bi-plus-square",
|
|
1375
|
-
expanderLazy: "bi bi-chevron-right wb-helper-lazy-expander",
|
|
1376
|
-
// expanderLazy: "bi bi-chevron-bar-right",
|
|
1377
|
-
checkChecked: "bi bi-check-square",
|
|
1378
|
-
checkUnchecked: "bi bi-square",
|
|
1379
|
-
checkUnknown: "bi dash-square-dotted",
|
|
1380
|
-
radioChecked: "bi bi-circle-fill",
|
|
1381
|
-
radioUnchecked: "bi bi-circle",
|
|
1382
|
-
radioUnknown: "bi bi-circle-dotted",
|
|
1383
|
-
folder: "bi bi-folder2",
|
|
1384
|
-
folderOpen: "bi bi-folder2-open",
|
|
1385
|
-
doc: "bi bi-file-earmark",
|
|
1386
|
-
};
|
|
1387
|
-
/** Dict keys that are evaluated by source loader (others are added to `tree.data` instead). */
|
|
1388
|
-
const RESERVED_TREE_SOURCE_KEYS = new Set([
|
|
1389
|
-
"children",
|
|
1390
|
-
"columns",
|
|
1391
|
-
"format",
|
|
1392
|
-
"keyMap",
|
|
1393
|
-
"positional",
|
|
1394
|
-
"typeList",
|
|
1395
|
-
"types",
|
|
1396
|
-
"version", // reserved for future use
|
|
1397
|
-
]);
|
|
1398
|
-
/** Key codes that trigger grid navigation, even when inside an input element. */
|
|
1399
|
-
const INPUT_BREAKOUT_KEYS = new Set([
|
|
1400
|
-
// "ArrowDown",
|
|
1401
|
-
// "ArrowUp",
|
|
1402
|
-
"Enter",
|
|
1403
|
-
"Escape",
|
|
1404
|
-
]);
|
|
1405
|
-
/** Map `KeyEvent.key` to navigation action. */
|
|
1406
|
-
const KEY_TO_ACTION_DICT = {
|
|
1407
|
-
" ": "toggleSelect",
|
|
1408
|
-
"+": "expand",
|
|
1409
|
-
Add: "expand",
|
|
1410
|
-
ArrowDown: "down",
|
|
1411
|
-
ArrowLeft: "left",
|
|
1412
|
-
ArrowRight: "right",
|
|
1413
|
-
ArrowUp: "up",
|
|
1414
|
-
Backspace: "parent",
|
|
1415
|
-
"/": "collapseAll",
|
|
1416
|
-
Divide: "collapseAll",
|
|
1417
|
-
End: "lastCol",
|
|
1418
|
-
Home: "firstCol",
|
|
1419
|
-
"Control+End": "last",
|
|
1420
|
-
"Control+Home": "first",
|
|
1421
|
-
"Meta+ArrowDown": "last",
|
|
1422
|
-
"Meta+ArrowUp": "first",
|
|
1423
|
-
"*": "expandAll",
|
|
1424
|
-
Multiply: "expandAll",
|
|
1425
|
-
PageDown: "pageDown",
|
|
1426
|
-
PageUp: "pageUp",
|
|
1427
|
-
"-": "collapse",
|
|
1428
|
-
Subtract: "collapse",
|
|
1429
|
-
};
|
|
1430
|
-
/** Return a callback that returns true if the node title contains a substring (case-insensitive). */
|
|
1431
|
-
function makeNodeTitleMatcher(s) {
|
|
1432
|
-
s = escapeRegex(s.toLowerCase());
|
|
1433
|
-
return function (node) {
|
|
1434
|
-
return node.title.toLowerCase().indexOf(s) >= 0;
|
|
1435
|
-
};
|
|
1436
|
-
}
|
|
1437
|
-
/** Return a callback that returns true if the node title starts with a string (case-insensitive). */
|
|
1438
|
-
function makeNodeTitleStartMatcher(s) {
|
|
1439
|
-
s = escapeRegex(s);
|
|
1440
|
-
const reMatch = new RegExp("^" + s, "i");
|
|
1441
|
-
return function (node) {
|
|
1442
|
-
return reMatch.test(node.title);
|
|
1443
|
-
};
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
1355
|
/*!
|
|
1447
1356
|
* Wunderbaum - ext-keynav
|
|
1448
1357
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1449
|
-
* v0.0.
|
|
1358
|
+
* v0.0.8, Fri, 23 Sep 2022 20:47:29 GMT (https://github.com/mar10/wunderbaum)
|
|
1450
1359
|
*/
|
|
1451
1360
|
const QUICKSEARCH_DELAY = 500;
|
|
1452
1361
|
class KeynavExtension extends WunderbaumExtension {
|
|
@@ -1480,7 +1389,7 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1480
1389
|
const event = data.event, tree = this.tree, opts = data.options, activate = !event.ctrlKey || opts.autoActivate, curInput = this._getEmbeddedInputElem(event.target), navModeOption = opts.navigationModeOption;
|
|
1481
1390
|
// isCellEditMode = tree.navMode === NavigationMode.cellEdit;
|
|
1482
1391
|
let focusNode, eventName = eventToString(event), node = data.node, handled = true;
|
|
1483
|
-
tree.log(`onKeyEvent: ${eventName}, curInput`, curInput);
|
|
1392
|
+
// tree.log(`onKeyEvent: ${eventName}, curInput`, curInput);
|
|
1484
1393
|
if (!tree.isEnabled()) {
|
|
1485
1394
|
// tree.logDebug(`onKeyEvent ignored for disabled tree: ${eventName}`);
|
|
1486
1395
|
return false;
|
|
@@ -1603,10 +1512,11 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1603
1512
|
if (eventName === "Escape") {
|
|
1604
1513
|
// Discard changes
|
|
1605
1514
|
node.render();
|
|
1515
|
+
// } else if (!INPUT_BREAKOUT_KEYS.has(eventName)) {
|
|
1606
1516
|
}
|
|
1607
|
-
else if (
|
|
1517
|
+
else if (eventName !== "Enter") {
|
|
1608
1518
|
// Let current `<input>` handle it
|
|
1609
|
-
node.logDebug(`Ignored ${eventName} inside input`);
|
|
1519
|
+
node.logDebug(`Ignored ${eventName} inside focused input`);
|
|
1610
1520
|
return;
|
|
1611
1521
|
}
|
|
1612
1522
|
// const curInputType = curInput.type || curInput.tagName;
|
|
@@ -1677,24 +1587,24 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1677
1587
|
if (isColspan && node.isExpanded()) {
|
|
1678
1588
|
node.setExpanded(false);
|
|
1679
1589
|
}
|
|
1680
|
-
else if (tree.activeColIdx > 0) {
|
|
1590
|
+
else if (!isColspan && tree.activeColIdx > 0) {
|
|
1681
1591
|
tree.setColumn(tree.activeColIdx - 1);
|
|
1682
|
-
handled = true;
|
|
1683
1592
|
}
|
|
1684
1593
|
else if (navModeOption !== NavigationOptions.cell) {
|
|
1685
1594
|
tree.setCellNav(false); // row-nav mode
|
|
1686
|
-
handled = true;
|
|
1687
1595
|
}
|
|
1596
|
+
handled = true;
|
|
1688
1597
|
break;
|
|
1689
1598
|
case "ArrowRight":
|
|
1690
1599
|
tree.setFocus(); // Blur prev. input if any
|
|
1691
1600
|
if (isColspan && !node.isExpanded()) {
|
|
1692
1601
|
node.setExpanded();
|
|
1693
1602
|
}
|
|
1694
|
-
else if (
|
|
1603
|
+
else if (!isColspan &&
|
|
1604
|
+
tree.activeColIdx < tree.columns.length - 1) {
|
|
1695
1605
|
tree.setColumn(tree.activeColIdx + 1);
|
|
1696
|
-
handled = true;
|
|
1697
1606
|
}
|
|
1607
|
+
handled = true;
|
|
1698
1608
|
break;
|
|
1699
1609
|
case "ArrowDown":
|
|
1700
1610
|
case "ArrowUp":
|
|
@@ -1727,7 +1637,7 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1727
1637
|
/*!
|
|
1728
1638
|
* Wunderbaum - ext-logger
|
|
1729
1639
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1730
|
-
* v0.0.
|
|
1640
|
+
* v0.0.8, Fri, 23 Sep 2022 20:47:29 GMT (https://github.com/mar10/wunderbaum)
|
|
1731
1641
|
*/
|
|
1732
1642
|
class LoggerExtension extends WunderbaumExtension {
|
|
1733
1643
|
constructor(tree) {
|
|
@@ -1750,7 +1660,7 @@ class LoggerExtension extends WunderbaumExtension {
|
|
|
1750
1660
|
if (ignoreEvents.has(name)) {
|
|
1751
1661
|
return tree._superApply(arguments);
|
|
1752
1662
|
}
|
|
1753
|
-
|
|
1663
|
+
const start = Date.now();
|
|
1754
1664
|
const res = tree._superApply(arguments);
|
|
1755
1665
|
console.debug(`${prefix}: callEvent('${name}') took ${Date.now() - start} ms.`, arguments[1]);
|
|
1756
1666
|
return res;
|
|
@@ -1764,10 +1674,250 @@ class LoggerExtension extends WunderbaumExtension {
|
|
|
1764
1674
|
}
|
|
1765
1675
|
}
|
|
1766
1676
|
|
|
1677
|
+
/*!
|
|
1678
|
+
* Wunderbaum - common
|
|
1679
|
+
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1680
|
+
* v0.0.8, Fri, 23 Sep 2022 20:47:29 GMT (https://github.com/mar10/wunderbaum)
|
|
1681
|
+
*/
|
|
1682
|
+
const DEFAULT_DEBUGLEVEL = 4; // Replaced by rollup script
|
|
1683
|
+
/**
|
|
1684
|
+
* Fixed height of a row in pixel. Must match the SCSS variable `$row-outer-height`.
|
|
1685
|
+
*/
|
|
1686
|
+
const ROW_HEIGHT = 22;
|
|
1687
|
+
/**
|
|
1688
|
+
* Fixed width of node icons in pixel. Must match the SCSS variable `$icon-outer-width`.
|
|
1689
|
+
*/
|
|
1690
|
+
const ICON_WIDTH = 20;
|
|
1691
|
+
/**
|
|
1692
|
+
* Adjust the width of the title span, so overflow ellipsis work.
|
|
1693
|
+
* (2 x `$col-padding-x` + 3px rounding errors).
|
|
1694
|
+
*/
|
|
1695
|
+
const TITLE_SPAN_PAD_Y = 7;
|
|
1696
|
+
/** Render row markup for N nodes above and below the visible viewport. */
|
|
1697
|
+
const RENDER_MAX_PREFETCH = 5;
|
|
1698
|
+
/** Regular expression to detect if a string describes an image URL (in contrast
|
|
1699
|
+
* to a class name). Strings are considered image urls if they contain '.' or '/'.
|
|
1700
|
+
*/
|
|
1701
|
+
const TEST_IMG = new RegExp(/\.|\//);
|
|
1702
|
+
// export const RECURSIVE_REQUEST_ERROR = "$recursive_request";
|
|
1703
|
+
// export const INVALID_REQUEST_TARGET_ERROR = "$request_target_invalid";
|
|
1704
|
+
/**
|
|
1705
|
+
* Default node icons.
|
|
1706
|
+
* Requires bootstrap icons https://icons.getbootstrap.com
|
|
1707
|
+
*/
|
|
1708
|
+
const iconMap = {
|
|
1709
|
+
error: "bi bi-exclamation-triangle",
|
|
1710
|
+
// loading: "bi bi-hourglass-split wb-busy",
|
|
1711
|
+
loading: "bi bi-chevron-right wb-busy",
|
|
1712
|
+
// loading: "bi bi-arrow-repeat wb-spin",
|
|
1713
|
+
// loading: '<div class="spinner-border spinner-border-sm" role="status"> <span class="visually-hidden">Loading...</span> </div>',
|
|
1714
|
+
// noData: "bi bi-search",
|
|
1715
|
+
noData: "bi bi-question-circle",
|
|
1716
|
+
expanderExpanded: "bi bi-chevron-down",
|
|
1717
|
+
// expanderExpanded: "bi bi-dash-square",
|
|
1718
|
+
expanderCollapsed: "bi bi-chevron-right",
|
|
1719
|
+
// expanderCollapsed: "bi bi-plus-square",
|
|
1720
|
+
expanderLazy: "bi bi-chevron-right wb-helper-lazy-expander",
|
|
1721
|
+
// expanderLazy: "bi bi-chevron-bar-right",
|
|
1722
|
+
checkChecked: "bi bi-check-square",
|
|
1723
|
+
checkUnchecked: "bi bi-square",
|
|
1724
|
+
checkUnknown: "bi dash-square-dotted",
|
|
1725
|
+
radioChecked: "bi bi-circle-fill",
|
|
1726
|
+
radioUnchecked: "bi bi-circle",
|
|
1727
|
+
radioUnknown: "bi bi-circle-dotted",
|
|
1728
|
+
folder: "bi bi-folder2",
|
|
1729
|
+
folderOpen: "bi bi-folder2-open",
|
|
1730
|
+
folderLazy: "bi bi-folder-symlink",
|
|
1731
|
+
doc: "bi bi-file-earmark",
|
|
1732
|
+
};
|
|
1733
|
+
/** Dict keys that are evaluated by source loader (others are added to `tree.data` instead). */
|
|
1734
|
+
const RESERVED_TREE_SOURCE_KEYS = new Set([
|
|
1735
|
+
"_format",
|
|
1736
|
+
"_keyMap",
|
|
1737
|
+
"_positional",
|
|
1738
|
+
"_typeList",
|
|
1739
|
+
"_version",
|
|
1740
|
+
"children",
|
|
1741
|
+
"columns",
|
|
1742
|
+
"types",
|
|
1743
|
+
]);
|
|
1744
|
+
// /** Key codes that trigger grid navigation, even when inside an input element. */
|
|
1745
|
+
// export const INPUT_BREAKOUT_KEYS: Set<string> = new Set([
|
|
1746
|
+
// // "ArrowDown",
|
|
1747
|
+
// // "ArrowUp",
|
|
1748
|
+
// "Enter",
|
|
1749
|
+
// "Escape",
|
|
1750
|
+
// ]);
|
|
1751
|
+
/** Map `KeyEvent.key` to navigation action. */
|
|
1752
|
+
const KEY_TO_ACTION_DICT = {
|
|
1753
|
+
" ": "toggleSelect",
|
|
1754
|
+
"+": "expand",
|
|
1755
|
+
Add: "expand",
|
|
1756
|
+
ArrowDown: "down",
|
|
1757
|
+
ArrowLeft: "left",
|
|
1758
|
+
ArrowRight: "right",
|
|
1759
|
+
ArrowUp: "up",
|
|
1760
|
+
Backspace: "parent",
|
|
1761
|
+
"/": "collapseAll",
|
|
1762
|
+
Divide: "collapseAll",
|
|
1763
|
+
End: "lastCol",
|
|
1764
|
+
Home: "firstCol",
|
|
1765
|
+
"Control+End": "last",
|
|
1766
|
+
"Control+Home": "first",
|
|
1767
|
+
"Meta+ArrowDown": "last",
|
|
1768
|
+
"Meta+ArrowUp": "first",
|
|
1769
|
+
"*": "expandAll",
|
|
1770
|
+
Multiply: "expandAll",
|
|
1771
|
+
PageDown: "pageDown",
|
|
1772
|
+
PageUp: "pageUp",
|
|
1773
|
+
"-": "collapse",
|
|
1774
|
+
Subtract: "collapse",
|
|
1775
|
+
};
|
|
1776
|
+
/** Return a callback that returns true if the node title matches the string
|
|
1777
|
+
* or regular expression.
|
|
1778
|
+
* @see {@link WunderbaumNode.findAll}
|
|
1779
|
+
*/
|
|
1780
|
+
function makeNodeTitleMatcher(match) {
|
|
1781
|
+
if (match instanceof RegExp) {
|
|
1782
|
+
return function (node) {
|
|
1783
|
+
return match.test(node.title);
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
assert(typeof match === "string");
|
|
1787
|
+
// s = escapeRegex(s.toLowerCase());
|
|
1788
|
+
return function (node) {
|
|
1789
|
+
return node.title === match;
|
|
1790
|
+
// console.log("match " + node, node.title.toLowerCase().indexOf(match))
|
|
1791
|
+
// return node.title.toLowerCase().indexOf(match) >= 0;
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
/** Return a callback that returns true if the node title starts with a string (case-insensitive). */
|
|
1795
|
+
function makeNodeTitleStartMatcher(s) {
|
|
1796
|
+
s = escapeRegex(s);
|
|
1797
|
+
const reMatch = new RegExp("^" + s, "i");
|
|
1798
|
+
return function (node) {
|
|
1799
|
+
return reMatch.test(node.title);
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
function unflattenSource(source) {
|
|
1803
|
+
var _a, _b, _c;
|
|
1804
|
+
const { _format, _keyMap, _positional, children } = source;
|
|
1805
|
+
if (_format !== "flat") {
|
|
1806
|
+
throw new Error(`Expected source._format: "flat", but got ${_format}`);
|
|
1807
|
+
}
|
|
1808
|
+
if (_positional && _positional.includes("children")) {
|
|
1809
|
+
throw new Error(`source._positional must not include "children": ${_positional}`);
|
|
1810
|
+
}
|
|
1811
|
+
// Inverse keyMap:
|
|
1812
|
+
let longToShort = {};
|
|
1813
|
+
if (_keyMap) {
|
|
1814
|
+
for (const [key, value] of Object.entries(_keyMap)) {
|
|
1815
|
+
longToShort[value] = key;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
const positionalShort = _positional.map((e) => longToShort[e]);
|
|
1819
|
+
const newChildren = [];
|
|
1820
|
+
const keyToNodeMap = {};
|
|
1821
|
+
const indexToNodeMap = {};
|
|
1822
|
+
const keyAttrName = (_a = longToShort["key"]) !== null && _a !== void 0 ? _a : "key";
|
|
1823
|
+
const childrenAttrName = (_b = longToShort["children"]) !== null && _b !== void 0 ? _b : "children";
|
|
1824
|
+
for (const [index, node] of children.entries()) {
|
|
1825
|
+
// Node entry format:
|
|
1826
|
+
// [PARENT_ID, [POSITIONAL_ARGS]]
|
|
1827
|
+
// or
|
|
1828
|
+
// [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
1829
|
+
const [parentId, args, kwargs = {}] = node;
|
|
1830
|
+
// Free up some memory as we go
|
|
1831
|
+
node[1] = null;
|
|
1832
|
+
if (node[2] != null) {
|
|
1833
|
+
node[2] = null;
|
|
1834
|
+
}
|
|
1835
|
+
// console.log("flatten", parentId, args, kwargs)
|
|
1836
|
+
// We keep `kwargs` as our new node definition. Then we add all positional
|
|
1837
|
+
// values to this object:
|
|
1838
|
+
args.forEach((val, positionalIdx) => {
|
|
1839
|
+
kwargs[positionalShort[positionalIdx]] = val;
|
|
1840
|
+
});
|
|
1841
|
+
// Find the parent node. `null` means 'toplevel'. PARENT_ID may be the numeric
|
|
1842
|
+
// index of the source.children list. If PARENT_ID is a string, we search
|
|
1843
|
+
// a parent with node.key of this value.
|
|
1844
|
+
indexToNodeMap[index] = kwargs;
|
|
1845
|
+
const key = kwargs[keyAttrName];
|
|
1846
|
+
if (key != null) {
|
|
1847
|
+
keyToNodeMap[key] = kwargs;
|
|
1848
|
+
}
|
|
1849
|
+
let parentNode = null;
|
|
1850
|
+
if (parentId === null) ;
|
|
1851
|
+
else if (typeof parentId === "number") {
|
|
1852
|
+
parentNode = indexToNodeMap[parentId];
|
|
1853
|
+
if (parentNode === undefined) {
|
|
1854
|
+
throw new Error(`unflattenSource: Could not find parent node by index: ${parentId}.`);
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
else {
|
|
1858
|
+
parentNode = keyToNodeMap[parentId];
|
|
1859
|
+
if (parentNode === undefined) {
|
|
1860
|
+
throw new Error(`unflattenSource: Could not find parent node by key: ${parentId}`);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
if (parentNode) {
|
|
1864
|
+
(_c = parentNode[childrenAttrName]) !== null && _c !== void 0 ? _c : (parentNode[childrenAttrName] = []);
|
|
1865
|
+
parentNode[childrenAttrName].push(kwargs);
|
|
1866
|
+
}
|
|
1867
|
+
else {
|
|
1868
|
+
newChildren.push(kwargs);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
delete source.children;
|
|
1872
|
+
source.children = newChildren;
|
|
1873
|
+
}
|
|
1874
|
+
function inflateSourceData(source) {
|
|
1875
|
+
const { _format, _keyMap, _typeList } = source;
|
|
1876
|
+
if (_format === "flat") {
|
|
1877
|
+
unflattenSource(source);
|
|
1878
|
+
}
|
|
1879
|
+
delete source._format;
|
|
1880
|
+
delete source._version;
|
|
1881
|
+
delete source._keyMap;
|
|
1882
|
+
delete source._typeList;
|
|
1883
|
+
delete source._positional;
|
|
1884
|
+
function _iter(childList) {
|
|
1885
|
+
for (let node of childList) {
|
|
1886
|
+
// Expand short alias names
|
|
1887
|
+
if (_keyMap) {
|
|
1888
|
+
// Iterate over a list of names, because we modify inside the loop:
|
|
1889
|
+
Object.getOwnPropertyNames(node).forEach((propName) => {
|
|
1890
|
+
var _a;
|
|
1891
|
+
const long = (_a = _keyMap[propName]) !== null && _a !== void 0 ? _a : propName;
|
|
1892
|
+
if (long !== propName) {
|
|
1893
|
+
node[long] = node[propName];
|
|
1894
|
+
delete node[propName];
|
|
1895
|
+
}
|
|
1896
|
+
});
|
|
1897
|
+
}
|
|
1898
|
+
// `node` now has long attribute names
|
|
1899
|
+
// Resolve node type indexes
|
|
1900
|
+
const type = node.type;
|
|
1901
|
+
if (_typeList && type != null && typeof type === "number") {
|
|
1902
|
+
const newType = _typeList[type];
|
|
1903
|
+
if (newType == null) {
|
|
1904
|
+
throw new Error(`Expected typeList[${type}] entry in [${_typeList}]`);
|
|
1905
|
+
}
|
|
1906
|
+
node.type = newType;
|
|
1907
|
+
}
|
|
1908
|
+
// Recursion
|
|
1909
|
+
if (node.children) {
|
|
1910
|
+
_iter(node.children);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
_iter(source.children);
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1767
1917
|
/*!
|
|
1768
1918
|
* Wunderbaum - ext-dnd
|
|
1769
1919
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1770
|
-
* v0.0.
|
|
1920
|
+
* v0.0.8, Fri, 23 Sep 2022 20:47:29 GMT (https://github.com/mar10/wunderbaum)
|
|
1771
1921
|
*/
|
|
1772
1922
|
const nodeMimeType = "application/x-wunderbaum-node";
|
|
1773
1923
|
class DndExtension extends WunderbaumExtension {
|
|
@@ -2035,7 +2185,7 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2035
2185
|
/*!
|
|
2036
2186
|
* Wunderbaum - drag_observer
|
|
2037
2187
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
2038
|
-
* v0.0.
|
|
2188
|
+
* v0.0.8, Fri, 23 Sep 2022 20:47:29 GMT (https://github.com/mar10/wunderbaum)
|
|
2039
2189
|
*/
|
|
2040
2190
|
/**
|
|
2041
2191
|
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
@@ -2169,7 +2319,7 @@ class DragObserver {
|
|
|
2169
2319
|
/*!
|
|
2170
2320
|
* Wunderbaum - ext-grid
|
|
2171
2321
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
2172
|
-
* v0.0.
|
|
2322
|
+
* v0.0.8, Fri, 23 Sep 2022 20:47:29 GMT (https://github.com/mar10/wunderbaum)
|
|
2173
2323
|
*/
|
|
2174
2324
|
class GridExtension extends WunderbaumExtension {
|
|
2175
2325
|
constructor(tree) {
|
|
@@ -2206,7 +2356,7 @@ class GridExtension extends WunderbaumExtension {
|
|
|
2206
2356
|
/*!
|
|
2207
2357
|
* Wunderbaum - deferred
|
|
2208
2358
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
2209
|
-
* v0.0.
|
|
2359
|
+
* v0.0.8, Fri, 23 Sep 2022 20:47:29 GMT (https://github.com/mar10/wunderbaum)
|
|
2210
2360
|
*/
|
|
2211
2361
|
/**
|
|
2212
2362
|
* Implement a ES6 Promise, that exposes a resolve() and reject() method.
|
|
@@ -2259,7 +2409,7 @@ class Deferred {
|
|
|
2259
2409
|
/*!
|
|
2260
2410
|
* Wunderbaum - wunderbaum_node
|
|
2261
2411
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
2262
|
-
* v0.0.
|
|
2412
|
+
* v0.0.8, Fri, 23 Sep 2022 20:47:29 GMT (https://github.com/mar10/wunderbaum)
|
|
2263
2413
|
*/
|
|
2264
2414
|
/** Top-level properties that can be passed with `data`. */
|
|
2265
2415
|
const NODE_PROPS = new Set([
|
|
@@ -2400,57 +2550,63 @@ class WunderbaumNode {
|
|
|
2400
2550
|
/**
|
|
2401
2551
|
* Append (or insert) a list of child nodes.
|
|
2402
2552
|
*
|
|
2403
|
-
* Tip: pass `{ before: 0 }` to prepend children
|
|
2404
|
-
*
|
|
2405
|
-
* @param child node (or key or index of such).
|
|
2406
|
-
* If omitted, the new children are appended.
|
|
2553
|
+
* Tip: pass `{ before: 0 }` to prepend new nodes as first children.
|
|
2554
|
+
*
|
|
2407
2555
|
* @returns first child added
|
|
2408
2556
|
*/
|
|
2409
2557
|
addChildren(nodeData, options) {
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
//
|
|
2414
|
-
|
|
2558
|
+
const tree = this.tree;
|
|
2559
|
+
let { before = null, applyMinExpanLevel = true, _level } = options !== null && options !== void 0 ? options : {};
|
|
2560
|
+
// let { before, loadLazy=true, _level } = options ?? {};
|
|
2561
|
+
// const isTopCall = _level == null;
|
|
2562
|
+
_level !== null && _level !== void 0 ? _level : (_level = this.getLevel());
|
|
2563
|
+
const nodeList = [];
|
|
2415
2564
|
try {
|
|
2416
|
-
|
|
2565
|
+
tree.enableUpdate(false);
|
|
2417
2566
|
if (isPlainObject(nodeData)) {
|
|
2418
2567
|
nodeData = [nodeData];
|
|
2419
2568
|
}
|
|
2569
|
+
const forceExpand = applyMinExpanLevel && _level < tree.options.minExpandLevel;
|
|
2420
2570
|
for (let child of nodeData) {
|
|
2421
|
-
|
|
2571
|
+
const subChildren = child.children;
|
|
2422
2572
|
delete child.children;
|
|
2423
|
-
|
|
2573
|
+
const n = new WunderbaumNode(tree, this, child);
|
|
2574
|
+
if (forceExpand && !n.isUnloaded()) {
|
|
2575
|
+
n.expanded = true;
|
|
2576
|
+
}
|
|
2424
2577
|
nodeList.push(n);
|
|
2425
2578
|
if (subChildren) {
|
|
2426
|
-
n.addChildren(subChildren, {
|
|
2579
|
+
n.addChildren(subChildren, { _level: _level + 1 });
|
|
2427
2580
|
}
|
|
2428
2581
|
}
|
|
2429
2582
|
if (!this.children) {
|
|
2430
2583
|
this.children = nodeList;
|
|
2431
2584
|
}
|
|
2432
|
-
else if (
|
|
2585
|
+
else if (before == null || this.children.length === 0) {
|
|
2433
2586
|
this.children = this.children.concat(nodeList);
|
|
2434
2587
|
}
|
|
2435
2588
|
else {
|
|
2436
|
-
// Returns null if
|
|
2437
|
-
|
|
2438
|
-
let pos = this.children.indexOf(
|
|
2439
|
-
assert(pos >= 0,
|
|
2589
|
+
// Returns null if before is not a direct child:
|
|
2590
|
+
before = this.findDirectChild(before);
|
|
2591
|
+
let pos = this.children.indexOf(before);
|
|
2592
|
+
assert(pos >= 0, `options.before must be a direct child of ${this}`);
|
|
2440
2593
|
// insert nodeList after children[pos]
|
|
2441
2594
|
this.children.splice(pos, 0, ...nodeList);
|
|
2442
2595
|
}
|
|
2443
2596
|
// TODO:
|
|
2444
|
-
// if (
|
|
2597
|
+
// if (tree.options.selectMode === 3) {
|
|
2445
2598
|
// this.fixSelection3FromEndNodes();
|
|
2446
2599
|
// }
|
|
2447
2600
|
// this.triggerModifyChild("add", nodeList.length === 1 ? nodeList[0] : null);
|
|
2448
|
-
|
|
2449
|
-
return nodeList[0];
|
|
2601
|
+
tree.setModified(ChangeType.structure);
|
|
2450
2602
|
}
|
|
2451
2603
|
finally {
|
|
2452
|
-
|
|
2604
|
+
tree.enableUpdate(true);
|
|
2453
2605
|
}
|
|
2606
|
+
// if(isTopCall && loadLazy){
|
|
2607
|
+
// this.logWarn("addChildren(): loadLazy is not yet implemented.")
|
|
2608
|
+
// }
|
|
2609
|
+
return nodeList[0];
|
|
2454
2610
|
}
|
|
2455
2611
|
/**
|
|
2456
2612
|
* Append or prepend a node, or append a child node.
|
|
@@ -2524,21 +2680,98 @@ class WunderbaumNode {
|
|
|
2524
2680
|
}
|
|
2525
2681
|
}
|
|
2526
2682
|
}
|
|
2527
|
-
/** Call `setExpanded()` on
|
|
2528
|
-
async expandAll(flag = true) {
|
|
2529
|
-
this.
|
|
2530
|
-
|
|
2531
|
-
}
|
|
2683
|
+
/** Call `setExpanded()` on all descendant nodes. */
|
|
2684
|
+
async expandAll(flag = true, options) {
|
|
2685
|
+
const tree = this.tree;
|
|
2686
|
+
const minExpandLevel = this.tree.options.minExpandLevel;
|
|
2687
|
+
let { depth = 99, loadLazy, force } = options !== null && options !== void 0 ? options : {};
|
|
2688
|
+
const expand_opts = {
|
|
2689
|
+
scrollIntoView: false,
|
|
2690
|
+
force: force,
|
|
2691
|
+
loadLazy: loadLazy,
|
|
2692
|
+
};
|
|
2693
|
+
// this.logInfo(`expandAll(${flag})`);
|
|
2694
|
+
// Expand all direct children in parallel:
|
|
2695
|
+
async function _iter(n, level) {
|
|
2696
|
+
var _a;
|
|
2697
|
+
// n.logInfo(` _iter(${level})`);
|
|
2698
|
+
if (level === 0) {
|
|
2699
|
+
return;
|
|
2700
|
+
}
|
|
2701
|
+
// if (!flag && minExpandLevel && !force && n.getLevel() <= minExpandLevel) {
|
|
2702
|
+
// return; // Do not collapse until minExpandLevel
|
|
2703
|
+
// }
|
|
2704
|
+
const level_1 = level == null ? null : level - 1;
|
|
2705
|
+
const promises = [];
|
|
2706
|
+
(_a = n.children) === null || _a === void 0 ? void 0 : _a.forEach((cn) => {
|
|
2707
|
+
if (flag) {
|
|
2708
|
+
if (!cn.expanded && (cn.children || (loadLazy && cn.lazy))) {
|
|
2709
|
+
// Node is collapsed and may be expanded (i.e. has children or is lazy)
|
|
2710
|
+
// Expanding may be async, so we store the promise.
|
|
2711
|
+
// Also the recursion is delayed until expansion finished.
|
|
2712
|
+
const p = cn.setExpanded(true, expand_opts);
|
|
2713
|
+
promises.push(p);
|
|
2714
|
+
p.then(async () => {
|
|
2715
|
+
await _iter(cn, level_1);
|
|
2716
|
+
});
|
|
2717
|
+
}
|
|
2718
|
+
else {
|
|
2719
|
+
// We don't expand the node, but still visit descendants.
|
|
2720
|
+
// There we may find lazy nodes, so we
|
|
2721
|
+
promises.push(_iter(cn, level_1));
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
else {
|
|
2725
|
+
// Collapsing is always synchronous, so no promises required
|
|
2726
|
+
if (!minExpandLevel || force || cn.getLevel() > minExpandLevel) {
|
|
2727
|
+
// Do not collapse until minExpandLevel
|
|
2728
|
+
cn.setExpanded(false, expand_opts);
|
|
2729
|
+
}
|
|
2730
|
+
_iter(cn, level_1); // recursion, even if cn was already collapsed
|
|
2731
|
+
}
|
|
2732
|
+
});
|
|
2733
|
+
return new Promise((resolve) => {
|
|
2734
|
+
Promise.all(promises).then(() => {
|
|
2735
|
+
resolve(true);
|
|
2736
|
+
});
|
|
2737
|
+
});
|
|
2738
|
+
}
|
|
2739
|
+
const tag = tree.logTime(`${this}.expandAll(${flag})`);
|
|
2740
|
+
try {
|
|
2741
|
+
tree.enableUpdate(false);
|
|
2742
|
+
await _iter(this, depth);
|
|
2743
|
+
}
|
|
2744
|
+
finally {
|
|
2745
|
+
tree.enableUpdate(true);
|
|
2746
|
+
tree.logTimeEnd(tag);
|
|
2747
|
+
}
|
|
2532
2748
|
}
|
|
2533
|
-
/**
|
|
2749
|
+
/**
|
|
2750
|
+
* Find all descendant nodes that match condition (excluding self).
|
|
2534
2751
|
*
|
|
2535
|
-
*
|
|
2536
|
-
*
|
|
2752
|
+
* If `match` is a string, search for exact node title.
|
|
2753
|
+
* If `match` is a RegExp expression, apply it to node.title, using
|
|
2754
|
+
* [RegExp.test()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test).
|
|
2755
|
+
* If `match` is a callback, match all nodes for that the callback(node) returns true.
|
|
2756
|
+
*
|
|
2757
|
+
* Returns an empty array if no nodes were found.
|
|
2758
|
+
*
|
|
2759
|
+
* Examples:
|
|
2760
|
+
* ```js
|
|
2761
|
+
* // Match all node titles that match exactly 'Joe':
|
|
2762
|
+
* nodeList = node.findAll("Joe")
|
|
2763
|
+
* // Match all node titles that start with 'Joe' case sensitive:
|
|
2764
|
+
* nodeList = node.findAll(/^Joe/)
|
|
2765
|
+
* // Match all node titles that contain 'oe', case insensitive:
|
|
2766
|
+
* nodeList = node.findAll(/oe/i)
|
|
2767
|
+
* // Match all nodes with `data.price` >= 99:
|
|
2768
|
+
* nodeList = node.findAll((n) => {
|
|
2769
|
+
* return n.data.price >= 99;
|
|
2770
|
+
* })
|
|
2771
|
+
* ```
|
|
2537
2772
|
*/
|
|
2538
2773
|
findAll(match) {
|
|
2539
|
-
const matcher =
|
|
2540
|
-
? match
|
|
2541
|
-
: makeNodeTitleMatcher(match);
|
|
2774
|
+
const matcher = typeof match === "function" ? match : makeNodeTitleMatcher(match);
|
|
2542
2775
|
const res = [];
|
|
2543
2776
|
this.visit((n) => {
|
|
2544
2777
|
if (matcher(n)) {
|
|
@@ -2568,15 +2801,13 @@ class WunderbaumNode {
|
|
|
2568
2801
|
}
|
|
2569
2802
|
return null;
|
|
2570
2803
|
}
|
|
2571
|
-
/**
|
|
2804
|
+
/**
|
|
2805
|
+
* Find first descendant node that matches condition (excluding self) or null.
|
|
2572
2806
|
*
|
|
2573
|
-
* @
|
|
2574
|
-
* callback function that returns `true` if a node is matched.
|
|
2807
|
+
* @see {@link WunderbaumNode.findAll} for examples.
|
|
2575
2808
|
*/
|
|
2576
2809
|
findFirst(match) {
|
|
2577
|
-
const matcher =
|
|
2578
|
-
? match
|
|
2579
|
-
: makeNodeTitleMatcher(match);
|
|
2810
|
+
const matcher = typeof match === "function" ? match : makeNodeTitleMatcher(match);
|
|
2580
2811
|
let res = null;
|
|
2581
2812
|
this.visit((n) => {
|
|
2582
2813
|
if (matcher(n)) {
|
|
@@ -2740,7 +2971,8 @@ class WunderbaumNode {
|
|
|
2740
2971
|
* an expand operation is currently possible.
|
|
2741
2972
|
*/
|
|
2742
2973
|
isExpandable(andCollapsed = false) {
|
|
2743
|
-
return !!this.children && (!this.expanded || !andCollapsed);
|
|
2974
|
+
// return !!this.children && (!this.expanded || !andCollapsed);
|
|
2975
|
+
return !!(this.children || this.lazy) && (!this.expanded || !andCollapsed);
|
|
2744
2976
|
}
|
|
2745
2977
|
/** Return true if this node is currently in edit-title mode. */
|
|
2746
2978
|
isEditing() {
|
|
@@ -2841,14 +3073,20 @@ class WunderbaumNode {
|
|
|
2841
3073
|
// this.debug("isVisible: VISIBLE");
|
|
2842
3074
|
return true;
|
|
2843
3075
|
}
|
|
2844
|
-
_loadSourceObject(source) {
|
|
3076
|
+
_loadSourceObject(source, level) {
|
|
3077
|
+
var _a;
|
|
2845
3078
|
const tree = this.tree;
|
|
3079
|
+
level !== null && level !== void 0 ? level : (level = this.getLevel());
|
|
2846
3080
|
// Let caller modify the parsed JSON response:
|
|
2847
3081
|
this._callEvent("receive", { response: source });
|
|
2848
3082
|
if (isArray(source)) {
|
|
2849
3083
|
source = { children: source };
|
|
2850
3084
|
}
|
|
2851
3085
|
assert(isPlainObject(source));
|
|
3086
|
+
const format = (_a = source.format) !== null && _a !== void 0 ? _a : "nested";
|
|
3087
|
+
assert(format === "nested" || format === "flat");
|
|
3088
|
+
// Pre-rocess for 'nested' or 'flat' format
|
|
3089
|
+
inflateSourceData(source);
|
|
2852
3090
|
assert(source.children, "If `source` is an object, it must have a `children` property");
|
|
2853
3091
|
if (source.types) {
|
|
2854
3092
|
tree.logInfo("Redefine types", source.columns);
|
|
@@ -2862,7 +3100,6 @@ class WunderbaumNode {
|
|
|
2862
3100
|
tree.updateColumns({ calculateCols: false });
|
|
2863
3101
|
}
|
|
2864
3102
|
this.addChildren(source.children);
|
|
2865
|
-
delete source.columns;
|
|
2866
3103
|
// Add extra data to `tree.data`
|
|
2867
3104
|
for (const [key, value] of Object.entries(source)) {
|
|
2868
3105
|
if (!RESERVED_TREE_SOURCE_KEYS.has(key)) {
|
|
@@ -3009,17 +3246,16 @@ class WunderbaumNode {
|
|
|
3009
3246
|
}
|
|
3010
3247
|
/** Expand all parents and optionally scroll into visible area as neccessary.
|
|
3011
3248
|
* Promise is resolved, when lazy loading and animations are done.
|
|
3012
|
-
* @param {object} [
|
|
3249
|
+
* @param {object} [options] passed to `setExpanded()`.
|
|
3013
3250
|
* Defaults to {noAnimation: false, noEvents: false, scrollIntoView: true}
|
|
3014
3251
|
*/
|
|
3015
|
-
async makeVisible(
|
|
3016
|
-
let i, dfd = new Deferred(), deferreds = [], parents = this.getParentList(false, false), len = parents.length,
|
|
3017
|
-
//
|
|
3018
|
-
scroll = !(opts && opts.scrollIntoView === false);
|
|
3252
|
+
async makeVisible(options) {
|
|
3253
|
+
let i, dfd = new Deferred(), deferreds = [], parents = this.getParentList(false, false), len = parents.length, noAnimation = getOption(options, "noAnimation", false), scroll = getOption(options, "scrollIntoView", true);
|
|
3254
|
+
// scroll = !(options && options.scrollIntoView === false);
|
|
3019
3255
|
// Expand bottom-up, so only the top node is animated
|
|
3020
3256
|
for (i = len - 1; i >= 0; i--) {
|
|
3021
3257
|
// self.debug("pushexpand" + parents[i]);
|
|
3022
|
-
const seOpts = { noAnimation:
|
|
3258
|
+
const seOpts = { noAnimation: noAnimation };
|
|
3023
3259
|
deferreds.push(parents[i].setExpanded(true, seOpts));
|
|
3024
3260
|
}
|
|
3025
3261
|
Promise.all(deferreds).then(() => {
|
|
@@ -3261,6 +3497,9 @@ class WunderbaumNode {
|
|
|
3261
3497
|
else if (this.children) {
|
|
3262
3498
|
icon = iconMap.folder;
|
|
3263
3499
|
}
|
|
3500
|
+
else if (this.lazy) {
|
|
3501
|
+
icon = iconMap.folderLazy;
|
|
3502
|
+
}
|
|
3264
3503
|
else {
|
|
3265
3504
|
icon = iconMap.doc;
|
|
3266
3505
|
}
|
|
@@ -3424,13 +3663,13 @@ class WunderbaumNode {
|
|
|
3424
3663
|
if (isColspan) {
|
|
3425
3664
|
let vpWidth = tree.element.clientWidth;
|
|
3426
3665
|
titleSpan.style.width =
|
|
3427
|
-
vpWidth - nodeElem._ofsTitlePx -
|
|
3666
|
+
vpWidth - nodeElem._ofsTitlePx - TITLE_SPAN_PAD_Y + "px";
|
|
3428
3667
|
}
|
|
3429
3668
|
else {
|
|
3430
3669
|
titleSpan.style.width =
|
|
3431
3670
|
columns[0]._widthPx -
|
|
3432
3671
|
nodeElem._ofsTitlePx -
|
|
3433
|
-
|
|
3672
|
+
TITLE_SPAN_PAD_Y +
|
|
3434
3673
|
"px";
|
|
3435
3674
|
}
|
|
3436
3675
|
}
|
|
@@ -3743,22 +3982,32 @@ class WunderbaumNode {
|
|
|
3743
3982
|
* Expand or collapse this node.
|
|
3744
3983
|
*/
|
|
3745
3984
|
async setExpanded(flag = true, options) {
|
|
3985
|
+
const { force, scrollIntoView, immediate } = options !== null && options !== void 0 ? options : {};
|
|
3746
3986
|
if (!flag &&
|
|
3747
3987
|
this.isExpanded() &&
|
|
3748
3988
|
this.getLevel() <= this.tree.getOption("minExpandLevel") &&
|
|
3749
|
-
!
|
|
3989
|
+
!force) {
|
|
3750
3990
|
this.logDebug("Ignored collapse request below expandLevel.");
|
|
3751
3991
|
return;
|
|
3752
3992
|
}
|
|
3753
3993
|
if (!flag === !this.expanded) {
|
|
3754
3994
|
return; // Nothing to do
|
|
3755
3995
|
}
|
|
3996
|
+
// this.log("setExpanded()");
|
|
3756
3997
|
if (flag && this.lazy && this.children == null) {
|
|
3757
3998
|
await this.loadLazy();
|
|
3758
3999
|
}
|
|
3759
4000
|
this.expanded = flag;
|
|
3760
|
-
const updateOpts = { immediate:
|
|
4001
|
+
const updateOpts = { immediate: immediate };
|
|
4002
|
+
// const updateOpts = { immediate: !!util.getOption(options, "immediate") };
|
|
3761
4003
|
this.tree.setModified(ChangeType.structure, updateOpts);
|
|
4004
|
+
if (flag && scrollIntoView !== false) {
|
|
4005
|
+
const lastChild = this.getLastChild();
|
|
4006
|
+
if (lastChild) {
|
|
4007
|
+
this.tree.updatePendingModifications();
|
|
4008
|
+
lastChild.scrollIntoView({ topNode: this });
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
3762
4011
|
}
|
|
3763
4012
|
/**
|
|
3764
4013
|
* Set keyboard focus here.
|
|
@@ -3987,7 +4236,7 @@ WunderbaumNode.sequence = 0;
|
|
|
3987
4236
|
/*!
|
|
3988
4237
|
* Wunderbaum - ext-edit
|
|
3989
4238
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
3990
|
-
* v0.0.
|
|
4239
|
+
* v0.0.8, Fri, 23 Sep 2022 20:47:29 GMT (https://github.com/mar10/wunderbaum)
|
|
3991
4240
|
*/
|
|
3992
4241
|
// const START_MARKER = "\uFFF7";
|
|
3993
4242
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -4133,8 +4382,9 @@ class EditExtension extends WunderbaumExtension {
|
|
|
4133
4382
|
node.logInfo("beforeEdit canceled operation.");
|
|
4134
4383
|
return;
|
|
4135
4384
|
}
|
|
4136
|
-
// `beforeEdit(e)` may return an input HTML string. Otherwise use a default
|
|
4137
|
-
|
|
4385
|
+
// `beforeEdit(e)` may return an input HTML string. Otherwise use a default.
|
|
4386
|
+
// (we also treat a `true` return value as 'use default'):
|
|
4387
|
+
if (inputHtml === true || !inputHtml) {
|
|
4138
4388
|
const title = escapeHtml(node.title);
|
|
4139
4389
|
inputHtml = `<input type=text class="wb-input-edit" value="${title}" required autocorrect=off>`;
|
|
4140
4390
|
}
|
|
@@ -4279,8 +4529,8 @@ class EditExtension extends WunderbaumExtension {
|
|
|
4279
4529
|
* https://github.com/mar10/wunderbaum
|
|
4280
4530
|
*
|
|
4281
4531
|
* Released under the MIT license.
|
|
4282
|
-
* @version v0.0.
|
|
4283
|
-
* @date
|
|
4532
|
+
* @version v0.0.8
|
|
4533
|
+
* @date Fri, 23 Sep 2022 20:47:29 GMT
|
|
4284
4534
|
*/
|
|
4285
4535
|
class WbSystemRoot extends WunderbaumNode {
|
|
4286
4536
|
constructor(tree) {
|
|
@@ -4851,22 +5101,22 @@ class Wunderbaum {
|
|
|
4851
5101
|
* - 'down', 'first', 'last', 'left', 'parent', 'right', 'up': navigate
|
|
4852
5102
|
*
|
|
4853
5103
|
*/
|
|
4854
|
-
applyCommand(cmd, nodeOrOpts,
|
|
5104
|
+
applyCommand(cmd, nodeOrOpts, options) {
|
|
4855
5105
|
let // clipboard,
|
|
4856
5106
|
node, refNode;
|
|
4857
|
-
//
|
|
5107
|
+
// options = $.extend(
|
|
4858
5108
|
// { setActive: true, clipboard: CLIPBOARD },
|
|
4859
|
-
//
|
|
5109
|
+
// options_
|
|
4860
5110
|
// );
|
|
4861
5111
|
if (nodeOrOpts instanceof WunderbaumNode) {
|
|
4862
5112
|
node = nodeOrOpts;
|
|
4863
5113
|
}
|
|
4864
5114
|
else {
|
|
4865
5115
|
node = this.getActiveNode();
|
|
4866
|
-
assert(
|
|
4867
|
-
|
|
5116
|
+
assert(options === undefined);
|
|
5117
|
+
options = nodeOrOpts;
|
|
4868
5118
|
}
|
|
4869
|
-
// clipboard =
|
|
5119
|
+
// clipboard = options.clipboard;
|
|
4870
5120
|
switch (cmd) {
|
|
4871
5121
|
// Sorting and indentation:
|
|
4872
5122
|
case "moveUp":
|
|
@@ -5072,16 +5322,8 @@ class Wunderbaum {
|
|
|
5072
5322
|
}
|
|
5073
5323
|
}
|
|
5074
5324
|
/** Recursively expand all expandable nodes (triggers lazy load id needed). */
|
|
5075
|
-
async expandAll(flag = true) {
|
|
5076
|
-
|
|
5077
|
-
try {
|
|
5078
|
-
this.enableUpdate(false);
|
|
5079
|
-
await this.root.expandAll(flag);
|
|
5080
|
-
}
|
|
5081
|
-
finally {
|
|
5082
|
-
this.enableUpdate(true);
|
|
5083
|
-
this.logTimeEnd(tag);
|
|
5084
|
-
}
|
|
5325
|
+
async expandAll(flag = true, options) {
|
|
5326
|
+
await this.root.expandAll(flag, options);
|
|
5085
5327
|
}
|
|
5086
5328
|
/** Recursively select all nodes. */
|
|
5087
5329
|
selectAll(flag = true) {
|
|
@@ -5115,10 +5357,7 @@ class Wunderbaum {
|
|
|
5115
5357
|
// util.assert(this.keyMap.size === i);
|
|
5116
5358
|
}
|
|
5117
5359
|
/**
|
|
5118
|
-
* Find all nodes that
|
|
5119
|
-
*
|
|
5120
|
-
* @param match title string to search for, or a
|
|
5121
|
-
* callback function that returns `true` if a node is matched.
|
|
5360
|
+
* Find all nodes that match condition.
|
|
5122
5361
|
*
|
|
5123
5362
|
* @see {@link WunderbaumNode.findAll}
|
|
5124
5363
|
*/
|
|
@@ -5128,10 +5367,7 @@ class Wunderbaum {
|
|
|
5128
5367
|
/**
|
|
5129
5368
|
* Find first node that matches condition.
|
|
5130
5369
|
*
|
|
5131
|
-
* @param match title string to search for, or a
|
|
5132
|
-
* callback function that returns `true` if a node is matched.
|
|
5133
5370
|
* @see {@link WunderbaumNode.findFirst}
|
|
5134
|
-
*
|
|
5135
5371
|
*/
|
|
5136
5372
|
findFirst(match) {
|
|
5137
5373
|
return this.root.findFirst(match);
|
|
@@ -5145,7 +5381,7 @@ class Wunderbaum {
|
|
|
5145
5381
|
*
|
|
5146
5382
|
*/
|
|
5147
5383
|
findKey(key) {
|
|
5148
|
-
return this.keyMap.get(key);
|
|
5384
|
+
return this.keyMap.get(key) || null;
|
|
5149
5385
|
}
|
|
5150
5386
|
/**
|
|
5151
5387
|
* Find the next visible node that starts with `match`, starting at `startNode`
|
|
@@ -5434,17 +5670,20 @@ class Wunderbaum {
|
|
|
5434
5670
|
}
|
|
5435
5671
|
/**
|
|
5436
5672
|
* Make sure that this node is vertically scrolled into the viewport.
|
|
5673
|
+
*
|
|
5674
|
+
* Nodes that are above the visible area become the top row, nodes that are
|
|
5675
|
+
* below the viewport become the bottom row.
|
|
5437
5676
|
*/
|
|
5438
5677
|
scrollTo(nodeOrOpts) {
|
|
5439
5678
|
const PADDING = 2; // leave some pixels between viewport bounds
|
|
5440
5679
|
let node;
|
|
5441
|
-
let
|
|
5680
|
+
let options;
|
|
5442
5681
|
if (nodeOrOpts instanceof WunderbaumNode) {
|
|
5443
5682
|
node = nodeOrOpts;
|
|
5444
5683
|
}
|
|
5445
5684
|
else {
|
|
5446
|
-
|
|
5447
|
-
node =
|
|
5685
|
+
options = nodeOrOpts;
|
|
5686
|
+
node = options.node;
|
|
5448
5687
|
}
|
|
5449
5688
|
assert(node && node._rowIdx != null);
|
|
5450
5689
|
const scrollParent = this.element;
|
|
@@ -5455,6 +5694,7 @@ class Wunderbaum {
|
|
|
5455
5694
|
const vpTop = headerHeight;
|
|
5456
5695
|
const vpRowTop = rowTop - scrollTop;
|
|
5457
5696
|
const vpRowBottom = vpRowTop + ROW_HEIGHT;
|
|
5697
|
+
const topNode = options === null || options === void 0 ? void 0 : options.topNode;
|
|
5458
5698
|
// this.log( `scrollTo(${node.title}), vpTop:${vpTop}px, scrollTop:${scrollTop}, vpHeight:${vpHeight}, rowTop:${rowTop}, vpRowTop:${vpRowTop}`, nodeOrOpts );
|
|
5459
5699
|
let newScrollTop = null;
|
|
5460
5700
|
if (vpRowTop >= vpTop) {
|
|
@@ -5462,17 +5702,21 @@ class Wunderbaum {
|
|
|
5462
5702
|
else {
|
|
5463
5703
|
// Node is below viewport
|
|
5464
5704
|
// this.log("Below viewport");
|
|
5465
|
-
newScrollTop = rowTop + ROW_HEIGHT - vpHeight + PADDING; // leave some pixels between
|
|
5705
|
+
newScrollTop = rowTop + ROW_HEIGHT - vpHeight + PADDING; // leave some pixels between viewport bounds
|
|
5466
5706
|
}
|
|
5467
5707
|
}
|
|
5468
5708
|
else {
|
|
5469
5709
|
// Node is above viewport
|
|
5470
5710
|
// this.log("Above viewport");
|
|
5471
|
-
newScrollTop = rowTop - vpTop - PADDING; // leave some pixels between
|
|
5711
|
+
newScrollTop = rowTop - vpTop - PADDING; // leave some pixels between viewport bounds
|
|
5472
5712
|
}
|
|
5473
5713
|
if (newScrollTop != null) {
|
|
5474
5714
|
this.log(`scrollTo(${rowTop}): ${scrollTop} => ${newScrollTop}`);
|
|
5475
5715
|
scrollParent.scrollTop = newScrollTop;
|
|
5716
|
+
if (topNode) {
|
|
5717
|
+
// Make sure the topNode is always visible
|
|
5718
|
+
this.scrollTo(topNode);
|
|
5719
|
+
}
|
|
5476
5720
|
// this.setModified(ChangeType.vscroll);
|
|
5477
5721
|
}
|
|
5478
5722
|
}
|
|
@@ -5501,10 +5745,9 @@ class Wunderbaum {
|
|
|
5501
5745
|
newLeft = colRight - vpWidth;
|
|
5502
5746
|
}
|
|
5503
5747
|
// util.assert(node._rowIdx != null);
|
|
5504
|
-
//
|
|
5505
|
-
|
|
5506
|
-
//
|
|
5507
|
-
// let newLeft;
|
|
5748
|
+
// this.log(
|
|
5749
|
+
// `scrollToHorz(${this.activeColIdx}): ${colLeft}..${colRight}, fixedOfs=${fixedWidth}, vpWidth=${vpWidth}, curLeft=${scrollLeft} -> ${newLeft}`
|
|
5750
|
+
// );
|
|
5508
5751
|
this.element.scrollLeft = newLeft;
|
|
5509
5752
|
// this.setModified(ChangeType.vscroll);
|
|
5510
5753
|
// }
|
|
@@ -5695,11 +5938,13 @@ class Wunderbaum {
|
|
|
5695
5938
|
}
|
|
5696
5939
|
}
|
|
5697
5940
|
/** Update column headers and width. */
|
|
5698
|
-
updateColumns(
|
|
5699
|
-
|
|
5941
|
+
updateColumns(options) {
|
|
5942
|
+
options = Object.assign({ calculateCols: true, updateRows: true }, options);
|
|
5700
5943
|
const defaultMinWidth = 4;
|
|
5701
5944
|
const vpWidth = this.element.clientWidth;
|
|
5702
5945
|
const isGrid = this.isGrid();
|
|
5946
|
+
// Shorten last column width to avoid h-scrollbar
|
|
5947
|
+
const FIX_ADJUST_LAST_COL = 2;
|
|
5703
5948
|
let totalWidth = 0;
|
|
5704
5949
|
let totalWeight = 0;
|
|
5705
5950
|
let fixedWidth = 0;
|
|
@@ -5708,7 +5953,7 @@ class Wunderbaum {
|
|
|
5708
5953
|
if (!isGrid && this.isCellNav()) {
|
|
5709
5954
|
this.setCellNav(false);
|
|
5710
5955
|
}
|
|
5711
|
-
if (
|
|
5956
|
+
if (options.calculateCols) {
|
|
5712
5957
|
// Gather width definitions
|
|
5713
5958
|
this._columnsById = {};
|
|
5714
5959
|
for (let col of this.columns) {
|
|
@@ -5760,7 +6005,8 @@ class Wunderbaum {
|
|
|
5760
6005
|
col._ofsPx = ofsPx;
|
|
5761
6006
|
ofsPx += col._widthPx;
|
|
5762
6007
|
}
|
|
5763
|
-
|
|
6008
|
+
this.columns[this.columns.length - 1]._widthPx -= FIX_ADJUST_LAST_COL;
|
|
6009
|
+
totalWidth = ofsPx - FIX_ADJUST_LAST_COL;
|
|
5764
6010
|
}
|
|
5765
6011
|
// if (this.options.fixedCol) {
|
|
5766
6012
|
// 'position: fixed' requires that the content has the correct size
|
|
@@ -5774,7 +6020,7 @@ class Wunderbaum {
|
|
|
5774
6020
|
// util.error("BREAK");
|
|
5775
6021
|
if (modified) {
|
|
5776
6022
|
this._renderHeaderMarkup();
|
|
5777
|
-
if (
|
|
6023
|
+
if (options.updateRows) {
|
|
5778
6024
|
this._updateRows();
|
|
5779
6025
|
}
|
|
5780
6026
|
}
|
|
@@ -5837,6 +6083,8 @@ class Wunderbaum {
|
|
|
5837
6083
|
*/
|
|
5838
6084
|
_updateViewportImmediately() {
|
|
5839
6085
|
var _a;
|
|
6086
|
+
// Shorten container height to avoid v-scrollbar
|
|
6087
|
+
const FIX_ADJUST_HEIGHT = 1;
|
|
5840
6088
|
if (this._disableUpdateCount) {
|
|
5841
6089
|
this.log(`IGNORED _updateViewportImmediately() disable level: ${this._disableUpdateCount}`);
|
|
5842
6090
|
return;
|
|
@@ -5850,7 +6098,7 @@ class Wunderbaum {
|
|
|
5850
6098
|
// let headerHeight = this.headerElement.children[0].children[0].clientHeight;
|
|
5851
6099
|
// const headerHeight = this.options.headerHeightPx;
|
|
5852
6100
|
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
5853
|
-
const wantHeight = this.element.clientHeight - headerHeight;
|
|
6101
|
+
const wantHeight = this.element.clientHeight - headerHeight - FIX_ADJUST_HEIGHT;
|
|
5854
6102
|
if (Math.abs(height - wantHeight) > 1.0) {
|
|
5855
6103
|
// this.log("resize", height, wantHeight);
|
|
5856
6104
|
this.scrollContainerElement.style.height = wantHeight + "px";
|
|
@@ -5907,14 +6155,15 @@ class Wunderbaum {
|
|
|
5907
6155
|
* (including upper and lower prefetch)
|
|
5908
6156
|
* -
|
|
5909
6157
|
*/
|
|
5910
|
-
_updateRows(
|
|
6158
|
+
_updateRows(options) {
|
|
5911
6159
|
// const label = this.logTime("_updateRows");
|
|
5912
6160
|
// this.log("_updateRows", opts)
|
|
5913
|
-
|
|
5914
|
-
const newNodesOnly = !!
|
|
6161
|
+
options = Object.assign({ newNodesOnly: false }, options);
|
|
6162
|
+
const newNodesOnly = !!options.newNodesOnly;
|
|
5915
6163
|
const row_height = ROW_HEIGHT;
|
|
5916
6164
|
const vp_height = this.element.clientHeight;
|
|
5917
6165
|
const prefetch = RENDER_MAX_PREFETCH;
|
|
6166
|
+
// const grace_prefetch = RENDER_MAX_PREFETCH - RENDER_MIN_PREFETCH;
|
|
5918
6167
|
const ofs = this.element.scrollTop;
|
|
5919
6168
|
let startIdx = Math.max(0, ofs / row_height - prefetch);
|
|
5920
6169
|
startIdx = Math.floor(startIdx);
|
|
@@ -6013,16 +6262,16 @@ class Wunderbaum {
|
|
|
6013
6262
|
* {start: First tree node, reverse: false, includeSelf: true, includeHidden: false, wrap: false}
|
|
6014
6263
|
* @returns {boolean} false if iteration was canceled
|
|
6015
6264
|
*/
|
|
6016
|
-
visitRows(callback,
|
|
6265
|
+
visitRows(callback, options) {
|
|
6017
6266
|
if (!this.root.hasChildren()) {
|
|
6018
6267
|
return false;
|
|
6019
6268
|
}
|
|
6020
|
-
if (
|
|
6021
|
-
delete
|
|
6022
|
-
return this._visitRowsUp(callback,
|
|
6269
|
+
if (options && options.reverse) {
|
|
6270
|
+
delete options.reverse;
|
|
6271
|
+
return this._visitRowsUp(callback, options);
|
|
6023
6272
|
}
|
|
6024
|
-
|
|
6025
|
-
let i, nextIdx, parent, res, siblings, stopNode, siblingOfs = 0, skipFirstNode =
|
|
6273
|
+
options = options || {};
|
|
6274
|
+
let i, nextIdx, parent, res, siblings, stopNode, siblingOfs = 0, skipFirstNode = options.includeSelf === false, includeHidden = !!options.includeHidden, checkFilter = !includeHidden && this.filterMode === "hide", node = options.start || this.root.children[0];
|
|
6026
6275
|
parent = node.parent;
|
|
6027
6276
|
while (parent) {
|
|
6028
6277
|
// visit siblings
|
|
@@ -6071,11 +6320,11 @@ class Wunderbaum {
|
|
|
6071
6320
|
node = parent;
|
|
6072
6321
|
parent = parent.parent;
|
|
6073
6322
|
siblingOfs = 1; //
|
|
6074
|
-
if (!parent &&
|
|
6323
|
+
if (!parent && options.wrap) {
|
|
6075
6324
|
this.logDebug("visitRows(): wrap around");
|
|
6076
|
-
assert(
|
|
6077
|
-
stopNode =
|
|
6078
|
-
|
|
6325
|
+
assert(options.start, "`wrap` option requires `start`");
|
|
6326
|
+
stopNode = options.start;
|
|
6327
|
+
options.wrap = false;
|
|
6079
6328
|
parent = this.root;
|
|
6080
6329
|
siblingOfs = 0;
|
|
6081
6330
|
}
|
|
@@ -6210,7 +6459,7 @@ class Wunderbaum {
|
|
|
6210
6459
|
}
|
|
6211
6460
|
Wunderbaum.sequence = 0;
|
|
6212
6461
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
6213
|
-
Wunderbaum.version = "v0.0.
|
|
6462
|
+
Wunderbaum.version = "v0.0.8"; // Set to semver by 'grunt release'
|
|
6214
6463
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
6215
6464
|
Wunderbaum.util = util;
|
|
6216
6465
|
|