3258e2c6493fbe592a1a0cd3ca1ac25c127ba331124edd8404ba3f5e2a92e70964f57f3401c3f8478c1b1f7d4606eb7bd39859f33f9cd4de0ebda8d29c5ab2 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import { TreeError } from './tree.js';
  6. import { splice, tail2 } from '../../../common/arrays.js';
  7. import { Delayer, MicrotaskDelay } from '../../../common/async.js';
  8. import { LcsDiff } from '../../../common/diff/diff.js';
  9. import { Emitter, EventBufferer } from '../../../common/event.js';
  10. import { Iterable } from '../../../common/iterator.js';
  11. export function isFilterResult(obj) {
  12. return typeof obj === 'object' && 'visibility' in obj && 'data' in obj;
  13. }
  14. export function getVisibleState(visibility) {
  15. switch (visibility) {
  16. case true: return 1 /* TreeVisibility.Visible */;
  17. case false: return 0 /* TreeVisibility.Hidden */;
  18. default: return visibility;
  19. }
  20. }
  21. function isCollapsibleStateUpdate(update) {
  22. return typeof update.collapsible === 'boolean';
  23. }
  24. export class IndexTreeModel {
  25. constructor(user, list, rootElement, options = {}) {
  26. this.user = user;
  27. this.list = list;
  28. this.rootRef = [];
  29. this.eventBufferer = new EventBufferer();
  30. this._onDidChangeCollapseState = new Emitter();
  31. this.onDidChangeCollapseState = this.eventBufferer.wrapEvent(this._onDidChangeCollapseState.event);
  32. this._onDidChangeRenderNodeCount = new Emitter();
  33. this.onDidChangeRenderNodeCount = this.eventBufferer.wrapEvent(this._onDidChangeRenderNodeCount.event);
  34. this._onDidSplice = new Emitter();
  35. this.onDidSplice = this._onDidSplice.event;
  36. this.refilterDelayer = new Delayer(MicrotaskDelay);
  37. this.collapseByDefault = typeof options.collapseByDefault === 'undefined' ? false : options.collapseByDefault;
  38. this.filter = options.filter;
  39. this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren;
  40. this.root = {
  41. parent: undefined,
  42. element: rootElement,
  43. children: [],
  44. depth: 0,
  45. visibleChildrenCount: 0,
  46. visibleChildIndex: -1,
  47. collapsible: false,
  48. collapsed: false,
  49. renderNodeCount: 0,
  50. visibility: 1 /* TreeVisibility.Visible */,
  51. visible: true,
  52. filterData: undefined
  53. };
  54. }
  55. splice(location, deleteCount, toInsert = Iterable.empty(), options = {}) {
  56. if (location.length === 0) {
  57. throw new TreeError(this.user, 'Invalid tree location');
  58. }
  59. if (options.diffIdentityProvider) {
  60. this.spliceSmart(options.diffIdentityProvider, location, deleteCount, toInsert, options);
  61. }
  62. else {
  63. this.spliceSimple(location, deleteCount, toInsert, options);
  64. }
  65. }
  66. spliceSmart(identity, location, deleteCount, toInsertIterable, options, recurseLevels) {
  67. var _a;
  68. if (toInsertIterable === void 0) { toInsertIterable = Iterable.empty(); }
  69. if (recurseLevels === void 0) { recurseLevels = (_a = options.diffDepth) !== null && _a !== void 0 ? _a : 0; }
  70. const { parentNode } = this.getParentNodeWithListIndex(location);
  71. if (!parentNode.lastDiffIds) {
  72. return this.spliceSimple(location, deleteCount, toInsertIterable, options);
  73. }
  74. const toInsert = [...toInsertIterable];
  75. const index = location[location.length - 1];
  76. const diff = new LcsDiff({ getElements: () => parentNode.lastDiffIds }, {
  77. getElements: () => [
  78. ...parentNode.children.slice(0, index),
  79. ...toInsert,
  80. ...parentNode.children.slice(index + deleteCount),
  81. ].map(e => identity.getId(e.element).toString())
  82. }).ComputeDiff(false);
  83. // if we were given a 'best effort' diff, use default behavior
  84. if (diff.quitEarly) {
  85. parentNode.lastDiffIds = undefined;
  86. return this.spliceSimple(location, deleteCount, toInsert, options);
  87. }
  88. const locationPrefix = location.slice(0, -1);
  89. const recurseSplice = (fromOriginal, fromModified, count) => {
  90. if (recurseLevels > 0) {
  91. for (let i = 0; i < count; i++) {
  92. fromOriginal--;
  93. fromModified--;
  94. this.spliceSmart(identity, [...locationPrefix, fromOriginal, 0], Number.MAX_SAFE_INTEGER, toInsert[fromModified].children, options, recurseLevels - 1);
  95. }
  96. }
  97. };
  98. let lastStartO = Math.min(parentNode.children.length, index + deleteCount);
  99. let lastStartM = toInsert.length;
  100. for (const change of diff.changes.sort((a, b) => b.originalStart - a.originalStart)) {
  101. recurseSplice(lastStartO, lastStartM, lastStartO - (change.originalStart + change.originalLength));
  102. lastStartO = change.originalStart;
  103. lastStartM = change.modifiedStart - index;
  104. this.spliceSimple([...locationPrefix, lastStartO], change.originalLength, Iterable.slice(toInsert, lastStartM, lastStartM + change.modifiedLength), options);
  105. }
  106. // at this point, startO === startM === count since any remaining prefix should match
  107. recurseSplice(lastStartO, lastStartM, lastStartO);
  108. }
  109. spliceSimple(location, deleteCount, toInsert = Iterable.empty(), { onDidCreateNode, onDidDeleteNode, diffIdentityProvider }) {
  110. const { parentNode, listIndex, revealed, visible } = this.getParentNodeWithListIndex(location);
  111. const treeListElementsToInsert = [];
  112. const nodesToInsertIterator = Iterable.map(toInsert, el => this.createTreeNode(el, parentNode, parentNode.visible ? 1 /* TreeVisibility.Visible */ : 0 /* TreeVisibility.Hidden */, revealed, treeListElementsToInsert, onDidCreateNode));
  113. const lastIndex = location[location.length - 1];
  114. const lastHadChildren = parentNode.children.length > 0;
  115. // figure out what's the visible child start index right before the
  116. // splice point
  117. let visibleChildStartIndex = 0;
  118. for (let i = lastIndex; i >= 0 && i < parentNode.children.length; i--) {
  119. const child = parentNode.children[i];
  120. if (child.visible) {
  121. visibleChildStartIndex = child.visibleChildIndex;
  122. break;
  123. }
  124. }
  125. const nodesToInsert = [];
  126. let insertedVisibleChildrenCount = 0;
  127. let renderNodeCount = 0;
  128. for (const child of nodesToInsertIterator) {
  129. nodesToInsert.push(child);
  130. renderNodeCount += child.renderNodeCount;
  131. if (child.visible) {
  132. child.visibleChildIndex = visibleChildStartIndex + insertedVisibleChildrenCount++;
  133. }
  134. }
  135. const deletedNodes = splice(parentNode.children, lastIndex, deleteCount, nodesToInsert);
  136. if (!diffIdentityProvider) {
  137. parentNode.lastDiffIds = undefined;
  138. }
  139. else if (parentNode.lastDiffIds) {
  140. splice(parentNode.lastDiffIds, lastIndex, deleteCount, nodesToInsert.map(n => diffIdentityProvider.getId(n.element).toString()));
  141. }
  142. else {
  143. parentNode.lastDiffIds = parentNode.children.map(n => diffIdentityProvider.getId(n.element).toString());
  144. }
  145. // figure out what is the count of deleted visible children
  146. let deletedVisibleChildrenCount = 0;
  147. for (const child of deletedNodes) {
  148. if (child.visible) {
  149. deletedVisibleChildrenCount++;
  150. }
  151. }
  152. // and adjust for all visible children after the splice point
  153. if (deletedVisibleChildrenCount !== 0) {
  154. for (let i = lastIndex + nodesToInsert.length; i < parentNode.children.length; i++) {
  155. const child = parentNode.children[i];
  156. if (child.visible) {
  157. child.visibleChildIndex -= deletedVisibleChildrenCount;
  158. }
  159. }
  160. }
  161. // update parent's visible children count
  162. parentNode.visibleChildrenCount += insertedVisibleChildrenCount - deletedVisibleChildrenCount;
  163. if (revealed && visible) {
  164. const visibleDeleteCount = deletedNodes.reduce((r, node) => r + (node.visible ? node.renderNodeCount : 0), 0);
  165. this._updateAncestorsRenderNodeCount(parentNode, renderNodeCount - visibleDeleteCount);
  166. this.list.splice(listIndex, visibleDeleteCount, treeListElementsToInsert);
  167. }
  168. if (deletedNodes.length > 0 && onDidDeleteNode) {
  169. const visit = (node) => {
  170. onDidDeleteNode(node);
  171. node.children.forEach(visit);
  172. };
  173. deletedNodes.forEach(visit);
  174. }
  175. this._onDidSplice.fire({ insertedNodes: nodesToInsert, deletedNodes });
  176. const currentlyHasChildren = parentNode.children.length > 0;
  177. if (lastHadChildren !== currentlyHasChildren) {
  178. this.setCollapsible(location.slice(0, -1), currentlyHasChildren);
  179. }
  180. let node = parentNode;
  181. while (node) {
  182. if (node.visibility === 2 /* TreeVisibility.Recurse */) {
  183. // delayed to avoid excessive refiltering, see #135941
  184. this.refilterDelayer.trigger(() => this.refilter());
  185. break;
  186. }
  187. node = node.parent;
  188. }
  189. }
  190. rerender(location) {
  191. if (location.length === 0) {
  192. throw new TreeError(this.user, 'Invalid tree location');
  193. }
  194. const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location);
  195. if (node.visible && revealed) {
  196. this.list.splice(listIndex, 1, [node]);
  197. }
  198. }
  199. has(location) {
  200. return this.hasTreeNode(location);
  201. }
  202. getListIndex(location) {
  203. const { listIndex, visible, revealed } = this.getTreeNodeWithListIndex(location);
  204. return visible && revealed ? listIndex : -1;
  205. }
  206. getListRenderCount(location) {
  207. return this.getTreeNode(location).renderNodeCount;
  208. }
  209. isCollapsible(location) {
  210. return this.getTreeNode(location).collapsible;
  211. }
  212. setCollapsible(location, collapsible) {
  213. const node = this.getTreeNode(location);
  214. if (typeof collapsible === 'undefined') {
  215. collapsible = !node.collapsible;
  216. }
  217. const update = { collapsible };
  218. return this.eventBufferer.bufferEvents(() => this._setCollapseState(location, update));
  219. }
  220. isCollapsed(location) {
  221. return this.getTreeNode(location).collapsed;
  222. }
  223. setCollapsed(location, collapsed, recursive) {
  224. const node = this.getTreeNode(location);
  225. if (typeof collapsed === 'undefined') {
  226. collapsed = !node.collapsed;
  227. }
  228. const update = { collapsed, recursive: recursive || false };
  229. return this.eventBufferer.bufferEvents(() => this._setCollapseState(location, update));
  230. }
  231. _setCollapseState(location, update) {
  232. const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location);
  233. const result = this._setListNodeCollapseState(node, listIndex, revealed, update);
  234. if (node !== this.root && this.autoExpandSingleChildren && result && !isCollapsibleStateUpdate(update) && node.collapsible && !node.collapsed && !update.recursive) {
  235. let onlyVisibleChildIndex = -1;
  236. for (let i = 0; i < node.children.length; i++) {
  237. const child = node.children[i];
  238. if (child.visible) {
  239. if (onlyVisibleChildIndex > -1) {
  240. onlyVisibleChildIndex = -1;
  241. break;
  242. }
  243. else {
  244. onlyVisibleChildIndex = i;
  245. }
  246. }
  247. }
  248. if (onlyVisibleChildIndex > -1) {
  249. this._setCollapseState([...location, onlyVisibleChildIndex], update);
  250. }
  251. }
  252. return result;
  253. }
  254. _setListNodeCollapseState(node, listIndex, revealed, update) {
  255. const result = this._setNodeCollapseState(node, update, false);
  256. if (!revealed || !node.visible || !result) {
  257. return result;
  258. }
  259. const previousRenderNodeCount = node.renderNodeCount;
  260. const toInsert = this.updateNodeAfterCollapseChange(node);
  261. const deleteCount = previousRenderNodeCount - (listIndex === -1 ? 0 : 1);
  262. this.list.splice(listIndex + 1, deleteCount, toInsert.slice(1));
  263. return result;
  264. }
  265. _setNodeCollapseState(node, update, deep) {
  266. let result;
  267. if (node === this.root) {
  268. result = false;
  269. }
  270. else {
  271. if (isCollapsibleStateUpdate(update)) {
  272. result = node.collapsible !== update.collapsible;
  273. node.collapsible = update.collapsible;
  274. }
  275. else if (!node.collapsible) {
  276. result = false;
  277. }
  278. else {
  279. result = node.collapsed !== update.collapsed;
  280. node.collapsed = update.collapsed;
  281. }
  282. if (result) {
  283. this._onDidChangeCollapseState.fire({ node, deep });
  284. }
  285. }
  286. if (!isCollapsibleStateUpdate(update) && update.recursive) {
  287. for (const child of node.children) {
  288. result = this._setNodeCollapseState(child, update, true) || result;
  289. }
  290. }
  291. return result;
  292. }
  293. expandTo(location) {
  294. this.eventBufferer.bufferEvents(() => {
  295. let node = this.getTreeNode(location);
  296. while (node.parent) {
  297. node = node.parent;
  298. location = location.slice(0, location.length - 1);
  299. if (node.collapsed) {
  300. this._setCollapseState(location, { collapsed: false, recursive: false });
  301. }
  302. }
  303. });
  304. }
  305. refilter() {
  306. const previousRenderNodeCount = this.root.renderNodeCount;
  307. const toInsert = this.updateNodeAfterFilterChange(this.root);
  308. this.list.splice(0, previousRenderNodeCount, toInsert);
  309. this.refilterDelayer.cancel();
  310. }
  311. createTreeNode(treeElement, parent, parentVisibility, revealed, treeListElements, onDidCreateNode) {
  312. const node = {
  313. parent,
  314. element: treeElement.element,
  315. children: [],
  316. depth: parent.depth + 1,
  317. visibleChildrenCount: 0,
  318. visibleChildIndex: -1,
  319. collapsible: typeof treeElement.collapsible === 'boolean' ? treeElement.collapsible : (typeof treeElement.collapsed !== 'undefined'),
  320. collapsed: typeof treeElement.collapsed === 'undefined' ? this.collapseByDefault : treeElement.collapsed,
  321. renderNodeCount: 1,
  322. visibility: 1 /* TreeVisibility.Visible */,
  323. visible: true,
  324. filterData: undefined
  325. };
  326. const visibility = this._filterNode(node, parentVisibility);
  327. node.visibility = visibility;
  328. if (revealed) {
  329. treeListElements.push(node);
  330. }
  331. const childElements = treeElement.children || Iterable.empty();
  332. const childRevealed = revealed && visibility !== 0 /* TreeVisibility.Hidden */ && !node.collapsed;
  333. const childNodes = Iterable.map(childElements, el => this.createTreeNode(el, node, visibility, childRevealed, treeListElements, onDidCreateNode));
  334. let visibleChildrenCount = 0;
  335. let renderNodeCount = 1;
  336. for (const child of childNodes) {
  337. node.children.push(child);
  338. renderNodeCount += child.renderNodeCount;
  339. if (child.visible) {
  340. child.visibleChildIndex = visibleChildrenCount++;
  341. }
  342. }
  343. node.collapsible = node.collapsible || node.children.length > 0;
  344. node.visibleChildrenCount = visibleChildrenCount;
  345. node.visible = visibility === 2 /* TreeVisibility.Recurse */ ? visibleChildrenCount > 0 : (visibility === 1 /* TreeVisibility.Visible */);
  346. if (!node.visible) {
  347. node.renderNodeCount = 0;
  348. if (revealed) {
  349. treeListElements.pop();
  350. }
  351. }
  352. else if (!node.collapsed) {
  353. node.renderNodeCount = renderNodeCount;
  354. }
  355. onDidCreateNode === null || onDidCreateNode === void 0 ? void 0 : onDidCreateNode(node);
  356. return node;
  357. }
  358. updateNodeAfterCollapseChange(node) {
  359. const previousRenderNodeCount = node.renderNodeCount;
  360. const result = [];
  361. this._updateNodeAfterCollapseChange(node, result);
  362. this._updateAncestorsRenderNodeCount(node.parent, result.length - previousRenderNodeCount);
  363. return result;
  364. }
  365. _updateNodeAfterCollapseChange(node, result) {
  366. if (node.visible === false) {
  367. return 0;
  368. }
  369. result.push(node);
  370. node.renderNodeCount = 1;
  371. if (!node.collapsed) {
  372. for (const child of node.children) {
  373. node.renderNodeCount += this._updateNodeAfterCollapseChange(child, result);
  374. }
  375. }
  376. this._onDidChangeRenderNodeCount.fire(node);
  377. return node.renderNodeCount;
  378. }
  379. updateNodeAfterFilterChange(node) {
  380. const previousRenderNodeCount = node.renderNodeCount;
  381. const result = [];
  382. this._updateNodeAfterFilterChange(node, node.visible ? 1 /* TreeVisibility.Visible */ : 0 /* TreeVisibility.Hidden */, result);
  383. this._updateAncestorsRenderNodeCount(node.parent, result.length - previousRenderNodeCount);
  384. return result;
  385. }
  386. _updateNodeAfterFilterChange(node, parentVisibility, result, revealed = true) {
  387. let visibility;
  388. if (node !== this.root) {
  389. visibility = this._filterNode(node, parentVisibility);
  390. if (visibility === 0 /* TreeVisibility.Hidden */) {
  391. node.visible = false;
  392. node.renderNodeCount = 0;
  393. return false;
  394. }
  395. if (revealed) {
  396. result.push(node);
  397. }
  398. }
  399. const resultStartLength = result.length;
  400. node.renderNodeCount = node === this.root ? 0 : 1;
  401. let hasVisibleDescendants = false;
  402. if (!node.collapsed || visibility !== 0 /* TreeVisibility.Hidden */) {
  403. let visibleChildIndex = 0;
  404. for (const child of node.children) {
  405. hasVisibleDescendants = this._updateNodeAfterFilterChange(child, visibility, result, revealed && !node.collapsed) || hasVisibleDescendants;
  406. if (child.visible) {
  407. child.visibleChildIndex = visibleChildIndex++;
  408. }
  409. }
  410. node.visibleChildrenCount = visibleChildIndex;
  411. }
  412. else {
  413. node.visibleChildrenCount = 0;
  414. }
  415. if (node !== this.root) {
  416. node.visible = visibility === 2 /* TreeVisibility.Recurse */ ? hasVisibleDescendants : (visibility === 1 /* TreeVisibility.Visible */);
  417. node.visibility = visibility;
  418. }
  419. if (!node.visible) {
  420. node.renderNodeCount = 0;
  421. if (revealed) {
  422. result.pop();
  423. }
  424. }
  425. else if (!node.collapsed) {
  426. node.renderNodeCount += result.length - resultStartLength;
  427. }
  428. this._onDidChangeRenderNodeCount.fire(node);
  429. return node.visible;
  430. }
  431. _updateAncestorsRenderNodeCount(node, diff) {
  432. if (diff === 0) {
  433. return;
  434. }
  435. while (node) {
  436. node.renderNodeCount += diff;
  437. this._onDidChangeRenderNodeCount.fire(node);
  438. node = node.parent;
  439. }
  440. }
  441. _filterNode(node, parentVisibility) {
  442. const result = this.filter ? this.filter.filter(node.element, parentVisibility) : 1 /* TreeVisibility.Visible */;
  443. if (typeof result === 'boolean') {
  444. node.filterData = undefined;
  445. return result ? 1 /* TreeVisibility.Visible */ : 0 /* TreeVisibility.Hidden */;
  446. }
  447. else if (isFilterResult(result)) {
  448. node.filterData = result.data;
  449. return getVisibleState(result.visibility);
  450. }
  451. else {
  452. node.filterData = undefined;
  453. return getVisibleState(result);
  454. }
  455. }
  456. // cheap
  457. hasTreeNode(location, node = this.root) {
  458. if (!location || location.length === 0) {
  459. return true;
  460. }
  461. const [index, ...rest] = location;
  462. if (index < 0 || index > node.children.length) {
  463. return false;
  464. }
  465. return this.hasTreeNode(rest, node.children[index]);
  466. }
  467. // cheap
  468. getTreeNode(location, node = this.root) {
  469. if (!location || location.length === 0) {
  470. return node;
  471. }
  472. const [index, ...rest] = location;
  473. if (index < 0 || index > node.children.length) {
  474. throw new TreeError(this.user, 'Invalid tree location');
  475. }
  476. return this.getTreeNode(rest, node.children[index]);
  477. }
  478. // expensive
  479. getTreeNodeWithListIndex(location) {
  480. if (location.length === 0) {
  481. return { node: this.root, listIndex: -1, revealed: true, visible: false };
  482. }
  483. const { parentNode, listIndex, revealed, visible } = this.getParentNodeWithListIndex(location);
  484. const index = location[location.length - 1];
  485. if (index < 0 || index > parentNode.children.length) {
  486. throw new TreeError(this.user, 'Invalid tree location');
  487. }
  488. const node = parentNode.children[index];
  489. return { node, listIndex, revealed, visible: visible && node.visible };
  490. }
  491. getParentNodeWithListIndex(location, node = this.root, listIndex = 0, revealed = true, visible = true) {
  492. const [index, ...rest] = location;
  493. if (index < 0 || index > node.children.length) {
  494. throw new TreeError(this.user, 'Invalid tree location');
  495. }
  496. // TODO@joao perf!
  497. for (let i = 0; i < index; i++) {
  498. listIndex += node.children[i].renderNodeCount;
  499. }
  500. revealed = revealed && !node.collapsed;
  501. visible = visible && node.visible;
  502. if (rest.length === 0) {
  503. return { parentNode: node, listIndex, revealed, visible };
  504. }
  505. return this.getParentNodeWithListIndex(rest, node.children[index], listIndex + 1, revealed, visible);
  506. }
  507. getNode(location = []) {
  508. return this.getTreeNode(location);
  509. }
  510. // TODO@joao perf!
  511. getNodeLocation(node) {
  512. const location = [];
  513. let indexTreeNode = node; // typing woes
  514. while (indexTreeNode.parent) {
  515. location.push(indexTreeNode.parent.children.indexOf(indexTreeNode));
  516. indexTreeNode = indexTreeNode.parent;
  517. }
  518. return location.reverse();
  519. }
  520. getParentNodeLocation(location) {
  521. if (location.length === 0) {
  522. return undefined;
  523. }
  524. else if (location.length === 1) {
  525. return [];
  526. }
  527. else {
  528. return tail2(location)[0];
  529. }
  530. }
  531. getFirstElementChild(location) {
  532. const node = this.getTreeNode(location);
  533. if (node.children.length === 0) {
  534. return undefined;
  535. }
  536. return node.children[0].element;
  537. }
  538. }