]>
git.wh0rd.org - tt-rss.git/blob - js/FeedTree.js
1 dojo
.provide("fox.FeedTree");
2 dojo
.provide("fox.FeedStoreModel");
4 dojo
.require("dijit.Tree");
5 dojo
.require("dijit.Menu");
7 dojo
.declare("fox.FeedStoreModel", dijit
.tree
.ForestStoreModel
, {
8 getItemsInCategory: function (id
) {
9 if (!this.store
._itemsByIdentity
) return undefined;
11 cat
= this.store
._itemsByIdentity
['CAT:' + id
];
19 getItemById: function(id
) {
20 return this.store
._itemsByIdentity
[id
];
22 getFeedValue: function(feed
, is_cat
, key
) {
23 if (!this.store
._itemsByIdentity
) return undefined;
26 treeItem
= this.store
._itemsByIdentity
['CAT:' + feed
];
28 treeItem
= this.store
._itemsByIdentity
['FEED:' + feed
];
31 return this.store
.getValue(treeItem
, key
);
33 getFeedName: function(feed
, is_cat
) {
34 return this.getFeedValue(feed
, is_cat
, 'name');
36 getFeedUnread: function(feed
, is_cat
) {
37 var unread
= parseInt(this.getFeedValue(feed
, is_cat
, 'unread'));
38 return (isNaN(unread
)) ? 0 : unread
;
40 setFeedUnread: function(feed
, is_cat
, unread
) {
41 return this.setFeedValue(feed
, is_cat
, 'unread', parseInt(unread
));
43 setFeedValue: function(feed
, is_cat
, key
, value
) {
44 if (!value
) value
= '';
45 if (!this.store
._itemsByIdentity
) return undefined;
48 treeItem
= this.store
._itemsByIdentity
['CAT:' + feed
];
50 treeItem
= this.store
._itemsByIdentity
['FEED:' + feed
];
53 return this.store
.setValue(treeItem
, key
, value
);
55 getNextUnreadFeed: function (feed
, is_cat
) {
56 if (!this.store
._itemsByIdentity
)
60 treeItem
= this.store
._itemsByIdentity
['CAT:' + feed
];
62 treeItem
= this.store
._itemsByIdentity
['FEED:' + feed
];
65 items
= this.store
._arrayOfAllItems
;
67 for (var i
= 0; i
< items
.length
; i
++) {
68 if (items
[i
] == treeItem
) {
70 for (var j
= i
+1; j
< items
.length
; j
++) {
71 var unread
= this.store
.getValue(items
[j
], 'unread');
72 var id
= this.store
.getValue(items
[j
], 'id');
74 if (unread
> 0 && ((is_cat
&& id
.match("CAT:")) || (!is_cat
&& id
.match("FEED:")))) {
75 if( !is_cat
|| ! (this.store
.hasAttribute(items
[j
], 'parent_id') && this.store
.getValue(items
[j
], 'parent_id') == feed
) ) return items
[j
];
79 for (var j
= 0; j
< i
; j
++) {
80 var unread
= this.store
.getValue(items
[j
], 'unread');
81 var id
= this.store
.getValue(items
[j
], 'id');
83 if (unread
> 0 && ((is_cat
&& id
.match("CAT:")) || (!is_cat
&& id
.match("FEED:")))) {
84 if( !is_cat
|| ! (this.store
.hasAttribute(items
[j
], 'parent_id') && this.store
.getValue(items
[j
], 'parent_id') == feed
) ) return items
[j
];
93 if (this.store
&& this.store
._itemsByIdentity
)
94 return this.store
._itemsByIdentity
['CAT:-1'] != undefined;
100 dojo
.declare("fox.FeedTree", dijit
.Tree
, {
101 _onKeyPress: function(/* Event */ e
) {
102 return; // Stop dijit.Tree from interpreting keystrokes
104 _createTreeNode: function(args
) {
105 var tnode
= new dijit
._TreeNode(args
);
107 if (args
.item
.icon
&& args
.item
.icon
[0])
108 tnode
.iconNode
.src
= args
.item
.icon
[0];
110 var id
= args
.item
.id
[0];
111 var bare_id
= parseInt(id
.substr(id
.indexOf(':')+1));
113 if (bare_id
< _label_base_index
) {
114 var span
= dojo
.doc
.createElement('span');
115 var fg_color
= args
.item
.fg_color
[0];
116 var bg_color
= args
.item
.bg_color
[0];
118 span
.innerHTML
= "α";
119 span
.className
= 'labelColorIndicator';
122 backgroundColor
: bg_color
});
124 dojo
.place(span
, tnode
.iconNode
, 'replace');
127 if (id
.match("FEED:")) {
128 var menu
= new dijit
.Menu();
129 menu
.row_id
= bare_id
;
131 menu
.addChild(new dijit
.MenuItem({
132 label
: __("Mark as read"),
133 onClick: function() {
134 catchupFeed(this.getParent().row_id
);
138 menu
.addChild(new dijit
.MenuItem({
139 label
: __("Edit feed"),
140 onClick: function() {
141 editFeed(this.getParent().row_id
, false);
144 /* menu.addChild(new dijit.MenuItem({
145 label: __("Update feed"),
146 onClick: function() {
147 heduleFeedUpdate(this.getParent().row_id, false);
151 menu
.bindDomNode(tnode
.domNode
);
155 if (id
.match("CAT:") && bare_id
>= 0) {
156 var menu
= new dijit
.Menu();
157 menu
.row_id
= bare_id
;
159 menu
.addChild(new dijit
.MenuItem({
160 label
: __("Mark as read"),
161 onClick: function() {
162 catchupFeed(this.getParent().row_id
, true);
165 menu
.bindDomNode(tnode
.domNode
);
169 if (id
.match("CAT:")) {
170 loading
= dojo
.doc
.createElement('img');
171 loading
.className
= 'loadingNode';
172 loading
.src
= 'images/blank_icon.gif';
173 dojo
.place(loading
, tnode
.labelNode
, 'after');
174 tnode
.loadingNode
= loading
;
177 if (id
.match("CAT:") && bare_id
== -1) {
178 var menu
= new dijit
.Menu();
179 menu
.row_id
= bare_id
;
181 menu
.addChild(new dijit
.MenuItem({
182 label
: __("Mark all feeds as read"),
183 onClick: function() {
187 menu
.bindDomNode(tnode
.domNode
);
191 ctr
= dojo
.doc
.createElement('span');
192 ctr
.className
= 'counterNode';
193 ctr
.innerHTML
= args
.item
.unread
> 0 ? args
.item
.unread
: args
.item
.auxcounter
;
195 //args.item.unread > 0 ? ctr.addClassName("unread") : ctr.removeClassName("unread");
197 args
.item
.unread
> 0 || args
.item
.auxcounter
> 0 ? Element
.show(ctr
) : Element
.hide(ctr
);
199 args
.item
.unread
== 0 && args
.item
.auxcounter
> 0 ? ctr
.addClassName("aux") : ctr
.removeClassName("aux");
201 dojo
.place(ctr
, tnode
.rowNode
, 'first');
202 tnode
.counterNode
= ctr
;
204 //tnode.labelNode.innerHTML = args.label;
207 postCreate: function() {
208 this.connect(this.model
, "onChange", "updateCounter");
209 this.connect(this, "_expandNode", function() {
210 this.hideRead(getInitParam("hide_read_feeds"), getInitParam("hide_read_shows_special"));
213 this.inherited(arguments
);
215 updateCounter: function (item
) {
218 //console.log("updateCounter: " + item.id[0] + " " + item.unread + " " + tree);
220 var node
= tree
._itemNodesMap
[item
.id
];
225 if (node
.counterNode
) {
226 ctr
= node
.counterNode
;
227 ctr
.innerHTML
= item
.unread
> 0 ? item
.unread
: item
.auxcounter
;
228 item
.unread
> 0 || item
.auxcounter
> 0 ?
229 Effect
.Appear(ctr
, {duration
: 0.3,
230 queue
: { position
: 'end', scope
: 'CAPPEAR-' + item
.id
, limit
: 1 }}) :
233 item
.unread
== 0 && item
.auxcounter
> 0 ? ctr
.addClassName("aux") : ctr
.removeClassName("aux");
239 getTooltip: function (item
) {
245 getIconClass: function (item
, opened
) {
246 return (!item
|| this.model
.mayHaveChildren(item
)) ? (opened
? "dijitFolderOpened" : "dijitFolderClosed") : "feedIcon";
248 getLabelClass: function (item
, opened
) {
249 return (item
.unread
== 0) ? "dijitTreeLabel" : "dijitTreeLabel Unread";
251 getRowClass: function (item
, opened
) {
252 var rc
= (!item
.error
|| item
.error
== '') ? "dijitTreeRow" :
253 "dijitTreeRow Error";
255 if (item
.unread
> 0) rc
+= " Unread";
259 getLabel: function(item
) {
260 var name
= String(item
.name
);
263 name
= name
.replace(/"/g, "\"");
264 name
= name
.replace(/&/g, "&");
265 name
= name
.replace(/—/g, "-");
266 name
= name
.replace(/</g, "<");
267 name
= name
.replace(/>/g, ">");
271 if (item.unread > 0) {
272 label = name + " (" + item.unread + ")";
279 expandParentNodes: function(feed
, is_cat
, list
) {
281 for (var i
= 0; i
< list
.length
; i
++) {
282 var id
= String(list
[i
].id
);
283 var item
= this._itemNodesMap
[id
];
287 this._expandNode(item
);
291 exception_error("expandParentNodes", e
);
294 findNodeParentsAndExpandThem: function(feed
, is_cat
, root
, parents
) {
295 // expands all parents of specified feed to properly mark it as active
296 // my fav thing about frameworks is doing everything myself
298 var test_id
= is_cat
? 'CAT:' + feed
: 'FEED:' + feed
;
301 if (!this.model
|| !this.model
.store
) return false;
303 var items
= this.model
.store
._arrayOfTopLevelItems
;
305 for (var i
= 0; i
< items
.length
; i
++) {
306 if (String(items
[i
].id
) == test_id
) {
307 this.expandParentNodes(feed
, is_cat
, parents
);
309 this.findNodeParentsAndExpandThem(feed
, is_cat
, items
[i
], []);
316 for (var i
= 0; i
< root
.items
.length
; i
++) {
317 if (String(root
.items
[i
].id
) == test_id
) {
318 this.expandParentNodes(feed
, is_cat
, parents
);
320 this.findNodeParentsAndExpandThem(feed
, is_cat
, root
.items
[i
], parents
.slice(0));
324 if (String(root
.id
) == test_id
) {
325 this.expandParentNodes(feed
, is_cat
, parents
.slice(0));
330 exception_error("findNodeParentsAndExpandThem", e
);
333 selectFeed: function(feed
, is_cat
) {
334 this.findNodeParentsAndExpandThem(feed
, is_cat
, false, false);
337 treeNode
= this._itemNodesMap
['CAT:' + feed
];
339 treeNode
= this._itemNodesMap
['FEED:' + feed
];
342 treeNode
= treeNode
[0];
343 if (!is_cat
) this._expandNode(treeNode
);
344 this.set("selectedNodes", [treeNode
]);
347 setFeedIcon: function(feed
, is_cat
, src
) {
349 treeNode
= this._itemNodesMap
['CAT:' + feed
];
351 treeNode
= this._itemNodesMap
['FEED:' + feed
];
354 treeNode
= treeNode
[0];
355 treeNode
.iconNode
.src
= src
;
360 setFeedExpandoIcon: function(feed
, is_cat
, src
) {
362 treeNode
= this._itemNodesMap
['CAT:' + feed
];
364 treeNode
= this._itemNodesMap
['FEED:' + feed
];
367 treeNode
= treeNode
[0];
368 if (treeNode
.loadingNode
) {
369 treeNode
.loadingNode
.src
= src
;
372 treeNode
.expandoNode
.src
= src
;
379 hasCats: function() {
380 return this.model
.hasCats();
382 hideReadCat: function (cat
, hide
, show_special
) {
383 if (this.hasCats()) {
386 if (cat
&& cat
.items
) {
387 var cat_unread
= tree
.hideReadFeeds(cat
.items
, hide
, show_special
);
389 var id
= String(cat
.id
);
390 var node
= tree
._itemNodesMap
[id
];
391 var bare_id
= parseInt(id
.substr(id
.indexOf(":")+1));
394 var check_unread
= tree
.model
.getFeedUnread(bare_id
, true);
396 if (hide
&& cat_unread
== 0 && check_unread
== 0 && (id
!= "CAT:-1" || !show_special
)) {
397 Effect
.Fade(node
[0].rowNode
, {duration
: 0.3,
398 queue
: { position
: 'end', scope
: 'FFADE-' + id
, limit
: 1 }});
400 Element
.show(node
[0].rowNode
);
407 hideRead: function (hide
, show_special
) {
408 if (this.hasCats()) {
411 var cats
= this.model
.store
._arrayOfTopLevelItems
;
413 cats
.each(function(cat
) {
414 tree
.hideReadCat(cat
, hide
, show_special
);
418 this.hideReadFeeds(this.model
.store
._arrayOfTopLevelItems
, hide
,
422 hideReadFeeds: function (items
, hide
, show_special
) {
426 items
.each(function(feed
) {
427 var id
= String(feed
.id
);
429 // it's a subcategory
431 tree
.hideReadCat(feed
, hide
, show_special
);
432 } else { // it's a feed
433 var bare_id
= parseInt(feed
.bare_id
);;
435 var unread
= feed
.unread
[0];
436 var has_error
= feed
.error
[0] != '';
437 var node
= tree
._itemNodesMap
[id
];
440 if (hide
&& unread
== 0 && !has_error
&& (bare_id
> 0 || bare_id
< _label_base_index
|| !show_special
)) {
441 Effect
.Fade(node
[0].rowNode
, {duration
: 0.3,
442 queue
: { position
: 'end', scope
: 'FFADE-' + id
, limit
: 1 }});
444 Element
.show(node
[0].rowNode
);
453 collapseCat: function(id
) {
454 if (!this.model
.hasCats()) return;
458 var node
= tree
._itemNodesMap
['CAT:' + id
][0];
459 var item
= tree
.model
.store
._itemsByIdentity
['CAT:' + id
];
462 if (!node
.isExpanded
)
463 tree
._expandNode(node
);
465 tree
._collapseNode(node
);
469 getVisibleUnreadFeeds: function() {
470 var items
= this.model
.store
._arrayOfAllItems
;
473 for (var i
= 0; i
< items
.length
; i
++) {
474 var id
= String(items
[i
].id
);
475 var box
= this._itemNodesMap
[id
];
478 var row
= box
[0].rowNode
;
482 cat
= box
[0].rowNode
.parentNode
.parentNode
;
486 if (Element
.visible(row
) && (!cat
|| Element
.visible(cat
))) {
487 var feed_id
= String(items
[i
].bare_id
);
488 var is_cat
= !id
.match('FEED:');
489 var unread
= this.model
.getFeedUnread(feed_id
, is_cat
);
492 rv
.push([feed_id
, is_cat
]);
501 getNextFeed: function (feed
, is_cat
) {
503 treeItem
= this.model
.store
._itemsByIdentity
['CAT:' + feed
];
505 treeItem
= this.model
.store
._itemsByIdentity
['FEED:' + feed
];
508 items
= this.model
.store
._arrayOfAllItems
;
511 for (var i
= 0; i
< items
.length
; i
++) {
512 if (items
[i
] == treeItem
) {
514 for (var j
= i
+1; j
< items
.length
; j
++) {
515 var id
= String(items
[j
].id
);
516 var box
= this._itemNodesMap
[id
];
519 var row
= box
[0].rowNode
;
520 var cat
= box
[0].rowNode
.parentNode
.parentNode
;
522 if (Element
.visible(cat
) && Element
.visible(row
)) {
533 return [this.model
.store
.getValue(item
, 'bare_id'),
534 !this.model
.store
.getValue(item
, 'id').match('FEED:')];
539 getPreviousFeed: function (feed
, is_cat
) {
541 treeItem
= this.model
.store
._itemsByIdentity
['CAT:' + feed
];
543 treeItem
= this.model
.store
._itemsByIdentity
['FEED:' + feed
];
546 items
= this.model
.store
._arrayOfAllItems
;
547 var item
= items
[0] == treeItem
? items
[items
.length
-1] : items
[0];
549 for (var i
= 0; i
< items
.length
; i
++) {
550 if (items
[i
] == treeItem
) {
552 for (var j
= i
-1; j
> 0; j
--) {
553 var id
= String(items
[j
].id
);
554 var box
= this._itemNodesMap
[id
];
557 var row
= box
[0].rowNode
;
558 var cat
= box
[0].rowNode
.parentNode
.parentNode
;
560 if (Element
.visible(cat
) && Element
.visible(row
)) {
572 return [this.model
.store
.getValue(item
, 'bare_id'),
573 !this.model
.store
.getValue(item
, 'id').match('FEED:')];
579 getFeedCategory: function(feed
) {
581 return this.getNodesByItem(this.model
.store
.
582 _itemsByIdentity
["FEED:" + feed
])[0].
583 getParent().item
.bare_id
[0];