]>
git.wh0rd.org - tt-rss.git/blob - js/FeedTree.js
2 define(["dojo/_base/declare", "dojo/dom-construct", "dijit/Tree", "dijit/Menu"], function (declare
, domConstruct
) {
4 return declare("fox.FeedTree", dijit
.Tree
, {
5 _onKeyPress: function(/* Event */ e
) {
6 return; // Stop dijit.Tree from interpreting keystrokes
8 _createTreeNode: function(args
) {
9 const tnode
= new dijit
._TreeNode(args
);
11 const icon
= dojo
.doc
.createElement('img');
12 if (args
.item
.icon
&& args
.item
.icon
[0]) {
13 icon
.src
= args
.item
.icon
[0];
15 icon
.src
= 'images/blank_icon.gif';
17 icon
.className
= 'tinyFeedIcon';
18 domConstruct
.place(icon
, tnode
.iconNode
, 'only');
20 const id
= args
.item
.id
[0];
21 const bare_id
= parseInt(id
.substr(id
.indexOf(':')+1));
23 if (bare_id
< _label_base_index
) {
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];
28 span
.innerHTML
= "α";
29 span
.className
= 'labelColorIndicator';
32 backgroundColor
: bg_color
});
34 domConstruct
.place(span
, tnode
.iconNode
, 'only');
37 if (id
.match("FEED:")) {
38 let menu
= new dijit
.Menu();
39 menu
.row_id
= bare_id
;
41 menu
.addChild(new dijit
.MenuItem({
42 label
: __("Mark as read"),
44 catchupFeed(this.getParent().row_id
);
48 menu
.addChild(new dijit
.MenuItem({
49 label
: __("Edit feed"),
51 editFeed(this.getParent().row_id
, false);
54 /* menu.addChild(new dijit.MenuItem({
55 label: __("Update feed"),
57 heduleFeedUpdate(this.getParent().row_id, false);
61 menu
.bindDomNode(tnode
.domNode
);
65 if (id
.match("CAT:") && bare_id
>= 0) {
66 let menu
= new dijit
.Menu();
67 menu
.row_id
= bare_id
;
69 menu
.addChild(new dijit
.MenuItem({
70 label
: __("Mark as read"),
72 catchupFeed(this.getParent().row_id
, true);
75 menu
.addChild(new dijit
.MenuItem({
76 label
: __("(Un)collapse"),
78 dijit
.byId("feedTree").collapseCat(this.getParent().row_id
);
81 menu
.bindDomNode(tnode
.domNode
);
85 if (id
.match("CAT:")) {
86 loading
= dojo
.doc
.createElement('img');
87 loading
.className
= 'loadingNode';
88 loading
.src
= 'images/blank_icon.gif';
89 domConstruct
.place(loading
, tnode
.labelNode
, 'after');
90 tnode
.loadingNode
= loading
;
93 if (id
.match("CAT:") && bare_id
== -1) {
94 let menu
= new dijit
.Menu();
95 menu
.row_id
= bare_id
;
97 menu
.addChild(new dijit
.MenuItem({
98 label
: __("Mark all feeds as read"),
103 menu
.bindDomNode(tnode
.domNode
);
107 ctr
= dojo
.doc
.createElement('span');
108 ctr
.className
= 'counterNode';
109 ctr
.innerHTML
= args
.item
.unread
> 0 ? args
.item
.unread
: args
.item
.auxcounter
;
111 //args.item.unread > 0 ? ctr.addClassName("unread") : ctr.removeClassName("unread");
113 args
.item
.unread
> 0 || args
.item
.auxcounter
> 0 ? Element
.show(ctr
) : Element
.hide(ctr
);
115 args
.item
.unread
== 0 && args
.item
.auxcounter
> 0 ? ctr
.addClassName("aux") : ctr
.removeClassName("aux");
117 domConstruct
.place(ctr
, tnode
.rowNode
, 'first');
118 tnode
.counterNode
= ctr
;
120 //tnode.labelNode.innerHTML = args.label;
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"));
129 this.inherited(arguments
);
131 updateCounter: function (item
) {
134 //console.log("updateCounter: " + item.id[0] + " " + item.unread + " " + tree);
136 let node
= tree
._itemNodesMap
[item
.id
];
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 ?
146 Effect
.Appear(ctr
, {duration
: 0.3,
147 queue
: { position
: 'end', scope
: 'CAPPEAR-' + item
.id
, limit
: 1 }}) :
151 item
.unread
== 0 && item
.auxcounter
> 0 ? ctr
.addClassName("aux") : ctr
.removeClassName("aux");
157 getTooltip: function (item
) {
163 getIconClass: function (item
, opened
) {
164 return (!item
|| this.model
.mayHaveChildren(item
)) ? (opened
? "dijitFolderOpened" : "dijitFolderClosed") : "feedIcon";
166 getLabelClass: function (item
, opened
) {
167 return (item
.unread
== 0) ? "dijitTreeLabel" : "dijitTreeLabel Unread";
169 getRowClass: function (item
, opened
) {
170 let rc
= (!item
.error
|| item
.error
== '') ? "dijitTreeRow" :
171 "dijitTreeRow Error";
173 if (item
.unread
> 0) rc
+= " Unread";
174 if (item
.updates_disabled
> 0) rc
+= " UpdatesDisabled";
178 getLabel: function(item
) {
179 let name
= String(item
.name
);
182 name
= name
.replace(/"/g, "\"");
183 name
= name
.replace(/&/g, "&");
184 name
= name
.replace(/—/g, "-");
185 name
= name
.replace(/</g, "<");
186 name
= name
.replace(/>/g, ">");
190 if (item.unread > 0) {
191 label = name + " (" + item.unread + ")";
198 expandParentNodes: function(feed
, is_cat
, list
) {
200 for (let i
= 0; i
< list
.length
; i
++) {
201 const id
= String(list
[i
].id
);
202 let item
= this._itemNodesMap
[id
];
206 this._expandNode(item
);
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
217 const test_id
= is_cat
? 'CAT:' + feed
: 'FEED:' + feed
;
220 if (!this.model
|| !this.model
.store
) return false;
222 const items
= this.model
.store
._arrayOfTopLevelItems
;
224 for (let i
= 0; i
< items
.length
; i
++) {
225 if (String(items
[i
].id
) == test_id
) {
226 this.expandParentNodes(feed
, is_cat
, parents
);
228 this.findNodeParentsAndExpandThem(feed
, is_cat
, items
[i
], []);
231 } else if (root
.items
) {
234 for (let i
= 0; i
< root
.items
.length
; i
++) {
235 if (String(root
.items
[i
].id
) == test_id
) {
236 this.expandParentNodes(feed
, is_cat
, parents
);
238 this.findNodeParentsAndExpandThem(feed
, is_cat
, root
.items
[i
], parents
.slice(0));
241 } else if (String(root
.id
) == test_id
) {
242 this.expandParentNodes(feed
, is_cat
, parents
.slice(0));
248 selectFeed: function(feed
, is_cat
) {
249 this.findNodeParentsAndExpandThem(feed
, is_cat
, false, false);
252 treeNode
= this._itemNodesMap
['CAT:' + feed
];
254 treeNode
= this._itemNodesMap
['FEED:' + feed
];
257 treeNode
= treeNode
[0];
258 if (!is_cat
) this._expandNode(treeNode
);
259 this.set("selectedNodes", [treeNode
]);
260 this.focusNode(treeNode
);
262 // focus headlines to route key events there
263 setTimeout(function() {
264 $("headlines-frame").focus();
268 setFeedIcon: function(feed
, is_cat
, src
) {
270 treeNode
= this._itemNodesMap
['CAT:' + feed
];
272 treeNode
= this._itemNodesMap
['FEED:' + feed
];
275 treeNode
= treeNode
[0];
276 const icon
= dojo
.doc
.createElement('img');
278 icon
.className
= 'tinyFeedIcon';
279 domConstruct
.place(icon
, treeNode
.iconNode
, 'only');
284 setFeedExpandoIcon: function(feed
, is_cat
, src
) {
286 treeNode
= this._itemNodesMap
['CAT:' + feed
];
288 treeNode
= this._itemNodesMap
['FEED:' + feed
];
291 treeNode
= treeNode
[0];
292 if (treeNode
.loadingNode
) {
293 treeNode
.loadingNode
.src
= src
;
296 const icon
= dojo
.doc
.createElement('img');
298 icon
.className
= 'loadingExpando';
299 domConstruct
.place(icon
, treeNode
.expandoNode
, 'only');
306 hasCats: function() {
307 return this.model
.hasCats();
309 hideReadCat: function (cat
, hide
, show_special
) {
310 if (this.hasCats()) {
313 if (cat
&& cat
.items
) {
314 let cat_unread
= tree
.hideReadFeeds(cat
.items
, hide
, show_special
);
316 const id
= String(cat
.id
);
317 const node
= tree
._itemNodesMap
[id
];
318 const bare_id
= parseInt(id
.substr(id
.indexOf(":")+1));
321 const check_unread
= tree
.model
.getFeedUnread(bare_id
, true);
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 }});
327 Element
.show(node
[0].rowNode
);
334 hideRead: function (hide
, show_special
) {
335 if (this.hasCats()) {
338 const cats
= this.model
.store
._arrayOfTopLevelItems
;
340 cats
.each(function(cat
) {
341 tree
.hideReadCat(cat
, hide
, show_special
);
345 this.hideReadFeeds(this.model
.store
._arrayOfTopLevelItems
, hide
,
349 hideReadFeeds: function (items
, hide
, show_special
) {
353 items
.each(function(feed
) {
354 const id
= String(feed
.id
);
356 // it's a subcategory
358 tree
.hideReadCat(feed
, hide
, show_special
);
359 } else { // it's a feed
360 const bare_id
= parseInt(feed
.bare_id
);
362 const unread
= feed
.unread
[0];
363 const has_error
= feed
.error
[0] != '';
364 const node
= tree
._itemNodesMap
[id
];
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 }});
371 Element
.show(node
[0].rowNode
);
380 collapseCat: function(id
) {
381 if (!this.model
.hasCats()) return;
385 const node
= tree
._itemNodesMap
['CAT:' + id
][0];
386 const item
= tree
.model
.store
._itemsByIdentity
['CAT:' + id
];
389 if (!node
.isExpanded
)
390 tree
._expandNode(node
);
392 tree
._collapseNode(node
);
396 getVisibleUnreadFeeds: function() {
397 const items
= this.model
.store
._arrayOfAllItems
;
400 for (let i
= 0; i
< items
.length
; i
++) {
401 const id
= String(items
[i
].id
);
402 const box
= this._itemNodesMap
[id
];
405 const row
= box
[0].rowNode
;
409 cat
= box
[0].rowNode
.parentNode
.parentNode
;
413 if (Element
.visible(row
) && (!cat
|| Element
.visible(cat
))) {
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
);
419 rv
.push([feed_id
, is_cat
]);
428 getNextFeed: function (feed
, is_cat
) {
430 treeItem
= this.model
.store
._itemsByIdentity
['CAT:' + feed
];
432 treeItem
= this.model
.store
._itemsByIdentity
['FEED:' + feed
];
435 const items
= this.model
.store
._arrayOfAllItems
;
438 for (let i
= 0; i
< items
.length
; i
++) {
439 if (items
[i
] == treeItem
) {
441 for (let j
= i
+1; j
< items
.length
; j
++) {
442 const id
= String(items
[j
].id
);
443 const box
= this._itemNodesMap
[id
];
446 const row
= box
[0].rowNode
;
447 const cat
= box
[0].rowNode
.parentNode
.parentNode
;
449 if (Element
.visible(cat
) && Element
.visible(row
)) {
460 return [this.model
.store
.getValue(item
, 'bare_id'),
461 !this.model
.store
.getValue(item
, 'id').match('FEED:')];
466 getPreviousFeed: function (feed
, is_cat
) {
468 treeItem
= this.model
.store
._itemsByIdentity
['CAT:' + feed
];
470 treeItem
= this.model
.store
._itemsByIdentity
['FEED:' + feed
];
473 const items
= this.model
.store
._arrayOfAllItems
;
474 let item
= items
[0] == treeItem
? items
[items
.length
-1] : items
[0];
476 for (let i
= 0; i
< items
.length
; i
++) {
477 if (items
[i
] == treeItem
) {
479 for (let j
= i
-1; j
> 0; j
--) {
480 const id
= String(items
[j
].id
);
481 const box
= this._itemNodesMap
[id
];
484 const row
= box
[0].rowNode
;
485 const cat
= box
[0].rowNode
.parentNode
.parentNode
;
487 if (Element
.visible(cat
) && Element
.visible(row
)) {
499 return [this.model
.store
.getValue(item
, 'bare_id'),
500 !this.model
.store
.getValue(item
, 'id').match('FEED:')];
506 getFeedCategory: function(feed
) {
508 return this.getNodesByItem(this.model
.store
.
509 _itemsByIdentity
["FEED:" + feed
])[0].
510 getParent().item
.bare_id
[0];