]> git.wh0rd.org - tt-rss.git/blame - js/FeedTree.js
update perms
[tt-rss.git] / js / FeedTree.js
CommitLineData
02672124 1/* global dijit */
a3e2f1a9 2define(["dojo/_base/declare", "dojo/dom-construct", "dijit/Tree", "dijit/Menu"], function (declare, domConstruct) {
452e75cc 3
d39a2f80
AD
4 return declare("fox.FeedTree", dijit.Tree, {
5 _onKeyPress: function(/* Event */ e) {
6 return; // Stop dijit.Tree from interpreting keystrokes
7 },
8 _createTreeNode: function(args) {
02672124 9 const tnode = new dijit._TreeNode(args);
05f224a3 10
02672124 11 const icon = dojo.doc.createElement('img');
6887a0f5
AK
12 if (args.item.icon && args.item.icon[0]) {
13 icon.src = args.item.icon[0];
14 } else {
15 icon.src = 'images/blank_icon.gif';
16 }
17 icon.className = 'tinyFeedIcon';
18 domConstruct.place(icon, tnode.iconNode, 'only');
997429c2 19
02672124
AD
20 const id = args.item.id[0];
21 const bare_id = parseInt(id.substr(id.indexOf(':')+1));
9fe80bcd 22
d39a2f80 23 if (bare_id < _label_base_index) {
02672124
AD
24 const span = dojo.doc.createElement('span');
25 const fg_color = args.item.fg_color[0];
26 const bg_color = args.item.bg_color[0];
9fe80bcd 27
d39a2f80
AD
28 span.innerHTML = "&alpha;";
29 span.className = 'labelColorIndicator';
30 span.setStyle({
31 color: fg_color,
32 backgroundColor: bg_color});
9fe80bcd 33
6887a0f5 34 domConstruct.place(span, tnode.iconNode, 'only');
d39a2f80 35 }
997429c2 36
d39a2f80 37 if (id.match("FEED:")) {
02672124 38 let menu = new dijit.Menu();
d39a2f80 39 menu.row_id = bare_id;
e3d2c029 40
10afc7c6 41 menu.addChild(new dijit.MenuItem({
d39a2f80 42 label: __("Mark as read"),
10afc7c6 43 onClick: function() {
d39a2f80 44 catchupFeed(this.getParent().row_id);
10afc7c6
AD
45 }}));
46
d39a2f80
AD
47 if (bare_id > 0) {
48 menu.addChild(new dijit.MenuItem({
49 label: __("Edit feed"),
50 onClick: function() {
51 editFeed(this.getParent().row_id, false);
52 }}));
53
54 /* menu.addChild(new dijit.MenuItem({
55 label: __("Update feed"),
56 onClick: function() {
57 heduleFeedUpdate(this.getParent().row_id, false);
58 }})); */
59 }
60
61 menu.bindDomNode(tnode.domNode);
62 tnode._menu = menu;
63 }
64
65 if (id.match("CAT:") && bare_id >= 0) {
02672124 66 let menu = new dijit.Menu();
d39a2f80
AD
67 menu.row_id = bare_id;
68
acfee412 69 menu.addChild(new dijit.MenuItem({
f04b12d8 70 label: __("Mark as read"),
acfee412 71 onClick: function() {
f04b12d8 72 catchupFeed(this.getParent().row_id, true);
acfee412
AD
73 }}));
74
d39a2f80 75 menu.addChild(new dijit.MenuItem({
f04b12d8 76 label: __("(Un)collapse"),
10afc7c6 77 onClick: function() {
f04b12d8 78 dijit.byId("feedTree").collapseCat(this.getParent().row_id);
d39a2f80
AD
79 }}));
80
81 menu.bindDomNode(tnode.domNode);
82 tnode._menu = menu;
10afc7c6 83 }
997429c2 84
d39a2f80
AD
85 if (id.match("CAT:")) {
86 loading = dojo.doc.createElement('img');
87 loading.className = 'loadingNode';
88 loading.src = 'images/blank_icon.gif';
9f539be3 89 domConstruct.place(loading, tnode.labelNode, 'after');
d39a2f80
AD
90 tnode.loadingNode = loading;
91 }
c594eca0 92
d39a2f80 93 if (id.match("CAT:") && bare_id == -1) {
02672124 94 let menu = new dijit.Menu();
d39a2f80 95 menu.row_id = bare_id;
c594eca0 96
d39a2f80
AD
97 menu.addChild(new dijit.MenuItem({
98 label: __("Mark all feeds as read"),
99 onClick: function() {
100 catchupAllFeeds();
101 }}));
102
103 menu.bindDomNode(tnode.domNode);
104 tnode._menu = menu;
8d4b5b46 105 }
d39a2f80
AD
106
107 ctr = dojo.doc.createElement('span');
108 ctr.className = 'counterNode';
109 ctr.innerHTML = args.item.unread > 0 ? args.item.unread : args.item.auxcounter;
110
111 //args.item.unread > 0 ? ctr.addClassName("unread") : ctr.removeClassName("unread");
112
113 args.item.unread > 0 || args.item.auxcounter > 0 ? Element.show(ctr) : Element.hide(ctr);
114
115 args.item.unread == 0 && args.item.auxcounter > 0 ? ctr.addClassName("aux") : ctr.removeClassName("aux");
116
9f539be3 117 domConstruct.place(ctr, tnode.rowNode, 'first');
d39a2f80
AD
118 tnode.counterNode = ctr;
119
120 //tnode.labelNode.innerHTML = args.label;
121 return tnode;
122 },
123 postCreate: function() {
124 this.connect(this.model, "onChange", "updateCounter");
125 this.connect(this, "_expandNode", function() {
126 this.hideRead(getInitParam("hide_read_feeds"), getInitParam("hide_read_shows_special"));
127 });
128
129 this.inherited(arguments);
130 },
131 updateCounter: function (item) {
02672124 132 const tree = this;
d39a2f80
AD
133
134 //console.log("updateCounter: " + item.id[0] + " " + item.unread + " " + tree);
135
02672124 136 let node = tree._itemNodesMap[item.id];
d39a2f80
AD
137
138 if (node) {
139 node = node[0];
140
141 if (node.counterNode) {
142 ctr = node.counterNode;
143 ctr.innerHTML = item.unread > 0 ? item.unread : item.auxcounter;
144 item.unread > 0 || item.auxcounter > 0 ?
70c5b2bf
AD
145 item.unread > 0 ?
146 Effect.Appear(ctr, {duration : 0.3,
147 queue: { position: 'end', scope: 'CAPPEAR-' + item.id, limit: 1 }}) :
148 Element.show(ctr) :
d39a2f80
AD
149 Element.hide(ctr);
150
151 item.unread == 0 && item.auxcounter > 0 ? ctr.addClassName("aux") : ctr.removeClassName("aux");
152
2196b517
AD
153 }
154 }
d39a2f80
AD
155
156 },
157 getTooltip: function (item) {
158 if (item.updated)
159 return item.updated;
160 else
161 return "";
162 },
163 getIconClass: function (item, opened) {
164 return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feedIcon";
165 },
166 getLabelClass: function (item, opened) {
167 return (item.unread == 0) ? "dijitTreeLabel" : "dijitTreeLabel Unread";
168 },
169 getRowClass: function (item, opened) {
02672124 170 let rc = (!item.error || item.error == '') ? "dijitTreeRow" :
d39a2f80
AD
171 "dijitTreeRow Error";
172
173 if (item.unread > 0) rc += " Unread";
f6269d1b 174 if (item.updates_disabled > 0) rc += " UpdatesDisabled";
d39a2f80
AD
175
176 return rc;
177 },
178 getLabel: function(item) {
02672124 179 let name = String(item.name);
d39a2f80
AD
180
181 /* Horrible */
182 name = name.replace(/&quot;/g, "\"");
183 name = name.replace(/&amp;/g, "&");
184 name = name.replace(/&mdash;/g, "-");
185 name = name.replace(/&lt;/g, "<");
186 name = name.replace(/&gt;/g, ">");
187
188 /* var label;
189
190 if (item.unread > 0) {
191 label = name + " (" + item.unread + ")";
192 } else {
193 label = name;
194 } */
195
196 return name;
197 },
198 expandParentNodes: function(feed, is_cat, list) {
199 try {
02672124
AD
200 for (let i = 0; i < list.length; i++) {
201 const id = String(list[i].id);
202 let item = this._itemNodesMap[id];
d39a2f80
AD
203
204 if (item) {
205 item = item[0];
206 this._expandNode(item);
2196b517
AD
207 }
208 }
d39a2f80 209 } catch (e) {
1bfe1d7b 210 exception_error(e);
d39a2f80
AD
211 }
212 },
213 findNodeParentsAndExpandThem: function(feed, is_cat, root, parents) {
214 // expands all parents of specified feed to properly mark it as active
215 // my fav thing about frameworks is doing everything myself
216 try {
02672124 217 const test_id = is_cat ? 'CAT:' + feed : 'FEED:' + feed;
d39a2f80
AD
218
219 if (!root) {
220 if (!this.model || !this.model.store) return false;
2196b517 221
02672124 222 const items = this.model.store._arrayOfTopLevelItems;
d39a2f80 223
02672124 224 for (let i = 0; i < items.length; i++) {
d39a2f80 225 if (String(items[i].id) == test_id) {
2196b517
AD
226 this.expandParentNodes(feed, is_cat, parents);
227 } else {
d39a2f80 228 this.findNodeParentsAndExpandThem(feed, is_cat, items[i], []);
2196b517
AD
229 }
230 }
02672124 231 } else if (root.items) {
d39a2f80
AD
232 parents.push(root);
233
02672124 234 for (let i = 0; i < root.items.length; i++) {
d39a2f80
AD
235 if (String(root.items[i].id) == test_id) {
236 this.expandParentNodes(feed, is_cat, parents);
237 } else {
238 this.findNodeParentsAndExpandThem(feed, is_cat, root.items[i], parents.slice(0));
239 }
240 }
02672124 241 } else if (String(root.id) == test_id) {
d39a2f80
AD
242 this.expandParentNodes(feed, is_cat, parents.slice(0));
243 }
d39a2f80 244 } catch (e) {
1bfe1d7b 245 exception_error(e);
2196b517 246 }
d39a2f80
AD
247 },
248 selectFeed: function(feed, is_cat) {
249 this.findNodeParentsAndExpandThem(feed, is_cat, false, false);
452e75cc 250
d39a2f80
AD
251 if (is_cat)
252 treeNode = this._itemNodesMap['CAT:' + feed];
253 else
254 treeNode = this._itemNodesMap['FEED:' + feed];
255
256 if (treeNode) {
257 treeNode = treeNode[0];
258 if (!is_cat) this._expandNode(treeNode);
259 this.set("selectedNodes", [treeNode]);
fa1be041 260 this.focusNode(treeNode);
7590f039
AD
261
262 // focus headlines to route key events there
263 setTimeout(function() {
264 $("headlines-frame").focus();
265 }, 0);
d39a2f80
AD
266 }
267 },
268 setFeedIcon: function(feed, is_cat, src) {
269 if (is_cat)
270 treeNode = this._itemNodesMap['CAT:' + feed];
271 else
272 treeNode = this._itemNodesMap['FEED:' + feed];
452e75cc 273
d39a2f80
AD
274 if (treeNode) {
275 treeNode = treeNode[0];
02672124 276 const icon = dojo.doc.createElement('img');
6887a0f5
AK
277 icon.src = src;
278 icon.className = 'tinyFeedIcon';
279 domConstruct.place(icon, treeNode.iconNode, 'only');
d39a2f80
AD
280 return true;
281 }
282 return false;
283 },
284 setFeedExpandoIcon: function(feed, is_cat, src) {
285 if (is_cat)
286 treeNode = this._itemNodesMap['CAT:' + feed];
287 else
288 treeNode = this._itemNodesMap['FEED:' + feed];
452e75cc 289
d39a2f80
AD
290 if (treeNode) {
291 treeNode = treeNode[0];
292 if (treeNode.loadingNode) {
293 treeNode.loadingNode.src = src;
294 return true;
295 } else {
02672124 296 const icon = dojo.doc.createElement('img');
6887a0f5 297 icon.src = src;
b7d63a58 298 icon.className = 'loadingExpando';
6887a0f5 299 domConstruct.place(icon, treeNode.expandoNode, 'only');
d39a2f80
AD
300 return true;
301 }
302 }
452e75cc 303
d39a2f80
AD
304 return false;
305 },
306 hasCats: function() {
307 return this.model.hasCats();
308 },
309 hideReadCat: function (cat, hide, show_special) {
310 if (this.hasCats()) {
02672124 311 const tree = this;
d39a2f80
AD
312
313 if (cat && cat.items) {
02672124 314 let cat_unread = tree.hideReadFeeds(cat.items, hide, show_special);
d39a2f80 315
02672124
AD
316 const id = String(cat.id);
317 const node = tree._itemNodesMap[id];
318 const bare_id = parseInt(id.substr(id.indexOf(":")+1));
d39a2f80
AD
319
320 if (node) {
02672124 321 const check_unread = tree.model.getFeedUnread(bare_id, true);
d39a2f80
AD
322
323 if (hide && cat_unread == 0 && check_unread == 0 && (id != "CAT:-1" || !show_special)) {
324 Effect.Fade(node[0].rowNode, {duration : 0.3,
325 queue: { position: 'end', scope: 'FFADE-' + id, limit: 1 }});
326 } else {
327 Element.show(node[0].rowNode);
328 ++cat_unread;
329 }
05f224a3 330 }
452e75cc 331 }
5712a2fe 332 }
d39a2f80
AD
333 },
334 hideRead: function (hide, show_special) {
335 if (this.hasCats()) {
336
02672124
AD
337 const tree = this;
338 const cats = this.model.store._arrayOfTopLevelItems;
5712a2fe 339
d39a2f80
AD
340 cats.each(function(cat) {
341 tree.hideReadCat(cat, hide, show_special);
342 });
343
344 } else {
345 this.hideReadFeeds(this.model.store._arrayOfTopLevelItems, hide,
346 show_special);
347 }
348 },
349 hideReadFeeds: function (items, hide, show_special) {
02672124
AD
350 const tree = this;
351 let cat_unread = 0;
5712a2fe 352
d39a2f80 353 items.each(function(feed) {
02672124 354 const id = String(feed.id);
05f224a3 355
d39a2f80
AD
356 // it's a subcategory
357 if (feed.items) {
358 tree.hideReadCat(feed, hide, show_special);
359 } else { // it's a feed
02672124 360 const bare_id = parseInt(feed.bare_id);
d39a2f80 361
02672124
AD
362 const unread = feed.unread[0];
363 const has_error = feed.error[0] != '';
364 const node = tree._itemNodesMap[id];
d39a2f80
AD
365
366 if (node) {
367 if (hide && unread == 0 && !has_error && (bare_id > 0 || bare_id < _label_base_index || !show_special)) {
368 Effect.Fade(node[0].rowNode, {duration : 0.3,
369 queue: { position: 'end', scope: 'FFADE-' + id, limit: 1 }});
370 } else {
371 Element.show(node[0].rowNode);
372 ++cat_unread;
373 }
6cdc4576 374 }
05f224a3 375 }
d39a2f80 376 });
452e75cc 377
d39a2f80
AD
378 return cat_unread;
379 },
380 collapseCat: function(id) {
381 if (!this.model.hasCats()) return;
7613280a 382
02672124 383 const tree = this;
7613280a 384
02672124
AD
385 const node = tree._itemNodesMap['CAT:' + id][0];
386 const item = tree.model.store._itemsByIdentity['CAT:' + id];
7613280a 387
d39a2f80
AD
388 if (node && item) {
389 if (!node.isExpanded)
390 tree._expandNode(node);
391 else
392 tree._collapseNode(node);
7613280a 393
d39a2f80
AD
394 }
395 },
396 getVisibleUnreadFeeds: function() {
02672124
AD
397 const items = this.model.store._arrayOfAllItems;
398 const rv = [];
452e75cc 399
02672124
AD
400 for (let i = 0; i < items.length; i++) {
401 const id = String(items[i].id);
402 const box = this._itemNodesMap[id];
452e75cc 403
d39a2f80 404 if (box) {
02672124
AD
405 const row = box[0].rowNode;
406 let cat = false;
452e75cc 407
d39a2f80
AD
408 try {
409 cat = box[0].rowNode.parentNode.parentNode;
410 } catch (e) { }
452e75cc 411
d39a2f80
AD
412 if (row) {
413 if (Element.visible(row) && (!cat || Element.visible(cat))) {
02672124
AD
414 const feed_id = String(items[i].bare_id);
415 const is_cat = !id.match('FEED:');
416 const unread = this.model.getFeedUnread(feed_id, is_cat);
452e75cc 417
d39a2f80
AD
418 if (unread > 0)
419 rv.push([feed_id, is_cat]);
452e75cc 420
d39a2f80 421 }
452e75cc
AD
422 }
423 }
424 }
d39a2f80
AD
425
426 return rv;
427 },
428 getNextFeed: function (feed, is_cat) {
429 if (is_cat) {
430 treeItem = this.model.store._itemsByIdentity['CAT:' + feed];
431 } else {
432 treeItem = this.model.store._itemsByIdentity['FEED:' + feed];
433 }
434
02672124
AD
435 const items = this.model.store._arrayOfAllItems;
436 let item = items[0];
d39a2f80 437
02672124 438 for (let i = 0; i < items.length; i++) {
d39a2f80
AD
439 if (items[i] == treeItem) {
440
02672124
AD
441 for (let j = i+1; j < items.length; j++) {
442 const id = String(items[j].id);
443 const box = this._itemNodesMap[id];
d39a2f80
AD
444
445 if (box) {
02672124
AD
446 const row = box[0].rowNode;
447 const cat = box[0].rowNode.parentNode.parentNode;
d39a2f80
AD
448
449 if (Element.visible(cat) && Element.visible(row)) {
450 item = items[j];
451 break;
452 }
d026d372
AD
453 }
454 }
d39a2f80 455 break;
d026d372 456 }
d026d372 457 }
d026d372 458
d39a2f80
AD
459 if (item) {
460 return [this.model.store.getValue(item, 'bare_id'),
461 !this.model.store.getValue(item, 'id').match('FEED:')];
462 } else {
463 return false;
464 }
465 },
466 getPreviousFeed: function (feed, is_cat) {
467 if (is_cat) {
468 treeItem = this.model.store._itemsByIdentity['CAT:' + feed];
469 } else {
470 treeItem = this.model.store._itemsByIdentity['FEED:' + feed];
471 }
472
02672124
AD
473 const items = this.model.store._arrayOfAllItems;
474 let item = items[0] == treeItem ? items[items.length-1] : items[0];
d39a2f80 475
02672124 476 for (let i = 0; i < items.length; i++) {
d39a2f80
AD
477 if (items[i] == treeItem) {
478
02672124
AD
479 for (let j = i-1; j > 0; j--) {
480 const id = String(items[j].id);
481 const box = this._itemNodesMap[id];
d39a2f80
AD
482
483 if (box) {
02672124
AD
484 const row = box[0].rowNode;
485 const cat = box[0].rowNode.parentNode.parentNode;
d39a2f80
AD
486
487 if (Element.visible(cat) && Element.visible(row)) {
488 item = items[j];
489 break;
490 }
d026d372 491 }
c2b2869c 492
d39a2f80
AD
493 }
494 break;
d026d372 495 }
d026d372 496 }
d026d372 497
d39a2f80
AD
498 if (item) {
499 return [this.model.store.getValue(item, 'bare_id'),
500 !this.model.store.getValue(item, 'id').match('FEED:')];
501 } else {
502 return false;
503 }
d026d372 504
d39a2f80
AD
505 },
506 getFeedCategory: function(feed) {
507 try {
508 return this.getNodesByItem(this.model.store.
6c8e8fbd 509 _itemsByIdentity["FEED:" + feed])[0].
d39a2f80 510 getParent().item.bare_id[0];
d026d372 511
d39a2f80
AD
512 } catch (e) {
513 return false;
514 }
515 },
516 });
05f224a3 517});
d39a2f80 518