@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1/**
2 * @requires javelin-dom
3 * javelin-install
4 * phabricator-diff-path-view
5 * @provides phabricator-diff-tree-view
6 *
7 * @javelin-installs JX.DiffTreeView
8 *
9 * @javelin
10 */
11
12JX.install('DiffTreeView', {
13
14 construct: function() {
15 this._keys = [];
16 this._tree = this._newTreeNode(null, [], 0);
17 this._nodes = {};
18 this._paths = [];
19 },
20
21 members: {
22 _node: null,
23 _keys: null,
24 _tree: null,
25 _nodes: null,
26 _dirty: false,
27 _paths: null,
28 _selectedPath: null,
29 _focusedPath: null,
30
31 getNode: function() {
32 if (!this._node) {
33 var attrs = {
34 className: 'diff-tree-view'
35 };
36
37 this._node = JX.$N('ul', attrs);
38 }
39
40 if (this._dirty) {
41 this.redraw();
42 }
43
44 return this._node;
45 },
46
47 addPath: function(path) {
48 this._paths.push(path);
49
50 var tree = this._getTree(this._tree, path.getPath(), 0);
51 tree.pathObject = path;
52
53 this._dirty = true;
54
55 return this;
56 },
57
58 getPaths: function() {
59 return this._paths;
60 },
61
62 setSelectedPath: function(path) {
63 if (this._selectedPath) {
64 this._selectedPath.setIsSelected(false);
65 this._selectedPath = null;
66 }
67
68 if (path) {
69 path.setIsSelected(true);
70 }
71
72 this._selectedPath = path;
73
74 return this;
75 },
76
77 setFocusedPath: function(path) {
78 if (this._focusedPath) {
79 this._focusedPath.setIsFocused(false);
80 this._focusedPath = null;
81 }
82
83 if (path) {
84 path.setIsFocused(true);
85 }
86
87 this._focusedPath = path;
88
89 return this;
90 },
91
92 redraw: function() {
93 if (!this._dirty) {
94 return;
95 }
96 this._dirty = false;
97
98 var ii;
99
100 // For nodes which don't have a path object yet, build one.
101 var tree;
102 var path;
103 var trees = [];
104 for (ii = 0; ii < this._keys.length; ii++) {
105 var key = this._keys[ii];
106 tree = this._nodes[key];
107 path = tree.pathObject;
108
109 if (!path) {
110 path = new JX.DiffPathView()
111 .setPath(tree.parts);
112
113 path.getIcon()
114 .setIcon('fa-folder-open-o')
115 .setColor('grey');
116
117 tree.pathObject = path;
118 }
119
120 trees.push(tree);
121 }
122
123 for (ii = 0; ii < trees.length; ii++) {
124 tree = trees[ii];
125 tree.displayRoot = null;
126 tree.displayPath = null;
127 tree.displayHide = false;
128 }
129
130 var child;
131 for (ii = 0; ii < trees.length; ii++) {
132 tree = trees[ii];
133
134 if (tree.childCount !== 1) {
135 continue;
136 }
137
138 for (var k in tree.children) {
139 if (tree.children.hasOwnProperty(k)) {
140 child = tree.children[k];
141 break;
142 }
143 }
144
145 if (child.pathObject.getChangeset()) {
146 continue;
147 }
148
149 child.displayRoot = tree.displayRoot || tree;
150 }
151
152 for (ii = 0; ii < trees.length; ii++) {
153 tree = trees[ii];
154
155 if (!tree.displayRoot) {
156 continue;
157 }
158
159 if (!tree.displayRoot.displayPath) {
160 tree.displayRoot.displayPath = [
161 tree.displayRoot.parts[tree.displayRoot.parts.length - 1]
162 ];
163 }
164
165 tree.displayRoot.displayPath.push(tree.parts[tree.parts.length - 1]);
166 tree.displayHide = true;
167 }
168
169 for (ii = 0; ii < trees.length; ii++) {
170 tree = trees[ii];
171 path = tree.pathObject;
172
173 path.setHidden(!!tree.displayHide);
174
175 if (tree.displayPath) {
176 path.setDisplayPath(tree.displayPath.join('/'));
177 } else {
178 path.setDisplayPath(null);
179 }
180 }
181
182 for (ii = 0; ii < trees.length; ii++) {
183 tree = trees[ii];
184
185 if (!tree.parent) {
186 tree.depth = 0;
187 } else {
188 // If this node was collapsed into the parent node, don't increase
189 // the tree depth.
190 if (tree.displayHide) {
191 tree.depth = tree.parent.depth;
192 } else {
193 tree.depth = tree.parent.depth + 1;
194 }
195 }
196
197 path = tree.pathObject;
198
199 if (tree.childCount > 0) {
200 path.setIsDirectory(true);
201 }
202
203 path.setDepth((tree.depth - 1));
204 }
205
206 var nodes = [];
207 for (ii = 0; ii < trees.length; ii++) {
208 tree = trees[ii];
209 nodes.push(tree.pathObject.getNode());
210 }
211
212 JX.DOM.setContent(this.getNode(), nodes);
213 },
214
215 _getTree: function(root, path, ii) {
216 if (ii >= path.length) {
217 return root;
218 }
219
220 var part = path[ii];
221
222 if (!root.children.hasOwnProperty(part)) {
223 root.children[part] = this._newTreeNode(root, path, ii);
224 root.childCount++;
225 }
226
227 return this._getTree(root.children[part], path, ii + 1);
228 },
229
230 _newTreeNode: function(parent, path, ii) {
231 var key;
232 var parts;
233 if (path.length) {
234 parts = path.slice(0, ii + 1);
235 key = parts.join('/');
236 this._keys.push(key);
237 } else {
238 parts = [];
239 key = null;
240 }
241
242 var node = {
243 parent: parent,
244 nodeKey: key,
245 parts: parts,
246 children: {},
247 pathObject: null,
248 childCount: 0,
249 depth: 0
250 };
251
252 if (key !== null) {
253 this._nodes[key] = node;
254 }
255
256 return node;
257 }
258
259 }
260
261});