diff --git a/src/webui/www/private/scripts/file-tree.js b/src/webui/www/private/scripts/file-tree.js index 3622b5842..328806988 100644 --- a/src/webui/www/private/scripts/file-tree.js +++ b/src/webui/www/private/scripts/file-tree.js @@ -72,14 +72,17 @@ window.qBittorrent.FileTree ??= (() => { return this.root; }, - generateNodeMap: function(node) { - // don't store root node in map - if (node.root !== null) - this.nodeMap[node.rowId] = node; + generateNodeMap: function(root) { + const stack = [root]; + while (stack.length > 0) { + const node = stack.pop(); - node.children.each((child) => { - this.generateNodeMap(child); - }); + // don't store root node in map + if (node.root !== null) + this.nodeMap[node.rowId] = node; + + stack.push(...node.children); + } }, getNode: function(rowId) { @@ -93,21 +96,17 @@ window.qBittorrent.FileTree ??= (() => { }, /** - * Returns the nodes in dfs order + * Returns the nodes in DFS in-order */ toArray: function() { - const nodes = []; - this.root.children.each((node) => { - this._getArrayOfNodes(node, nodes); - }); - return nodes; - }, - - _getArrayOfNodes: function(node, array) { - array.push(node); - node.children.each((child) => { - this._getArrayOfNodes(child, array); - }); + const ret = []; + const stack = this.root.children.toReversed(); + while (stack.length > 0) { + const node = stack.pop(); + ret.push(node); + stack.push(...node.children.toReversed()); + } + return ret; } }); @@ -140,55 +139,68 @@ window.qBittorrent.FileTree ??= (() => { this.isFolder = true; }, - addChild(node) { + addChild: function(node) { this.children.push(node); }, /** - * Recursively calculate size of node and its children + * Calculate size of node and its children */ calculateSize: function() { - let size = 0; - let remaining = 0; - let progress = 0; - let availability = 0; - let checked = TriState.Unchecked; - let priority = FilePriority.Normal; + const stack = [this]; + const visited = []; - let isFirstFile = true; + while (stack.length > 0) { + const root = stack.at(-1); - this.children.each((node) => { - if (node.isFolder) - node.calculateSize(); + if (root.isFolder) { + if (visited.at(-1) !== root) { + visited.push(root); + stack.push(...root.children); + continue; + } - size += node.size; + visited.pop(); - if (isFirstFile) { - priority = node.priority; - checked = node.checked; - isFirstFile = false; - } - else { - if (priority !== node.priority) - priority = FilePriority.Mixed; - if (checked !== node.checked) - checked = TriState.Partial; + // process children + root.size = 0; + root.remaining = 0; + root.progress = 0; + root.availability = 0; + root.checked = TriState.Unchecked; + root.priority = FilePriority.Normal; + let isFirstFile = true; + + for (const child of root.children) { + root.size += child.size; + + if (isFirstFile) { + root.priority = child.priority; + root.checked = child.checked; + isFirstFile = false; + } + else { + if (root.priority !== child.priority) + root.priority = FilePriority.Mixed; + if (root.checked !== child.checked) + root.checked = TriState.Partial; + } + + const isIgnored = (child.priority === FilePriority.Ignored); + if (!isIgnored) { + root.remaining += child.remaining; + root.progress += (child.progress * child.size); + root.availability += (child.availability * child.size); + } + } + + root.checked = root.autoCheckFolders ? root.checked : TriState.Checked; + root.progress /= root.size; + root.availability /= root.size; } - const isIgnored = (node.priority === FilePriority.Ignored); - if (!isIgnored) { - remaining += node.remaining; - progress += (node.progress * node.size); - availability += (node.availability * node.size); - } - }); - - this.size = size; - this.remaining = remaining; - this.checked = this.autoCheckFolders ? checked : TriState.Checked; - this.progress = (progress / size); - this.priority = priority; - this.availability = (availability / size); + stack.pop(); + } } });