]> git.wh0rd.org Git - tt-rss.git/blob - js/FeedTree.js
plugins: remove obsolete plugin_data/stored stuff
[tt-rss.git] / js / FeedTree.js
1 dojo.provide("fox.FeedTree");
2 dojo.provide("fox.FeedStoreModel");
3
4 dojo.require("dijit.Tree");
5 dojo.require("dijit.Menu");
6
7 dojo.declare("fox.FeedStoreModel", dijit.tree.ForestStoreModel, {
8         getItemsInCategory: function (id) {
9                 if (!this.store._itemsByIdentity) return undefined;
10
11                 cat = this.store._itemsByIdentity['CAT:' + id];
12
13                 if (cat && cat.items)
14                         return cat.items;
15                 else
16                         return undefined;
17
18         },
19         getItemById: function(id) {
20                 return this.store._itemsByIdentity[id];
21         },
22         getFeedValue: function(feed, is_cat, key) {
23                 if (!this.store._itemsByIdentity) return undefined;
24
25                 if (is_cat)
26                         treeItem = this.store._itemsByIdentity['CAT:' + feed];
27                 else
28                         treeItem = this.store._itemsByIdentity['FEED:' + feed];
29
30                 if (treeItem)
31                         return this.store.getValue(treeItem, key);
32         },
33         getFeedName: function(feed, is_cat) {
34                 return this.getFeedValue(feed, is_cat, 'name');
35         },
36         getFeedUnread: function(feed, is_cat) {
37                 var unread = parseInt(this.getFeedValue(feed, is_cat, 'unread'));
38                 return (isNaN(unread)) ? 0 : unread;
39         },
40         setFeedUnread: function(feed, is_cat, unread) {
41                 return this.setFeedValue(feed, is_cat, 'unread', parseInt(unread));
42         },
43         setFeedValue: function(feed, is_cat, key, value) {
44                 if (!value) value = '';
45                 if (!this.store._itemsByIdentity) return undefined;
46
47                 if (is_cat)
48                         treeItem = this.store._itemsByIdentity['CAT:' + feed];
49                 else
50                         treeItem = this.store._itemsByIdentity['FEED:' + feed];
51
52                 if (treeItem)
53                         return this.store.setValue(treeItem, key, value);
54         },
55         getNextUnreadFeed: function (feed, is_cat) {
56                 if (!this.store._itemsByIdentity)
57                         return null;
58
59                 if (is_cat) {
60                         treeItem = this.store._itemsByIdentity['CAT:' + feed];
61                 } else {
62                         treeItem = this.store._itemsByIdentity['FEED:' + feed];
63                 }
64
65                 items = this.store._arrayOfAllItems;
66
67                 for (var i = 0; i < items.length; i++) {
68                         if (items[i] == treeItem) {
69
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');
73
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];
76                                         }
77                                 }
78
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');
82
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];
85                                         }
86                                 }
87                         }
88                 }
89
90                 return null;
91         },
92         hasCats: function() {
93                 if (this.store && this.store._itemsByIdentity)
94                         return this.store._itemsByIdentity['CAT:-1'] != undefined;
95                 else
96                         return false;
97         },
98 });
99
100 dojo.declare("fox.FeedTree", dijit.Tree, {
101         _onKeyPress: function(/* Event */ e) {
102                 return; // Stop dijit.Tree from interpreting keystrokes
103         },
104         _createTreeNode: function(args) {
105                 var tnode = new dijit._TreeNode(args);
106
107                 if (args.item.icon && args.item.icon[0])
108                         tnode.iconNode.src = args.item.icon[0];
109
110                 var id = args.item.id[0];
111                 var bare_id = parseInt(id.substr(id.indexOf(':')+1));
112
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];
117
118                         span.innerHTML = "&alpha;";
119                         span.className = 'labelColorIndicator';
120                         span.setStyle({
121                                 color: fg_color,
122                                 backgroundColor: bg_color});
123
124                         dojo.place(span, tnode.iconNode, 'replace');
125                 }
126
127                 if (id.match("FEED:")) {
128                         var menu = new dijit.Menu();
129                         menu.row_id = bare_id;
130
131                         menu.addChild(new dijit.MenuItem({
132                                 label: __("Mark as read"),
133                                 onClick: function() {
134                                         catchupFeed(this.getParent().row_id);
135                                 }}));
136
137                         if (bare_id > 0) {
138                                 menu.addChild(new dijit.MenuItem({
139                                         label: __("Edit feed"),
140                                         onClick: function() {
141                                                 editFeed(this.getParent().row_id, false);
142                                         }}));
143
144                                 /* menu.addChild(new dijit.MenuItem({
145                                         label: __("Update feed"),
146                                         onClick: function() {
147                                                 heduleFeedUpdate(this.getParent().row_id, false);
148                                         }})); */
149                         }
150
151                         menu.bindDomNode(tnode.domNode);
152                         tnode._menu = menu;
153                 }
154
155                 if (id.match("CAT:") && bare_id >= 0) {
156                         var menu = new dijit.Menu();
157                         menu.row_id = bare_id;
158
159                         menu.addChild(new dijit.MenuItem({
160                                 label: __("Mark as read"),
161                                 onClick: function() {
162                                         catchupFeed(this.getParent().row_id, true);
163                                 }}));
164
165                         menu.bindDomNode(tnode.domNode);
166                         tnode._menu = menu;
167                 }
168
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;
175                 }
176
177                 if (id.match("CAT:") && bare_id == -1) {
178                         var menu = new dijit.Menu();
179                         menu.row_id = bare_id;
180
181                         menu.addChild(new dijit.MenuItem({
182                                 label: __("Mark all feeds as read"),
183                                 onClick: function() {
184                                         catchupAllFeeds();
185                                 }}));
186
187                         menu.bindDomNode(tnode.domNode);
188                         tnode._menu = menu;
189                 }
190
191                 ctr = dojo.doc.createElement('span');
192                 ctr.className = 'counterNode';
193                 ctr.innerHTML = args.item.unread > 0 ? args.item.unread : args.item.auxcounter;
194
195                 //args.item.unread > 0 ? ctr.addClassName("unread") : ctr.removeClassName("unread");
196
197                 args.item.unread > 0 || args.item.auxcounter > 0 ? Element.show(ctr) : Element.hide(ctr);
198
199                 args.item.unread == 0 && args.item.auxcounter > 0 ? ctr.addClassName("aux") : ctr.removeClassName("aux");
200
201                 dojo.place(ctr, tnode.rowNode, 'first');
202                 tnode.counterNode = ctr;
203
204                 //tnode.labelNode.innerHTML = args.label;
205                 return tnode;
206         },
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"));
211                 });
212
213                 this.inherited(arguments);
214         },
215         updateCounter: function (item) {
216                 var tree = this;
217
218                 //console.log("updateCounter: " + item.id[0] + " " + item.unread + " " + tree);
219
220                 var node = tree._itemNodesMap[item.id];
221
222                 if (node) {
223                         node = node[0];
224
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 }}) :
231                                                 Element.hide(ctr);
232
233                                 item.unread == 0 && item.auxcounter > 0 ? ctr.addClassName("aux") : ctr.removeClassName("aux");
234
235                         }
236                 }
237
238         },
239         getTooltip: function (item) {
240                 if (item.updated)
241                         return item.updated;
242                 else
243                         return "";
244         },
245         getIconClass: function (item, opened) {
246                 return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feedIcon";
247         },
248         getLabelClass: function (item, opened) {
249                 return (item.unread == 0) ? "dijitTreeLabel" : "dijitTreeLabel Unread";
250         },
251         getRowClass: function (item, opened) {
252                 var rc = (!item.error || item.error == '') ? "dijitTreeRow" :
253                         "dijitTreeRow Error";
254
255                 if (item.unread > 0) rc += " Unread";
256
257                 return rc;
258         },
259         getLabel: function(item) {
260                 var name = String(item.name);
261
262                 /* Horrible */
263                 name = name.replace(/&quot;/g, "\"");
264                 name = name.replace(/&amp;/g, "&");
265                 name = name.replace(/&mdash;/g, "-");
266                 name = name.replace(/&lt;/g, "<");
267                 name = name.replace(/&gt;/g, ">");
268
269                 /* var label;
270
271                 if (item.unread > 0) {
272                         label = name + " (" + item.unread + ")";
273                 } else {
274                         label = name;
275                 } */
276
277                 return name;
278         },
279         expandParentNodes: function(feed, is_cat, list) {
280                 try {
281                         for (var i = 0; i < list.length; i++) {
282                                 var id = String(list[i].id);
283                                 var item = this._itemNodesMap[id];
284
285                                 if (item) {
286                                         item = item[0];
287                                         this._expandNode(item);
288                                 }
289                         }
290                 } catch (e) {
291                         exception_error("expandParentNodes", e);
292                 }
293         },
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
297                 try {
298                         var test_id = is_cat ? 'CAT:' + feed : 'FEED:' + feed;
299
300                         if (!root) {
301                                 if (!this.model || !this.model.store) return false;
302
303                                 var items = this.model.store._arrayOfTopLevelItems;
304
305                                 for (var i = 0; i < items.length; i++) {
306                                         if (String(items[i].id) == test_id) {
307                                                 this.expandParentNodes(feed, is_cat, parents);
308                                         } else {
309                                                 this.findNodeParentsAndExpandThem(feed, is_cat, items[i], []);
310                                         }
311                                 }
312                         } else {
313                                 if (root.items) {
314                                         parents.push(root);
315
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);
319                                                 } else {
320                                                         this.findNodeParentsAndExpandThem(feed, is_cat, root.items[i], parents.slice(0));
321                                                 }
322                                         }
323                                 } else {
324                                         if (String(root.id) == test_id) {
325                                                 this.expandParentNodes(feed, is_cat, parents.slice(0));
326                                         }
327                                 }
328                         }
329                 } catch (e) {
330                         exception_error("findNodeParentsAndExpandThem", e);
331                 }
332         },
333         selectFeed: function(feed, is_cat) {
334                 this.findNodeParentsAndExpandThem(feed, is_cat, false, false);
335
336                 if (is_cat)
337                         treeNode = this._itemNodesMap['CAT:' + feed];
338                 else
339                         treeNode = this._itemNodesMap['FEED:' + feed];
340
341                 if (treeNode) {
342                         treeNode = treeNode[0];
343                         if (!is_cat) this._expandNode(treeNode);
344                         this.set("selectedNodes", [treeNode]);
345                 }
346         },
347         setFeedIcon: function(feed, is_cat, src) {
348                 if (is_cat)
349                         treeNode = this._itemNodesMap['CAT:' + feed];
350                 else
351                         treeNode = this._itemNodesMap['FEED:' + feed];
352
353                 if (treeNode) {
354                         treeNode = treeNode[0];
355                         treeNode.iconNode.src = src;
356                         return true;
357                 }
358                 return false;
359         },
360         setFeedExpandoIcon: function(feed, is_cat, src) {
361                 if (is_cat)
362                         treeNode = this._itemNodesMap['CAT:' + feed];
363                 else
364                         treeNode = this._itemNodesMap['FEED:' + feed];
365
366                 if (treeNode) {
367                         treeNode = treeNode[0];
368                         if (treeNode.loadingNode) {
369                                 treeNode.loadingNode.src = src;
370                                 return true;
371                         } else {
372                                 treeNode.expandoNode.src = src;
373                                 return true;
374                         }
375                 }
376
377                 return false;
378         },
379         hasCats: function() {
380                 return this.model.hasCats();
381         },
382         hideReadCat: function (cat, hide, show_special) {
383                 if (this.hasCats()) {
384                         var tree = this;
385
386                         if (cat && cat.items) {
387                                 var cat_unread = tree.hideReadFeeds(cat.items, hide, show_special);
388
389                                 var id = String(cat.id);
390                                 var node = tree._itemNodesMap[id];
391                                 var bare_id = parseInt(id.substr(id.indexOf(":")+1));
392
393                                 if (node) {
394                                         var check_unread = tree.model.getFeedUnread(bare_id, true);
395
396                                         if (hide && cat_unread == 0 && check_unread == 0) {
397                                                 Effect.Fade(node[0].rowNode, {duration : 0.3,
398                                                         queue: { position: 'end', scope: 'FFADE-' + id, limit: 1 }});
399                                         } else {
400                                                 Element.show(node[0].rowNode);
401                                                 ++cat_unread;
402                                         }
403                                 }
404                         }
405                 }
406         },
407         hideRead: function (hide, show_special) {
408                 if (this.hasCats()) {
409
410                         var tree = this;
411                         var cats = this.model.store._arrayOfTopLevelItems;
412
413                         cats.each(function(cat) {
414                                 tree.hideReadCat(cat, hide, show_special);
415                         });
416
417                 } else {
418                         this.hideReadFeeds(this.model.store._arrayOfTopLevelItems, hide,
419                                 show_special);
420                 }
421         },
422         hideReadFeeds: function (items, hide, show_special) {
423                 var tree = this;
424                 var cat_unread = 0;
425
426                 items.each(function(feed) {
427                         var id = String(feed.id);
428
429                         // it's a subcategory
430                         if (feed.items) {
431                                 tree.hideReadCat(feed, hide, show_special);
432                         } else {        // it's a feed
433                                 var bare_id = parseInt(feed.bare_id);;
434
435                                 var unread = feed.unread[0];
436                                 var node = tree._itemNodesMap[id];
437
438                                 if (node) {
439                                         if (hide && unread == 0 && (bare_id > 0 || bare_id < _label_base_index || !show_special)) {
440                                                 Effect.Fade(node[0].rowNode, {duration : 0.3,
441                                                         queue: { position: 'end', scope: 'FFADE-' + id, limit: 1 }});
442                                         } else {
443                                                 Element.show(node[0].rowNode);
444                                                 ++cat_unread;
445                                         }
446                                 }
447                         }
448                 });
449
450                 return cat_unread;
451         },
452         collapseCat: function(id) {
453                 if (!this.model.hasCats()) return;
454
455                 var tree = this;
456
457                 var node = tree._itemNodesMap['CAT:' + id][0];
458                 var item = tree.model.store._itemsByIdentity['CAT:' + id];
459
460                 if (node && item) {
461                         if (!node.isExpanded)
462                                 tree._expandNode(node);
463                         else
464                                 tree._collapseNode(node);
465
466                 }
467         },
468         getVisibleUnreadFeeds: function() {
469                 var items = this.model.store._arrayOfAllItems;
470                 var rv = [];
471
472                 for (var i = 0; i < items.length; i++) {
473                         var id = String(items[i].id);
474                         var box = this._itemNodesMap[id];
475
476                         if (box) {
477                                 var row = box[0].rowNode;
478                                 var cat = false;
479
480                                 try {
481                                         cat = box[0].rowNode.parentNode.parentNode;
482                                 } catch (e) { }
483
484                                 if (row) {
485                                         if (Element.visible(row) && (!cat || Element.visible(cat))) {
486                                                 var feed_id = String(items[i].bare_id);
487                                                 var is_cat = !id.match('FEED:');
488                                                 var unread = this.model.getFeedUnread(feed_id, is_cat);
489
490                                                 if (unread > 0)
491                                                         rv.push([feed_id, is_cat]);
492
493                                         }
494                                 }
495                         }
496                 }
497
498                 return rv;
499         },
500         getNextFeed: function (feed, is_cat) {
501                 if (is_cat) {
502                         treeItem = this.model.store._itemsByIdentity['CAT:' + feed];
503                 } else {
504                         treeItem = this.model.store._itemsByIdentity['FEED:' + feed];
505                 }
506
507                 items = this.model.store._arrayOfAllItems;
508                 var item = items[0];
509
510                 for (var i = 0; i < items.length; i++) {
511                         if (items[i] == treeItem) {
512
513                                 for (var j = i+1; j < items.length; j++) {
514                                         var id = String(items[j].id);
515                                         var box = this._itemNodesMap[id];
516
517                                         if (box) {
518                                                 var row = box[0].rowNode;
519                                                 var cat = box[0].rowNode.parentNode.parentNode;
520
521                                                 if (Element.visible(cat) && Element.visible(row)) {
522                                                         item = items[j];
523                                                         break;
524                                                 }
525                                         }
526                                 }
527                                 break;
528                         }
529                 }
530
531                 if (item) {
532                         return [this.model.store.getValue(item, 'bare_id'),
533                                                 !this.model.store.getValue(item, 'id').match('FEED:')];
534                 } else {
535                         return false;
536                 }
537         },
538         getPreviousFeed: function (feed, is_cat) {
539                 if (is_cat) {
540                         treeItem = this.model.store._itemsByIdentity['CAT:' + feed];
541                 } else {
542                         treeItem = this.model.store._itemsByIdentity['FEED:' + feed];
543                 }
544
545                 items = this.model.store._arrayOfAllItems;
546                 var item = items[0] == treeItem ? items[items.length-1] : items[0];
547
548                 for (var i = 0; i < items.length; i++) {
549                         if (items[i] == treeItem) {
550
551                                 for (var j = i-1; j > 0; j--) {
552                                         var id = String(items[j].id);
553                                         var box = this._itemNodesMap[id];
554
555                                         if (box) {
556                                                 var row = box[0].rowNode;
557                                                 var cat = box[0].rowNode.parentNode.parentNode;
558
559                                                 if (Element.visible(cat) && Element.visible(row)) {
560                                                         item = items[j];
561                                                         break;
562                                                 }
563                                         }
564
565                                 }
566                                 break;
567                         }
568                 }
569
570                 if (item) {
571                         return [this.model.store.getValue(item, 'bare_id'),
572                                                 !this.model.store.getValue(item, 'id').match('FEED:')];
573                 } else {
574                         return false;
575                 }
576
577         },
578         getFeedCategory: function(feed) {
579                 try {
580                         return this.getNodesByItem(this.model.store.
581                                         _itemsByIdentity["FEED:" + feed])[0].
582                                         getParent().item.bare_id[0];
583
584                 } catch (e) {
585                         return false;
586                 }
587         },
588 });