]>
git.wh0rd.org - tt-rss.git/blob - js/FeedTree.js
1 require(["dojo/_base/declare", "dijit/tree/ForestStoreModel"], function (declare
) {
3 return declare("fox.FeedStoreModel", dijit
.tree
.ForestStoreModel
, {
4 getItemsInCategory: function (id
) {
5 if (!this.store
._itemsByIdentity
) return undefined;
7 cat
= this.store
._itemsByIdentity
['CAT:' + id
];
15 getItemById: function (id
) {
16 return this.store
._itemsByIdentity
[id
];
18 getFeedValue: function (feed
, is_cat
, key
) {
19 if (!this.store
._itemsByIdentity
) return undefined;
22 treeItem
= this.store
._itemsByIdentity
['CAT:' + feed
];
24 treeItem
= this.store
._itemsByIdentity
['FEED:' + feed
];
27 return this.store
.getValue(treeItem
, key
);
29 getFeedName: function (feed
, is_cat
) {
30 return this.getFeedValue(feed
, is_cat
, 'name');
32 getFeedUnread: function (feed
, is_cat
) {
33 var unread
= parseInt(this.getFeedValue(feed
, is_cat
, 'unread'));
34 return (isNaN(unread
)) ? 0 : unread
;
36 setFeedUnread: function (feed
, is_cat
, unread
) {
37 return this.setFeedValue(feed
, is_cat
, 'unread', parseInt(unread
));
39 setFeedValue: function (feed
, is_cat
, key
, value
) {
40 if (!value
) value
= '';
41 if (!this.store
._itemsByIdentity
) return undefined;
44 treeItem
= this.store
._itemsByIdentity
['CAT:' + feed
];
46 treeItem
= this.store
._itemsByIdentity
['FEED:' + feed
];
49 return this.store
.setValue(treeItem
, key
, value
);
51 getNextUnreadFeed: function (feed
, is_cat
) {
52 if (!this.store
._itemsByIdentity
)
56 treeItem
= this.store
._itemsByIdentity
['CAT:' + feed
];
58 treeItem
= this.store
._itemsByIdentity
['FEED:' + feed
];
61 items
= this.store
._arrayOfAllItems
;
63 for (var i
= 0; i
< items
.length
; i
++) {
64 if (items
[i
] == treeItem
) {
66 for (var j
= i
+ 1; j
< items
.length
; j
++) {
67 var unread
= this.store
.getValue(items
[j
], 'unread');
68 var id
= this.store
.getValue(items
[j
], 'id');
70 if (unread
> 0 && ((is_cat
&& id
.match("CAT:")) || (!is_cat
&& id
.match("FEED:")))) {
71 if (!is_cat
|| !(this.store
.hasAttribute(items
[j
], 'parent_id') && this.store
.getValue(items
[j
], 'parent_id') == feed
)) return items
[j
];
75 for (var j
= 0; j
< i
; j
++) {
76 var unread
= this.store
.getValue(items
[j
], 'unread');
77 var id
= this.store
.getValue(items
[j
], 'id');
79 if (unread
> 0 && ((is_cat
&& id
.match("CAT:")) || (!is_cat
&& id
.match("FEED:")))) {
80 if (!is_cat
|| !(this.store
.hasAttribute(items
[j
], 'parent_id') && this.store
.getValue(items
[j
], 'parent_id') == feed
)) return items
[j
];
88 hasCats: function () {
89 if (this.store
&& this.store
._itemsByIdentity
)
90 return this.store
._itemsByIdentity
['CAT:-1'] != undefined;
98 require(["dojo/_base/declare", "dojo/dom-construct", "dijit/Tree", "dijit/Menu"], function (declare
, domConstruct
) {
100 return 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 var icon
= dojo
.doc
.createElement('img');
108 if (args
.item
.icon
&& args
.item
.icon
[0]) {
109 icon
.src
= args
.item
.icon
[0];
111 icon
.src
= 'images/blank_icon.gif';
113 icon
.className
= 'tinyFeedIcon';
114 domConstruct
.place(icon
, tnode
.iconNode
, 'only');
116 var id
= args
.item
.id
[0];
117 var bare_id
= parseInt(id
.substr(id
.indexOf(':')+1));
119 if (bare_id
< _label_base_index
) {
120 var span
= dojo
.doc
.createElement('span');
121 var fg_color
= args
.item
.fg_color
[0];
122 var bg_color
= args
.item
.bg_color
[0];
124 span
.innerHTML
= "α";
125 span
.className
= 'labelColorIndicator';
128 backgroundColor
: bg_color
});
130 domConstruct
.place(span
, tnode
.iconNode
, 'only');
133 if (id
.match("FEED:")) {
134 var menu
= new dijit
.Menu();
135 menu
.row_id
= bare_id
;
137 menu
.addChild(new dijit
.MenuItem({
138 label
: __("Mark as read"),
139 onClick: function() {
140 catchupFeed(this.getParent().row_id
);
144 menu
.addChild(new dijit
.MenuItem({
145 label
: __("Edit feed"),
146 onClick: function() {
147 editFeed(this.getParent().row_id
, false);
150 /* menu.addChild(new dijit.MenuItem({
151 label: __("Update feed"),
152 onClick: function() {
153 heduleFeedUpdate(this.getParent().row_id, false);
157 menu
.bindDomNode(tnode
.domNode
);
161 if (id
.match("CAT:") && bare_id
>= 0) {
162 var menu
= new dijit
.Menu();
163 menu
.row_id
= bare_id
;
165 menu
.addChild(new dijit
.MenuItem({
166 label
: __("Mark as read"),
167 onClick: function() {
168 catchupFeed(this.getParent().row_id
, true);
171 menu
.addChild(new dijit
.MenuItem({
172 label
: __("(Un)collapse"),
173 onClick: function() {
174 dijit
.byId("feedTree").collapseCat(this.getParent().row_id
);
177 menu
.bindDomNode(tnode
.domNode
);
181 if (id
.match("CAT:")) {
182 loading
= dojo
.doc
.createElement('img');
183 loading
.className
= 'loadingNode';
184 loading
.src
= 'images/blank_icon.gif';
185 domConstruct
.place(loading
, tnode
.labelNode
, 'after');
186 tnode
.loadingNode
= loading
;
189 if (id
.match("CAT:") && bare_id
== -1) {
190 var menu
= new dijit
.Menu();
191 menu
.row_id
= bare_id
;
193 menu
.addChild(new dijit
.MenuItem({
194 label
: __("Mark all feeds as read"),
195 onClick: function() {
199 menu
.bindDomNode(tnode
.domNode
);
203 ctr
= dojo
.doc
.createElement('span');
204 ctr
.className
= 'counterNode';
205 ctr
.innerHTML
= args
.item
.unread
> 0 ? args
.item
.unread
: args
.item
.auxcounter
;
207 //args.item.unread > 0 ? ctr.addClassName("unread") : ctr.removeClassName("unread");
209 args
.item
.unread
> 0 || args
.item
.auxcounter
> 0 ? Element
.show(ctr
) : Element
.hide(ctr
);
211 args
.item
.unread
== 0 && args
.item
.auxcounter
> 0 ? ctr
.addClassName("aux") : ctr
.removeClassName("aux");
213 domConstruct
.place(ctr
, tnode
.rowNode
, 'first');
214 tnode
.counterNode
= ctr
;
216 //tnode.labelNode.innerHTML = args.label;
219 postCreate: function() {
220 this.connect(this.model
, "onChange", "updateCounter");
221 this.connect(this, "_expandNode", function() {
222 this.hideRead(getInitParam("hide_read_feeds"), getInitParam("hide_read_shows_special"));
225 this.inherited(arguments
);
227 updateCounter: function (item
) {
230 //console.log("updateCounter: " + item.id[0] + " " + item.unread + " " + tree);
232 var node
= tree
._itemNodesMap
[item
.id
];
237 if (node
.counterNode
) {
238 ctr
= node
.counterNode
;
239 ctr
.innerHTML
= item
.unread
> 0 ? item
.unread
: item
.auxcounter
;
240 item
.unread
> 0 || item
.auxcounter
> 0 ?
242 Effect
.Appear(ctr
, {duration
: 0.3,
243 queue
: { position
: 'end', scope
: 'CAPPEAR-' + item
.id
, limit
: 1 }}) :
247 item
.unread
== 0 && item
.auxcounter
> 0 ? ctr
.addClassName("aux") : ctr
.removeClassName("aux");
253 getTooltip: function (item
) {
259 getIconClass: function (item
, opened
) {
260 return (!item
|| this.model
.mayHaveChildren(item
)) ? (opened
? "dijitFolderOpened" : "dijitFolderClosed") : "feedIcon";
262 getLabelClass: function (item
, opened
) {
263 return (item
.unread
== 0) ? "dijitTreeLabel" : "dijitTreeLabel Unread";
265 getRowClass: function (item
, opened
) {
266 var rc
= (!item
.error
|| item
.error
== '') ? "dijitTreeRow" :
267 "dijitTreeRow Error";
269 if (item
.unread
> 0) rc
+= " Unread";
270 if (item
.updates_disabled
> 0) rc
+= " UpdatesDisabled";
274 getLabel: function(item
) {
275 var name
= String(item
.name
);
278 name
= name
.replace(/"/g, "\"");
279 name
= name
.replace(/&/g, "&");
280 name
= name
.replace(/—/g, "-");
281 name
= name
.replace(/</g, "<");
282 name
= name
.replace(/>/g, ">");
286 if (item.unread > 0) {
287 label = name + " (" + item.unread + ")";
294 expandParentNodes: function(feed
, is_cat
, list
) {
296 for (var i
= 0; i
< list
.length
; i
++) {
297 var id
= String(list
[i
].id
);
298 var item
= this._itemNodesMap
[id
];
302 this._expandNode(item
);
309 findNodeParentsAndExpandThem: function(feed
, is_cat
, root
, parents
) {
310 // expands all parents of specified feed to properly mark it as active
311 // my fav thing about frameworks is doing everything myself
313 var test_id
= is_cat
? 'CAT:' + feed
: 'FEED:' + feed
;
316 if (!this.model
|| !this.model
.store
) return false;
318 var items
= this.model
.store
._arrayOfTopLevelItems
;
320 for (var i
= 0; i
< items
.length
; i
++) {
321 if (String(items
[i
].id
) == test_id
) {
322 this.expandParentNodes(feed
, is_cat
, parents
);
324 this.findNodeParentsAndExpandThem(feed
, is_cat
, items
[i
], []);
331 for (var i
= 0; i
< root
.items
.length
; i
++) {
332 if (String(root
.items
[i
].id
) == test_id
) {
333 this.expandParentNodes(feed
, is_cat
, parents
);
335 this.findNodeParentsAndExpandThem(feed
, is_cat
, root
.items
[i
], parents
.slice(0));
339 if (String(root
.id
) == test_id
) {
340 this.expandParentNodes(feed
, is_cat
, parents
.slice(0));
348 selectFeed: function(feed
, is_cat
) {
349 this.findNodeParentsAndExpandThem(feed
, is_cat
, false, false);
352 treeNode
= this._itemNodesMap
['CAT:' + feed
];
354 treeNode
= this._itemNodesMap
['FEED:' + feed
];
357 treeNode
= treeNode
[0];
358 if (!is_cat
) this._expandNode(treeNode
);
359 this.set("selectedNodes", [treeNode
]);
360 this.focusNode(treeNode
);
362 // focus headlines to route key events there
363 setTimeout(function() {
364 $("headlines-frame").focus();
368 setFeedIcon: function(feed
, is_cat
, src
) {
370 treeNode
= this._itemNodesMap
['CAT:' + feed
];
372 treeNode
= this._itemNodesMap
['FEED:' + feed
];
375 treeNode
= treeNode
[0];
376 var icon
= dojo
.doc
.createElement('img');
378 icon
.className
= 'tinyFeedIcon';
379 domConstruct
.place(icon
, treeNode
.iconNode
, 'only');
384 setFeedExpandoIcon: function(feed
, is_cat
, src
) {
386 treeNode
= this._itemNodesMap
['CAT:' + feed
];
388 treeNode
= this._itemNodesMap
['FEED:' + feed
];
391 treeNode
= treeNode
[0];
392 if (treeNode
.loadingNode
) {
393 treeNode
.loadingNode
.src
= src
;
396 var icon
= dojo
.doc
.createElement('img');
398 icon
.className
= 'loadingExpando';
399 domConstruct
.place(icon
, treeNode
.expandoNode
, 'only');
406 hasCats: function() {
407 return this.model
.hasCats();
409 hideReadCat: function (cat
, hide
, show_special
) {
410 if (this.hasCats()) {
413 if (cat
&& cat
.items
) {
414 var cat_unread
= tree
.hideReadFeeds(cat
.items
, hide
, show_special
);
416 var id
= String(cat
.id
);
417 var node
= tree
._itemNodesMap
[id
];
418 var bare_id
= parseInt(id
.substr(id
.indexOf(":")+1));
421 var check_unread
= tree
.model
.getFeedUnread(bare_id
, true);
423 if (hide
&& cat_unread
== 0 && check_unread
== 0 && (id
!= "CAT:-1" || !show_special
)) {
424 Effect
.Fade(node
[0].rowNode
, {duration
: 0.3,
425 queue
: { position
: 'end', scope
: 'FFADE-' + id
, limit
: 1 }});
427 Element
.show(node
[0].rowNode
);
434 hideRead: function (hide
, show_special
) {
435 if (this.hasCats()) {
438 var cats
= this.model
.store
._arrayOfTopLevelItems
;
440 cats
.each(function(cat
) {
441 tree
.hideReadCat(cat
, hide
, show_special
);
445 this.hideReadFeeds(this.model
.store
._arrayOfTopLevelItems
, hide
,
449 hideReadFeeds: function (items
, hide
, show_special
) {
453 items
.each(function(feed
) {
454 var id
= String(feed
.id
);
456 // it's a subcategory
458 tree
.hideReadCat(feed
, hide
, show_special
);
459 } else { // it's a feed
460 var bare_id
= parseInt(feed
.bare_id
);;
462 var unread
= feed
.unread
[0];
463 var has_error
= feed
.error
[0] != '';
464 var node
= tree
._itemNodesMap
[id
];
467 if (hide
&& unread
== 0 && !has_error
&& (bare_id
> 0 || bare_id
< _label_base_index
|| !show_special
)) {
468 Effect
.Fade(node
[0].rowNode
, {duration
: 0.3,
469 queue
: { position
: 'end', scope
: 'FFADE-' + id
, limit
: 1 }});
471 Element
.show(node
[0].rowNode
);
480 collapseCat: function(id
) {
481 if (!this.model
.hasCats()) return;
485 var node
= tree
._itemNodesMap
['CAT:' + id
][0];
486 var item
= tree
.model
.store
._itemsByIdentity
['CAT:' + id
];
489 if (!node
.isExpanded
)
490 tree
._expandNode(node
);
492 tree
._collapseNode(node
);
496 getVisibleUnreadFeeds: function() {
497 var items
= this.model
.store
._arrayOfAllItems
;
500 for (var i
= 0; i
< items
.length
; i
++) {
501 var id
= String(items
[i
].id
);
502 var box
= this._itemNodesMap
[id
];
505 var row
= box
[0].rowNode
;
509 cat
= box
[0].rowNode
.parentNode
.parentNode
;
513 if (Element
.visible(row
) && (!cat
|| Element
.visible(cat
))) {
514 var feed_id
= String(items
[i
].bare_id
);
515 var is_cat
= !id
.match('FEED:');
516 var unread
= this.model
.getFeedUnread(feed_id
, is_cat
);
519 rv
.push([feed_id
, is_cat
]);
528 getNextFeed: function (feed
, is_cat
) {
530 treeItem
= this.model
.store
._itemsByIdentity
['CAT:' + feed
];
532 treeItem
= this.model
.store
._itemsByIdentity
['FEED:' + feed
];
535 items
= this.model
.store
._arrayOfAllItems
;
538 for (var i
= 0; i
< items
.length
; i
++) {
539 if (items
[i
] == treeItem
) {
541 for (var j
= i
+1; j
< items
.length
; j
++) {
542 var id
= String(items
[j
].id
);
543 var box
= this._itemNodesMap
[id
];
546 var row
= box
[0].rowNode
;
547 var cat
= box
[0].rowNode
.parentNode
.parentNode
;
549 if (Element
.visible(cat
) && Element
.visible(row
)) {
560 return [this.model
.store
.getValue(item
, 'bare_id'),
561 !this.model
.store
.getValue(item
, 'id').match('FEED:')];
566 getPreviousFeed: function (feed
, is_cat
) {
568 treeItem
= this.model
.store
._itemsByIdentity
['CAT:' + feed
];
570 treeItem
= this.model
.store
._itemsByIdentity
['FEED:' + feed
];
573 items
= this.model
.store
._arrayOfAllItems
;
574 var item
= items
[0] == treeItem
? items
[items
.length
-1] : items
[0];
576 for (var i
= 0; i
< items
.length
; i
++) {
577 if (items
[i
] == treeItem
) {
579 for (var j
= i
-1; j
> 0; j
--) {
580 var id
= String(items
[j
].id
);
581 var box
= this._itemNodesMap
[id
];
584 var row
= box
[0].rowNode
;
585 var cat
= box
[0].rowNode
.parentNode
.parentNode
;
587 if (Element
.visible(cat
) && Element
.visible(row
)) {
599 return [this.model
.store
.getValue(item
, 'bare_id'),
600 !this.model
.store
.getValue(item
, 'id').match('FEED:')];
606 getFeedCategory: function(feed
) {
608 return this.getNodesByItem(this.model
.store
.
609 _itemsByIdentity
["FEED:" + feed
])[0].
610 getParent().item
.bare_id
[0];