jqtree 1.7.0 → 1.7.2

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.
Files changed (99) hide show
  1. package/.github/workflows/ci.yml +2 -15
  2. package/.github/workflows/codeql-analysis.yml +2 -2
  3. package/.github/workflows/size.yml +24 -0
  4. package/README.md +1 -1
  5. package/bower.json +1 -1
  6. package/docs/Gemfile.lock +39 -40
  7. package/docs/_config.yml +1 -1
  8. package/docs/_entries/03_features.md +1 -1
  9. package/docs/_entries/10_changelog.md +9 -0
  10. package/docs/_entries/16_closedicon.md +17 -3
  11. package/docs/_entries/31_openedicon.md +17 -3
  12. package/docs/package.json +7 -7
  13. package/docs/pnpm-lock.yaml +378 -284
  14. package/docs/static/bower.json +2 -2
  15. package/docs/static/bower_components/fontawesome/css/all.min.css +6 -2
  16. package/docs/static/bower_components/fontawesome/webfonts/fa-brands-400.ttf +0 -0
  17. package/docs/static/bower_components/fontawesome/webfonts/fa-brands-400.woff2 +0 -0
  18. package/docs/static/bower_components/fontawesome/webfonts/fa-regular-400.ttf +0 -0
  19. package/docs/static/bower_components/fontawesome/webfonts/fa-regular-400.woff2 +0 -0
  20. package/docs/static/bower_components/fontawesome/webfonts/fa-solid-900.ttf +0 -0
  21. package/docs/static/bower_components/fontawesome/webfonts/fa-solid-900.woff2 +0 -0
  22. package/docs/static/bower_components/fontawesome/webfonts/fa-v4compatibility.ttf +0 -0
  23. package/docs/static/bower_components/fontawesome/webfonts/fa-v4compatibility.woff2 +0 -0
  24. package/docs/static/bower_components/jquery/dist/jquery.js +813 -1102
  25. package/docs/static/bower_components/jquery/dist/jquery.min.js +2 -2
  26. package/docs/static/documentation.css +104 -222
  27. package/docs/static/examples/autoescape.js +15 -17
  28. package/docs/static/examples/autoscroll.js +5 -7
  29. package/docs/static/examples/button-on-right.js +4 -6
  30. package/docs/static/examples/custom_html.js +22 -24
  31. package/docs/static/examples/drag-outside.js +23 -25
  32. package/docs/static/examples/drag_and_drop.js +4 -6
  33. package/docs/static/examples/icon_buttons.js +3 -5
  34. package/docs/static/examples/load_json_data.js +14 -16
  35. package/docs/static/examples/load_json_data_from_server.js +1 -3
  36. package/docs/static/examples/load_on_demand.js +3 -5
  37. package/docs/static/examples/multiple_select.js +19 -21
  38. package/docs/static/examples/right-to-left.js +2 -4
  39. package/docs/static/examples/save_state.js +2 -4
  40. package/docs/tree.jquery.js +3 -3
  41. package/lib/dataLoader.js +3 -3
  42. package/lib/dragAndDropHandler.js +22 -11
  43. package/lib/elementsRenderer.js +26 -16
  44. package/lib/keyHandler.js +1 -1
  45. package/lib/mouse.widget.js +13 -2
  46. package/lib/node.js +17 -14
  47. package/lib/nodeElement.js +20 -13
  48. package/lib/playwright/coverage.js +11 -16
  49. package/lib/playwright/playwright.test.js +37 -58
  50. package/lib/playwright/testUtils.js +27 -44
  51. package/lib/saveStateHandler.js +2 -2
  52. package/lib/scrollHandler.js +8 -4
  53. package/lib/selectNodeHandler.js +2 -2
  54. package/lib/simple.widget.js +5 -3
  55. package/lib/test/jqTree/accessibility.test.js +37 -0
  56. package/lib/test/jqTree/events.test.js +73 -77
  57. package/lib/test/jqTree/loadOnDemand.test.js +5 -4
  58. package/lib/test/jqTree/methods.test.js +2 -2
  59. package/lib/test/jqTree/options.test.js +43 -2
  60. package/lib/test/node.test.js +7 -4
  61. package/lib/tree.jquery.js +5 -5
  62. package/lib/version.js +1 -1
  63. package/package.json +37 -41
  64. package/src/dragAndDropHandler.ts +24 -10
  65. package/src/elementsRenderer.ts +57 -50
  66. package/src/jqtreeOptions.ts +5 -5
  67. package/src/mouse.widget.ts +12 -0
  68. package/src/node.ts +10 -5
  69. package/src/nodeElement.ts +27 -21
  70. package/src/saveStateHandler.ts +1 -1
  71. package/src/scrollHandler.ts +6 -2
  72. package/src/selectNodeHandler.ts +1 -1
  73. package/src/simple.widget.ts +2 -0
  74. package/src/test/.eslintrc +2 -1
  75. package/src/test/jqTree/accessibility.test.ts +25 -0
  76. package/src/test/jqTree/events.test.ts +79 -93
  77. package/src/test/jqTree/loadOnDemand.test.ts +6 -5
  78. package/src/test/jqTree/methods.test.ts +27 -27
  79. package/src/test/jqTree/options.test.ts +61 -8
  80. package/src/test/node.test.ts +6 -4
  81. package/src/test/nodeUtil.test.ts +1 -1
  82. package/src/tree.jquery.d.ts +20 -11
  83. package/src/tree.jquery.ts +1 -1
  84. package/src/version.ts +1 -1
  85. package/tree.jquery.debug.js +143 -97
  86. package/tree.jquery.debug.js.map +1 -1
  87. package/tree.jquery.js +3 -3
  88. package/tree.jquery.js.map +1 -1
  89. package/tsconfig.json +1 -0
  90. package/.tool-versions +0 -1
  91. package/docs/static/bower_components/fontawesome/webfonts/fa-brands-400.eot +0 -0
  92. package/docs/static/bower_components/fontawesome/webfonts/fa-brands-400.svg +0 -3717
  93. package/docs/static/bower_components/fontawesome/webfonts/fa-brands-400.woff +0 -0
  94. package/docs/static/bower_components/fontawesome/webfonts/fa-regular-400.eot +0 -0
  95. package/docs/static/bower_components/fontawesome/webfonts/fa-regular-400.svg +0 -801
  96. package/docs/static/bower_components/fontawesome/webfonts/fa-regular-400.woff +0 -0
  97. package/docs/static/bower_components/fontawesome/webfonts/fa-solid-900.eot +0 -0
  98. package/docs/static/bower_components/fontawesome/webfonts/fa-solid-900.svg +0 -5028
  99. package/docs/static/bower_components/fontawesome/webfonts/fa-solid-900.woff +0 -0
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "jqtree",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "Tree widget for jQuery",
5
5
  "keywords": [
6
6
  "jquery-plugin",
7
7
  "tree"
8
8
  ],
9
9
  "license": "Apache-2.0",
10
- "main": "./lib/tree.jquery.js",
10
+ "browser": "./tree.jquery.js",
11
11
  "types": "./src/tree.jquery.d.ts",
12
12
  "repository": {
13
13
  "type": "git",
@@ -23,60 +23,56 @@
23
23
  "devserver-with-coverage": "COVERAGE=true SERVE=true rollup --config config/rollup.config.mjs",
24
24
  "build-with-coverage": "COVERAGE=true rollup --config config/rollup.config.mjs",
25
25
  "prettier": "prettier src/*.ts --write --tab-width 4",
26
- "write-coverage": "nyc report --reporter=text-lcov > lcov.info",
27
26
  "tsc": "tsc --noEmit --project tsconfig.json",
28
- "merge-coverage": "cp jest-coverage/coverage-final.json .nyc_output/coverage_jsdom.json",
29
- "clean-coverage": "rm -rf .nyc_output && jest --clearCache",
30
- "print-coverage": "nyc report",
31
27
  "playwright": "pnpm build-with-coverage && playwright test --config config/playwright.config.js",
32
28
  "test": "pnpm jest && pnpm playwright"
33
29
  },
34
30
  "dependencies": {
35
- "jquery": "^3.6.3"
31
+ "jquery": "^3.7.1"
36
32
  },
37
33
  "devDependencies": {
38
- "@babel/cli": "^7.20.7",
39
- "@babel/core": "^7.20.7",
40
- "@babel/preset-env": "^7.20.2",
41
- "@babel/preset-typescript": "^7.18.6",
42
- "@playwright/test": "^1.29.1",
34
+ "@babel/cli": "^7.22.15",
35
+ "@babel/core": "^7.22.17",
36
+ "@babel/preset-env": "^7.22.15",
37
+ "@babel/preset-typescript": "^7.22.15",
38
+ "@playwright/test": "^1.38.0",
43
39
  "@rollup/plugin-babel": "^6.0.3",
44
- "@rollup/plugin-node-resolve": "^15.0.1",
45
- "@rollup/plugin-terser": "^0.1.0",
46
- "@testing-library/dom": "^8.19.0",
47
- "@types/debug": "^4.1.7",
48
- "@types/jest": "^29.2.4",
49
- "@types/jquery": "^3.5.14",
50
- "@types/node": "^18.11.17",
51
- "@typescript-eslint/eslint-plugin": "^5.47.0",
52
- "@typescript-eslint/parser": "^5.47.0",
53
- "autoprefixer": "^10.4.13",
54
- "babel-jest": "^29.3.1",
40
+ "@rollup/plugin-node-resolve": "^15.2.1",
41
+ "@rollup/plugin-terser": "^0.4.3",
42
+ "@testing-library/dom": "^9.3.1",
43
+ "@types/debug": "^4.1.8",
44
+ "@types/jest": "^29.5.4",
45
+ "@types/jest-axe": "^3.5.5",
46
+ "@types/jquery": "^3.5.18",
47
+ "@types/node": "^20.6.0",
48
+ "@typescript-eslint/eslint-plugin": "^6.7.0",
49
+ "@typescript-eslint/parser": "^6.7.0",
50
+ "autoprefixer": "^10.4.15",
51
+ "babel-jest": "^29.7.0",
55
52
  "babel-plugin-istanbul": "^6.1.1",
56
- "eslint": "^8.30.0",
57
- "eslint-plugin-import": "^2.26.0",
58
- "eslint-plugin-jest": "^27.1.7",
59
- "eslint-plugin-playwright": "^0.11.2",
60
- "eslint-plugin-testing-library": "^5.9.1",
53
+ "eslint": "^8.49.0",
54
+ "eslint-plugin-import": "^2.28.1",
55
+ "eslint-plugin-jest": "^27.2.3",
56
+ "eslint-plugin-playwright": "^0.16.0",
57
+ "eslint-plugin-testing-library": "^6.0.1",
61
58
  "givens": "^1.3.9",
62
- "graphql": "^16.6.0",
63
- "jest": "^29.3.1",
64
- "jest-environment-jsdom": "^29.3.1",
65
- "jest-extended": "^3.2.0",
59
+ "graphql": "^16.8.0",
60
+ "jest": "^29.7.0",
61
+ "jest-axe": "^8.0.0",
62
+ "jest-environment-jsdom": "^29.7.0",
63
+ "jest-extended": "^4.0.1",
66
64
  "jsonfile": "^6.1.0",
67
65
  "lodash.template": "^4.5.0",
68
- "msw": "^0.49.2",
69
- "nyc": "^15.1.0",
70
- "playwright": "^1.29.1",
71
- "postcss": "^8.4.20",
66
+ "msw": "^1.3.1",
67
+ "postcss": "^8.4.29",
72
68
  "postcss-cli": "^10.1.0",
73
69
  "postcss-import": "^15.1.0",
74
70
  "postcss-load-config": "^4.0.1",
75
- "postcss-nested": "^6.0.0",
76
- "prettier": "^2.8.1",
77
- "rollup": "^3.8.1",
71
+ "postcss-nested": "^6.0.1",
72
+ "prettier": "^3.0.3",
73
+ "rollup": "^3.29.1",
78
74
  "rollup-plugin-serve": "^2.0.2",
79
- "tslib": "^2.4.1",
80
- "typescript": "~4.9.4"
75
+ "tslib": "^2.6.2",
76
+ "typescript": "^5.2.2"
81
77
  }
82
78
  }
@@ -261,6 +261,10 @@ export class DragAndDropHandler {
261
261
  const mid = (low + high) >> 1;
262
262
  const area = this.hitAreas[mid];
263
263
 
264
+ if (!area) {
265
+ return null;
266
+ }
267
+
264
268
  if (y < area.top) {
265
269
  high = mid;
266
270
  } else if (y > area.bottom) {
@@ -433,10 +437,18 @@ abstract class VisibleNodeIterator {
433
437
  if (mustIterateInside) {
434
438
  const childrenLength = node.children.length;
435
439
  node.children.forEach((_, i) => {
436
- if (i === childrenLength - 1) {
437
- _iterateNode(node.children[i], null);
438
- } else {
439
- _iterateNode(node.children[i], node.children[i + 1]);
440
+ const child = node.children[i];
441
+
442
+ if (child) {
443
+ if (i === childrenLength - 1) {
444
+ _iterateNode(child, null);
445
+ } else {
446
+ const nextChild = node.children[i + 1];
447
+
448
+ if (nextChild) {
449
+ _iterateNode(child, nextChild);
450
+ }
451
+ }
440
452
  }
441
453
  });
442
454
 
@@ -639,12 +651,14 @@ export class HitAreasGenerator extends VisibleNodeIterator {
639
651
  while (i < positionCount) {
640
652
  const position = positionsInGroup[i];
641
653
 
642
- hitAreas.push({
643
- top: areaTop,
644
- bottom: areaTop + areaHeight,
645
- node: position.node,
646
- position: position.position,
647
- });
654
+ if (position) {
655
+ hitAreas.push({
656
+ top: areaTop,
657
+ bottom: areaTop + areaHeight,
658
+ node: position.node,
659
+ position: position.position,
660
+ });
661
+ }
648
662
 
649
663
  areaTop += areaHeight;
650
664
  i += 1;
@@ -2,21 +2,21 @@ import { getBoolString } from "./util";
2
2
  import { Node } from "./node";
3
3
  import { JqTreeWidget } from "./tree.jquery";
4
4
 
5
- type IconElement = Text | Element;
5
+ type IconElement = string | HTMLElement | JQuery<HTMLElement>;
6
6
 
7
7
  export default class ElementsRenderer {
8
- public openedIconElement: IconElement;
9
- public closedIconElement: IconElement;
8
+ public openedIconElement?: HTMLElement | Text;
9
+ public closedIconElement?: HTMLElement | Text;
10
10
  private treeWidget: JqTreeWidget;
11
11
 
12
12
  constructor(treeWidget: JqTreeWidget) {
13
13
  this.treeWidget = treeWidget;
14
14
 
15
15
  this.openedIconElement = this.createButtonElement(
16
- treeWidget.options.openedIcon || "+"
16
+ treeWidget.options.openedIcon || "+",
17
17
  );
18
18
  this.closedIconElement = this.createButtonElement(
19
- treeWidget.options.closedIcon || "-"
19
+ treeWidget.options.closedIcon || "-",
20
20
  );
21
21
  }
22
22
 
@@ -32,12 +32,14 @@ export default class ElementsRenderer {
32
32
  const $element = this.treeWidget.element;
33
33
  $element.empty();
34
34
 
35
- this.createDomElements(
36
- $element[0],
37
- this.treeWidget.tree.children,
38
- true,
39
- 1
40
- );
35
+ if ($element[0]) {
36
+ this.createDomElements(
37
+ $element[0],
38
+ this.treeWidget.tree.children,
39
+ true,
40
+ 1,
41
+ );
42
+ }
41
43
  }
42
44
 
43
45
  public renderFromNode(node: Node): void {
@@ -60,7 +62,7 @@ export default class ElementsRenderer {
60
62
  li,
61
63
  node.children,
62
64
  false,
63
- node.getLevel() + 1
65
+ node.getLevel() + 1,
64
66
  );
65
67
  }
66
68
  }
@@ -69,7 +71,7 @@ export default class ElementsRenderer {
69
71
  element: Element,
70
72
  children: Node[],
71
73
  isRootNode: boolean,
72
- level: number
74
+ level: number,
73
75
  ): void {
74
76
  const ul = this.createUl(isRootNode);
75
77
  element.appendChild(ul);
@@ -121,7 +123,7 @@ export default class ElementsRenderer {
121
123
 
122
124
  private createLi(node: Node, level: number): HTMLLIElement {
123
125
  const isSelected = Boolean(
124
- this.treeWidget.selectNodeHandler.isNodeSelected(node)
126
+ this.treeWidget.selectNodeHandler.isNodeSelected(node),
125
127
  );
126
128
 
127
129
  const mustShowFolder =
@@ -139,10 +141,22 @@ export default class ElementsRenderer {
139
141
  return li;
140
142
  }
141
143
 
144
+ private setTreeItemAriaAttributes(
145
+ element: HTMLElement,
146
+ name: string,
147
+ level: number,
148
+ isSelected: boolean,
149
+ ) {
150
+ element.setAttribute("aria-label", name);
151
+ element.setAttribute("aria-level", `${level}`);
152
+ element.setAttribute("aria-selected", getBoolString(isSelected));
153
+ element.setAttribute("role", "treeitem");
154
+ }
155
+
142
156
  private createFolderLi(
143
157
  node: Node,
144
158
  level: number,
145
- isSelected: boolean
159
+ isSelected: boolean,
146
160
  ): HTMLLIElement {
147
161
  const buttonClasses = this.getButtonClasses(node);
148
162
  const folderClasses = this.getFolderClasses(node, isSelected);
@@ -154,12 +168,12 @@ export default class ElementsRenderer {
154
168
  // li
155
169
  const li = document.createElement("li");
156
170
  li.className = `jqtree_common ${folderClasses}`;
157
- li.setAttribute("role", "presentation");
171
+ li.setAttribute("role", "none");
158
172
 
159
173
  // div
160
174
  const div = document.createElement("div");
161
175
  div.className = "jqtree-element jqtree_common";
162
- div.setAttribute("role", "presentation");
176
+ div.setAttribute("role", "none");
163
177
 
164
178
  li.appendChild(div);
165
179
 
@@ -167,25 +181,23 @@ export default class ElementsRenderer {
167
181
  const buttonLink = document.createElement("a");
168
182
  buttonLink.className = buttonClasses;
169
183
 
170
- buttonLink.appendChild(iconElement.cloneNode(true));
171
-
172
- buttonLink.setAttribute("role", "presentation");
173
- buttonLink.setAttribute("aria-hidden", "true");
184
+ if (iconElement) {
185
+ buttonLink.appendChild(iconElement.cloneNode(true));
186
+ }
174
187
 
175
188
  if (this.treeWidget.options.buttonLeft) {
176
189
  div.appendChild(buttonLink);
177
190
  }
178
191
 
179
192
  // title span
180
- div.appendChild(
181
- this.createTitleSpan(
182
- node.name,
183
- level,
184
- isSelected,
185
- node.is_open,
186
- true
187
- )
193
+ const titleSpan = this.createTitleSpan(
194
+ node.name,
195
+ isSelected,
196
+ true,
197
+ level,
188
198
  );
199
+ titleSpan.setAttribute("aria-expanded", getBoolString(node.is_open));
200
+ div.appendChild(titleSpan);
189
201
 
190
202
  if (!this.treeWidget.options.buttonLeft) {
191
203
  div.appendChild(buttonLink);
@@ -197,7 +209,7 @@ export default class ElementsRenderer {
197
209
  private createNodeLi(
198
210
  node: Node,
199
211
  level: number,
200
- isSelected: boolean
212
+ isSelected: boolean,
201
213
  ): HTMLLIElement {
202
214
  const liClasses = ["jqtree_common"];
203
215
 
@@ -210,35 +222,32 @@ export default class ElementsRenderer {
210
222
  // li
211
223
  const li = document.createElement("li");
212
224
  li.className = classString;
213
- li.setAttribute("role", "presentation");
225
+ li.setAttribute("role", "none");
214
226
 
215
227
  // div
216
228
  const div = document.createElement("div");
217
229
  div.className = "jqtree-element jqtree_common";
218
- div.setAttribute("role", "presentation");
230
+ div.setAttribute("role", "none");
219
231
 
220
232
  li.appendChild(div);
221
233
 
222
234
  // title span
223
- div.appendChild(
224
- this.createTitleSpan(
225
- node.name,
226
- level,
227
- isSelected,
228
- node.is_open,
229
- false
230
- )
235
+ const titleSpan = this.createTitleSpan(
236
+ node.name,
237
+ isSelected,
238
+ false,
239
+ level,
231
240
  );
241
+ div.appendChild(titleSpan);
232
242
 
233
243
  return li;
234
244
  }
235
245
 
236
246
  private createTitleSpan(
237
247
  nodeName: string,
238
- level: number,
239
248
  isSelected: boolean,
240
- isOpen: boolean,
241
- isFolder: boolean
249
+ isFolder: boolean,
250
+ level: number,
242
251
  ): HTMLSpanElement {
243
252
  const titleSpan = document.createElement("span");
244
253
 
@@ -254,12 +263,6 @@ export default class ElementsRenderer {
254
263
 
255
264
  titleSpan.className = classes;
256
265
 
257
- titleSpan.setAttribute("role", "treeitem");
258
- titleSpan.setAttribute("aria-level", `${level}`);
259
-
260
- titleSpan.setAttribute("aria-selected", getBoolString(isSelected));
261
- titleSpan.setAttribute("aria-expanded", getBoolString(isOpen));
262
-
263
266
  if (isSelected) {
264
267
  const tabIndex = this.treeWidget.options.tabIndex;
265
268
 
@@ -268,6 +271,8 @@ export default class ElementsRenderer {
268
271
  }
269
272
  }
270
273
 
274
+ this.setTreeItemAriaAttributes(titleSpan, nodeName, level, isSelected);
275
+
271
276
  if (this.treeWidget.options.autoEscape) {
272
277
  titleSpan.textContent = nodeName;
273
278
  } else {
@@ -311,7 +316,9 @@ export default class ElementsRenderer {
311
316
  return classes.join(" ");
312
317
  }
313
318
 
314
- private createButtonElement(value: string | Element): IconElement {
319
+ private createButtonElement(
320
+ value: IconElement,
321
+ ): HTMLElement | Text | undefined {
315
322
  if (typeof value === "string") {
316
323
  // convert value to html
317
324
  const div = document.createElement("div");
@@ -3,7 +3,7 @@ import { Node } from "./node";
3
3
  type CanMoveNodeTo = (
4
4
  node: Node,
5
5
  targetNode: Node,
6
- positionName: string
6
+ positionName: string,
7
7
  ) => boolean;
8
8
  type CreateLi = (node: Node, el: JQuery, isSelected: boolean) => void;
9
9
  type DataFilter = (data: unknown) => NodeData[];
@@ -13,15 +13,15 @@ type DragMethod = (node: Node, event: Event | Touch) => void;
13
13
  type HandleLoadingMethod = (
14
14
  isLoading: boolean,
15
15
  node: Node | null,
16
- $el: JQuery
16
+ $el: JQuery,
17
17
  ) => void;
18
18
 
19
19
  export interface JQTreeOptions {
20
- animationSpeed: string | number;
20
+ animationSpeed: JQuery.Duration;
21
21
  autoEscape: boolean;
22
22
  autoOpen: boolean | number;
23
23
  buttonLeft: boolean;
24
- closedIcon: string | Element | undefined;
24
+ closedIcon?: string | HTMLElement | JQuery<HTMLElement>;
25
25
  data: NodeData[] | undefined;
26
26
  dataFilter: DataFilter | undefined;
27
27
  dataUrl: DataUrl | undefined;
@@ -39,7 +39,7 @@ export interface JQTreeOptions {
39
39
  onLoadFailed: ((response: JQuery.jqXHR) => void) | undefined;
40
40
  onLoading: HandleLoadingMethod | undefined;
41
41
  onSetStateFromStorage: ((data: string) => void) | undefined;
42
- openedIcon: string | Element;
42
+ openedIcon?: string | HTMLElement | JQuery<HTMLElement>;
43
43
  openFolderDelay: number | false;
44
44
  rtl: boolean | undefined;
45
45
  selectable: boolean;
@@ -219,6 +219,10 @@ abstract class MouseWidget<WidgetOptions> extends SimpleWidget<WidgetOptions> {
219
219
 
220
220
  const touch = e.changedTouches[0];
221
221
 
222
+ if (!touch) {
223
+ return;
224
+ }
225
+
222
226
  this.handleMouseDown(getPositionInfoFromTouch(touch, e));
223
227
  };
224
228
 
@@ -233,6 +237,10 @@ abstract class MouseWidget<WidgetOptions> extends SimpleWidget<WidgetOptions> {
233
237
 
234
238
  const touch = e.changedTouches[0];
235
239
 
240
+ if (!touch) {
241
+ return;
242
+ }
243
+
236
244
  this.handleMouseMove(e, getPositionInfoFromTouch(touch, e));
237
245
  };
238
246
 
@@ -247,6 +255,10 @@ abstract class MouseWidget<WidgetOptions> extends SimpleWidget<WidgetOptions> {
247
255
 
248
256
  const touch = e.changedTouches[0];
249
257
 
258
+ if (!touch) {
259
+ return;
260
+ }
261
+
250
262
  this.handleMouseUp(getPositionInfoFromTouch(touch, e));
251
263
  };
252
264
  }
package/src/node.ts CHANGED
@@ -518,7 +518,7 @@ export class Node implements INode {
518
518
  } else {
519
519
  const previousIndex = this.parent.getChildIndex(this) - 1;
520
520
  if (previousIndex >= 0) {
521
- return this.parent.children[previousIndex];
521
+ return this.parent.children[previousIndex] || null;
522
522
  } else {
523
523
  return null;
524
524
  }
@@ -531,7 +531,7 @@ export class Node implements INode {
531
531
  } else {
532
532
  const nextIndex = this.parent.getChildIndex(this) + 1;
533
533
  if (nextIndex < this.parent.children.length) {
534
- return this.parent.children[nextIndex];
534
+ return this.parent.children[nextIndex] || null;
535
535
  } else {
536
536
  return null;
537
537
  }
@@ -558,7 +558,7 @@ export class Node implements INode {
558
558
 
559
559
  public getNextNode(includeChildren = true): Node | null {
560
560
  if (includeChildren && this.hasChildren()) {
561
- return this.children[0];
561
+ return this.children[0] || null;
562
562
  } else if (!this.parent) {
563
563
  return null;
564
564
  } else {
@@ -575,7 +575,7 @@ export class Node implements INode {
575
575
  public getNextVisibleNode(): Node | null {
576
576
  if (this.hasChildren() && this.is_open) {
577
577
  // First child
578
- return this.children[0];
578
+ return this.children[0] || null;
579
579
  } else {
580
580
  if (!this.parent) {
581
581
  return null;
@@ -646,10 +646,15 @@ export class Node implements INode {
646
646
  return null;
647
647
  } else {
648
648
  const lastChild = this.children[this.children.length - 1];
649
+
650
+ if (!lastChild) {
651
+ return null;
652
+ }
653
+
649
654
  if (!(lastChild.hasChildren() && lastChild.is_open)) {
650
655
  return lastChild;
651
656
  } else {
652
- return lastChild.getLastChild();
657
+ return lastChild?.getLastChild();
653
658
  }
654
659
  }
655
660
  }
@@ -34,7 +34,7 @@ export class NodeElement {
34
34
  if (this.mustShowBorderDropHint(position)) {
35
35
  return new BorderDropHint(
36
36
  this.$element,
37
- this.treeWidget._getScrollLeft()
37
+ this.treeWidget._getScrollLeft(),
38
38
  );
39
39
  } else {
40
40
  return new GhostDropHint(this.node, this.$element, position);
@@ -45,10 +45,10 @@ export class NodeElement {
45
45
  const $li = this.getLi();
46
46
 
47
47
  $li.addClass("jqtree-selected");
48
- $li.attr("aria-selected", "true");
49
48
 
50
49
  const $span = this.getSpan();
51
50
  $span.attr("tabindex", this.treeWidget.options.tabIndex ?? null);
51
+ $span.attr("aria-selected", "true");
52
52
 
53
53
  if (mustSetFocus) {
54
54
  $span.trigger("focus");
@@ -59,12 +59,12 @@ export class NodeElement {
59
59
  const $li = this.getLi();
60
60
 
61
61
  $li.removeClass("jqtree-selected");
62
- $li.attr("aria-selected", "false");
63
62
 
64
63
  const $span = this.getSpan();
65
64
  $span.removeAttr("tabindex");
65
+ $span.attr("aria-selected", "false");
66
66
 
67
- $span.blur();
67
+ $span.trigger("blur");
68
68
  }
69
69
 
70
70
  protected getUl(): JQuery<HTMLElement> {
@@ -81,7 +81,7 @@ export class NodeElement {
81
81
  return this.$element;
82
82
  }
83
83
 
84
- protected mustShowBorderDropHint(position: number): boolean {
84
+ protected mustShowBorderDropHint(position: Position): boolean {
85
85
  return position === Position.Inside;
86
86
  }
87
87
  }
@@ -90,7 +90,7 @@ export class FolderElement extends NodeElement {
90
90
  public open(
91
91
  onFinished: OnFinishOpenNode | null,
92
92
  slide = true,
93
- animationSpeed: JQuery.Duration | string = "fast"
93
+ animationSpeed: JQuery.Duration = "fast",
94
94
  ): void {
95
95
  if (this.node.is_open) {
96
96
  return;
@@ -105,18 +105,21 @@ export class FolderElement extends NodeElement {
105
105
  const buttonEl = $button.get(0);
106
106
 
107
107
  if (buttonEl) {
108
- const icon =
109
- this.treeWidget.renderer.openedIconElement.cloneNode(true);
108
+ const openedIconElement =
109
+ this.treeWidget.renderer.openedIconElement;
110
110
 
111
- buttonEl.appendChild(icon);
111
+ if (openedIconElement) {
112
+ const icon = openedIconElement.cloneNode(true);
113
+ buttonEl.appendChild(icon);
114
+ }
112
115
  }
113
116
 
114
117
  const doOpen = (): void => {
115
118
  const $li = this.getLi();
116
119
  $li.removeClass("jqtree-closed");
117
120
 
118
- const $span = this.getSpan();
119
- $span.attr("aria-expanded", "true");
121
+ const $titleSpan = this.getSpan();
122
+ $titleSpan.attr("aria-expanded", "true");
120
123
 
121
124
  if (onFinished) {
122
125
  onFinished(this.node);
@@ -137,7 +140,7 @@ export class FolderElement extends NodeElement {
137
140
 
138
141
  public close(
139
142
  slide = true,
140
- animationSpeed: JQuery.Duration | string = "fast"
143
+ animationSpeed: JQuery.Duration | undefined = "fast",
141
144
  ): void {
142
145
  if (!this.node.is_open) {
143
146
  return;
@@ -152,18 +155,21 @@ export class FolderElement extends NodeElement {
152
155
  const buttonEl = $button.get(0);
153
156
 
154
157
  if (buttonEl) {
155
- const icon =
156
- this.treeWidget.renderer.closedIconElement.cloneNode(true);
158
+ const closedIconElement =
159
+ this.treeWidget.renderer.closedIconElement;
157
160
 
158
- buttonEl.appendChild(icon);
161
+ if (closedIconElement) {
162
+ const icon = closedIconElement.cloneNode(true);
163
+ buttonEl.appendChild(icon);
164
+ }
159
165
  }
160
166
 
161
167
  const doClose = (): void => {
162
168
  const $li = this.getLi();
163
169
  $li.addClass("jqtree-closed");
164
170
 
165
- const $span = this.getSpan();
166
- $span.attr("aria-expanded", "false");
171
+ const $titleSpan = this.getSpan();
172
+ $titleSpan.attr("aria-expanded", "false");
167
173
 
168
174
  this.treeWidget._triggerEvent("tree.close", {
169
175
  node: this.node,
@@ -178,7 +184,7 @@ export class FolderElement extends NodeElement {
178
184
  }
179
185
  }
180
186
 
181
- protected mustShowBorderDropHint(position: number): boolean {
187
+ protected mustShowBorderDropHint(position: Position): boolean {
182
188
  return !this.node.is_open && position === Position.Inside;
183
189
  }
184
190
 
@@ -217,13 +223,13 @@ class GhostDropHint implements DropHint {
217
223
  private node: Node;
218
224
  private $ghost: JQuery;
219
225
 
220
- constructor(node: Node, $element: JQuery<Element>, position: number) {
226
+ constructor(node: Node, $element: JQuery<Element>, position: Position) {
221
227
  this.$element = $element;
222
228
 
223
229
  this.node = node;
224
230
  this.$ghost = jQuery(
225
231
  `<li class="jqtree_common jqtree-ghost"><span class="jqtree_common jqtree-circle"></span>
226
- <span class="jqtree_common jqtree-line"></span></li>`
232
+ <span class="jqtree_common jqtree-line"></span></li>`,
227
233
  );
228
234
 
229
235
  if (position === Position.After) {
@@ -252,7 +258,7 @@ class GhostDropHint implements DropHint {
252
258
  }
253
259
 
254
260
  public moveInsideOpenFolder(): void {
255
- const childElement = this.node.children[0].element;
261
+ const childElement = this.node.children[0]?.element;
256
262
 
257
263
  if (childElement) {
258
264
  jQuery(childElement).before(this.$ghost);