]> git.wh0rd.org Git - tt-rss.git/blob - digest.js
Merge branch 'master' into digest-experimental
[tt-rss.git] / digest.js
1 var last_feeds = [];
2 var init_params = {};
3
4 var db = false;
5 var store = false;
6
7 var _active_feed_id = false;
8 var _active_feed_offset = false;
9 var _update_timeout = false;
10 var _view_update_timeout = false;
11 var _feedlist_expanded = false;
12 var _update_seq = 1;
13
14 function article_appear(article_id) {
15         try {
16                 new Effect.Appear('A-' + article_id);
17         } catch (e) {
18                 exception_error("article_appear", e);
19         }
20 }
21
22 function catchup_feed(feed_id, callback) {
23         try {
24
25                 var fn = find_feed(last_feeds, feed_id).title;
26
27                 if (confirm(__("Mark all articles in %s as read?").replace("%s", fn))) {
28
29                         var is_cat = "";
30
31                         if (feed_id < 0) is_cat = "true"; // KLUDGE
32
33                         var query = "?op=rpc&subop=catchupFeed&feed_id=" + 
34                                 feed_id + "&is_cat=" + is_cat;
35
36                         new Ajax.Request("backend.php", {
37                                 parameters: query, 
38                                 onComplete: function(transport) {
39                                         if (callback) callback(transport);
40         
41                                         update();
42                                 } });
43                 }
44
45         } catch (e) {
46                 exception_error("catchup_article", e);
47         }
48 }
49
50 function catchup_visible_articles(callback) {
51         try {
52
53                 var elems = $("headlines-content").getElementsByTagName("LI");
54                 var ids = [];
55                         
56                 for (var i = 0; i < elems.length; i++) {
57                         if (elems[i].id && elems[i].id.match("A-")) {
58                         ids.push(elems[i].id.replace("A-", ""));
59                         }
60                 }
61
62                 if (confirm(__("Mark %d displayed articles as read?").replace("%d", ids.length))) {
63
64                         var query = "?op=rpc&subop=catchupSelected" +
65                                 "&cmode=0&ids=" + param_escape(ids);
66         
67                         new Ajax.Request("backend.php", {
68                                 parameters: query, 
69                                 onComplete: function(transport) {
70                                         if (callback) callback(transport);
71         
72                                         viewfeed(_active_feed_id, 0);
73                                 } });
74         
75                         }
76
77         } catch (e) {
78                 exception_error("catchup_visible_articles", e);
79         }
80 }
81
82 function catchup_article(article_id, callback) {
83         try {
84                 var query = "?op=rpc&subop=catchupSelected" +
85                         "&cmode=0&ids=" + article_id;
86
87                 new Ajax.Request("backend.php", {
88                         parameters: query, 
89                         onComplete: function(transport) {
90                                 if (callback) callback(transport);
91                         } });
92
93         } catch (e) {
94                 exception_error("catchup_article", e);
95         }
96 }
97
98 function set_selected_feed(feed_id) {
99         try {
100                 var feeds = $("feeds-content").getElementsByTagName("LI");
101
102                 for (var i = 0; i < feeds.length; i++) {
103                         if (feeds[i].id == "F-" + feed_id)
104                                 feeds[i].className = "selected";
105                         else
106                                 feeds[i].className = "";
107                 }
108
109                 _active_feed_id = feed_id;
110
111         } catch (e) {
112                 exception_error("mark_selected_feed", e);
113         }
114 }
115
116 function zoom(elem, article_id) {
117         try {
118                 //alert(elem + "/" + article_id);
119
120                 elem.innerHTML = "<img src='images/indicator_tiny.gif'> " +
121                         __("Loading, please wait...");
122
123                 new Ajax.Request("backend.php", {
124                         parameters: "?op=rpc&subop=digest-get-contents&article_id=" +
125                                 article_id,
126                         onComplete: function(transport) {
127                                 fatal_error_check(transport);
128
129                                 if (transport.responseXML) {
130                                         var article = transport.responseXML.getElementsByTagName('article')[0];
131                                         elem.innerHTML = article.firstChild.nodeValue;
132
133                                         new Effect.BlindDown(elem, {duration : 0.5});
134
135                                         elem.onclick = false;
136                                         elem.style.cursor = "auto";
137
138                                         catchup_article(article_id, 
139                                                 function() {                                                    
140                                                         window.clearTimeout(_view_update_timeout);
141                                                         _view_update_timeout = window.setTimeout("view_update()", 500);
142                                                         $("A-" + article_id).className = "read";
143                                         });
144
145
146                                 } else {
147                                         elem.innerHTML = __("Error: unable to load article.");
148                                 }
149                                 
150                                 } });
151
152
153         } catch (e) {
154                 exception_error("zoom", e);
155         }
156 }
157
158 function load_more() {
159         try {
160                 var pr = $("H-LOADING-IMG");
161
162                 if (pr) Element.show(pr);
163
164                 viewfeed(_active_feed_id, _active_feed_offset + 10, false, false, true,
165                         function() { 
166                                 var pr = $("H-LOADING-IMG");
167
168                                 if (pr) Element.hide(pr);       
169                         });
170         } catch (e) {
171                 exception_error("load_more", e);
172         }
173 }
174
175 function update(callback) {
176         try {
177                 console.log('updating feeds...');
178
179                 window.clearTimeout(_update_timeout);
180
181                 new Ajax.Request("backend.php", {
182                         parameters: "?op=rpc&subop=digest-init",
183                         onComplete: function(transport) {
184                                 fatal_error_check(transport);
185                                 parse_feeds(transport);
186                                 set_selected_feed(_active_feed_id);
187
188                                 if (callback) callback(transport);
189                                 } });
190
191                 _update_timeout = window.setTimeout('update()', 5*1000);
192         } catch (e) {
193                 exception_error("update", e);
194         }
195 }
196
197 function remove_headline_entry(article_id) {
198         try {
199                 var elem = $('A-' + article_id);
200
201                 if (elem) {
202                         elem.parentNode.removeChild(elem);
203                 }
204
205         } catch (e) {
206                 exception_error("remove_headline_entry", e);
207         }
208 }
209
210 function view_update() {
211         try {
212                 viewfeed(_active_feed_id, _active_feed_offset, false, true, true);
213                 update();
214         } catch (e) {
215                 exception_error("view_update", e);
216         }
217 }
218
219 function view(article_id, dismiss_only) {
220         try {
221                 remove_headline_entry(article_id);
222
223                 catchup_article(article_id, 
224                         function() { 
225                                 window.clearTimeout(_view_update_timeout);
226                                 _view_update_timeout = window.setTimeout("view_update()", 500);
227                         });
228
229                 return dismiss_only != true;
230         } catch (e) {
231                 exception_error("view", e);
232         }
233 }
234
235 function viewfeed(feed_id, offset, replace, no_effects, no_indicator, callback) {
236         try {
237
238                 if (!feed_id) feed_id = _active_feed_id;
239
240                 if (!offset) {
241                         offset = 0;
242                 } else {
243                         offset = _active_feed_offset + offset;
244                 }
245
246                 if (replace == undefined) replace = (offset == 0);
247
248                 _update_seq = _update_seq + 1;
249
250                 var query = "backend.php?op=rpc&subop=digest-update&feed_id=" + 
251                                 param_escape(feed_id) + "&offset=" + offset +
252                                 "&seq=" + _update_seq;
253
254                 console.log(query);
255
256                 var img = $("F-" + feed_id).getElementsByTagName("IMG")[0];
257
258                 if (img && !no_indicator) {
259                         img.setAttribute("orig_src", img.src);
260                         img.src = 'images/indicator_tiny.gif';
261                 }
262
263                 new Ajax.Request("backend.php", {
264                         parameters: query, 
265                         onComplete: function(transport) {
266                                 Element.hide("overlay");
267
268                                 fatal_error_check(transport);
269                                 parse_headlines(transport, replace, no_effects);
270                                 set_selected_feed(feed_id);                             
271                                 _active_feed_offset = offset;
272
273                                 if (img && !no_indicator)
274                                         img.src = img.getAttribute("orig_src");
275
276                                 if (callback) callback(transport);
277
278                         } });
279
280         } catch (e) {
281                 exception_error("view", e);
282         }
283 }
284
285 function find_article(articles, article_id) {
286         try {
287                 for (var i = 0; i < articles.length; i++) {
288                         if (articles[i].id == article_id)
289                                 return articles[i];
290                 }
291
292                 return false;
293
294         } catch (e) {
295                 exception_error("find_article", e);
296         }
297 }
298
299 function find_feed(feeds, feed_id) {
300         try {
301                 for (var i = 0; i < feeds.length; i++) {
302                         if (feeds[i].id == feed_id)
303                                 return feeds[i];
304                 }
305
306                 return false;
307
308         } catch (e) {
309                 exception_error("find_feed", e);
310         }
311 }
312
313 function get_feed_icon(feed) {
314         try {
315                 if (feed.has_icon)
316                         return getInitParam('icons_location') + "/" + feed.id + '.ico';
317
318                 if (feed.id == -1)
319                         return 'images/mark_set.png';
320
321                 if (feed.id == -2)
322                         return 'images/pub_set.png';
323
324                 if (feed.id == -3)
325                         return 'images/fresh.png';
326
327                 if (feed.id == -4) 
328                         return 'images/tag.png';
329
330                 if (feed.id < -10) 
331                         return 'images/label.png';
332
333                 return 'images/blank_icon.gif';
334
335         } catch (e) {
336                 exception_error("get_feed_icon", e);
337         }
338 }
339
340 function add_feed_entry(feed) {
341         try {
342                 var icon_part = "";
343
344                 icon_part = "<img src='" + get_feed_icon(feed) + "'/>";
345
346                 var tmp_html = "<li id=\"F-"+feed.id+"\" " +
347                                 "onmouseover=\"feed_mi(this)\" onmouseout=\"feed_mo(this)\">" + 
348                         icon_part +
349                         "<a href=\"#\" onclick=\"viewfeed("+feed.id+")\">" + feed.title + "</a>" +
350                         "<div class='unread-ctr'>" + 
351                                 "<img onclick=\"catchup_feed("+feed.id+")\" title=\"" + 
352                                         __("Mark as read") + 
353                                         "\" class=\"dismiss\" style='display : none' src=\"images/digest_checkbox.png\">" +
354                                 "<span class=\"unread\">" + feed.unread + "</span>" + 
355                         "</div>" +      
356                         "</li>";
357
358                 $("feeds-content").innerHTML += tmp_html;
359
360         } catch (e) {
361                 exception_error("add_feed_entry", e);
362         }
363 }
364
365 function add_headline_entry(article, feed, no_effects) {
366         try {
367
368                 var icon_part = "";
369
370                 icon_part = "<img class='icon' src='" + get_feed_icon(feed) + "'/>";
371
372                 var mark_part = "";
373                 var publ_part = "";
374
375                 var tags_part = "";
376
377                 if (article.tags.length > 0) {
378
379                         tags_part = " " + __("in") + " ";
380
381                         for (var i = 0; i < Math.min(5, article.tags.length); i++) {
382                                 tags_part += "<a href=\"#\" onclick=\"viewfeed('" + 
383                                                 article.tags[i] + "')\">" + 
384                                         article.tags[i] + "</a>, ";
385                         }
386
387                         tags_part = tags_part.replace(/, $/, "");
388                         tags_part = "<span class=\"tags\">" + tags_part + "</span>";
389                 }
390
391                 if (article.marked)
392                         mark_part = "<img title='"+ __("Unstar article")+"' onclick=\"toggle_mark(this, "+article.id+")\" src='images/mark_set.png'>";
393                 else
394                         mark_part =     "<img title='"+__("Star article")+"' onclick=\"toggle_mark(this, "+article.id+")\" src='images/mark_unset.png'>";
395
396                 if (article.published)
397                         publ_part = "<img title='"+__("Unpublish article")+"' onclick=\"toggle_pub(this, "+article.id+")\" src='images/pub_set.png'>";
398                 else
399                         publ_part =     "<img title='"+__("Publish article")+"' onclick=\"toggle_pub(this, "+article.id+")\" src='images/pub_unset.png'>";
400
401                 var style = "";
402
403                 if (!no_effects) style = "style=\"display : none\"";
404
405                 if (article.excerpt.trim() == "")
406                         article.excerpt = __("Click to expand article.");
407
408                 var tmp_html = "<li id=\"A-"+article.id+"\" "+style+" class=\"unread\">" + 
409                         icon_part +
410
411                         "<div class='digest-check'>" +
412                                 mark_part +
413                                 publ_part +
414                                 "<img title='" + __("Mark as read") + "' onclick=\"view("+article.id+", true)\" src='images/digest_checkbox.png'>" +
415                         "</div>" + 
416                         "<a target=\"_blank\" href=\""+article.link+"\""+
417                                 "onclick=\"return view("+article.id+")\" class='title'>" + 
418                                 article.title + "</a>" +
419                         "<div class='body'>" + 
420                         "<div title=\""+__("Click to expand article")+"\" onclick=\"zoom(this, "+article.id+")\" class='excerpt'>" + 
421                                 article.excerpt + "</div>" +
422                         "<div class='info'><a href=\#\" onclick=\"viewfeed("+feed.id+")\">" + 
423                                 feed.title + "</a> " + tags_part + " @ " + 
424                                 new Date(article.updated * 1000) + "</div>" +
425                         "</div></li>";
426
427                 $("headlines-content").innerHTML += tmp_html;
428
429                 if (!no_effects)
430                         window.setTimeout('article_appear(' + article.id + ')', 100);
431
432         } catch (e) {
433                 exception_error("add_headline_entry", e);
434         }
435 }
436
437 function expand_feeds() {
438         try {
439                 _feedlist_expanded = true;
440
441                 redraw_feedlist(last_feeds);
442
443         } catch (e) {
444                 exception_error("expand_feeds", e);
445         }
446 }
447
448 function redraw_feedlist(feeds) {
449         try {
450
451                 $('feeds-content').innerHTML = "";
452
453                 var limit = 10;
454
455                 if (_feedlist_expanded) limit = feeds.length;
456
457                 for (var i = 0; i < Math.min(limit, feeds.length); i++) {
458                         add_feed_entry(feeds[i]);
459                 }
460
461                 if (feeds.length > limit) {
462                         $('feeds-content').innerHTML += "<li id='F-MORE-PROMPT'>" +
463                                 "<img src='images/blank_icon.gif'>" + 
464                                 "<a href=\"#\" onclick=\"expand_feeds()\">" +
465                                 __("%d more...").replace("%d", feeds.length-10) + 
466                                 "</a>" + "</li>";
467                 }
468
469         } catch (e) {
470                 exception_error("redraw_feedlist", e);
471         }
472 }
473
474 function parse_feeds(transport) {
475         try {
476
477                 if (!transport.responseXML) return;
478
479                 var feeds = transport.responseXML.getElementsByTagName('feeds')[0];
480
481                 if (feeds) {
482                         feeds = eval("(" + feeds.firstChild.nodeValue + ")");
483
484                         feeds.sort( function (a,b) 
485                                 { 
486                                         if (b.unread != a.unread)
487                                                 return (b.unread - a.unread) 
488                                         else
489                                                 if (a.title > b.title)
490                                                         return 1;
491                                                 else if (a.title < b.title)
492                                                         return -1;
493                                                 else
494                                                         return 0;                                       
495                                 });
496
497                         var all_articles = find_feed(feeds, -4);
498
499                         update_title(all_articles.unread);
500
501                         last_feeds = feeds;
502
503                         redraw_feedlist(feeds);
504                 }
505
506         } catch (e) {
507                 exception_error("parse_feeds", e);
508         }
509 }
510
511 function parse_headlines(transport, replace, no_effects) {
512         try {
513                 if (!transport.responseXML) return;
514
515                 var seq = transport.responseXML.getElementsByTagName('seq')[0];
516
517                 if (seq) {
518                         seq = seq.firstChild.nodeValue;
519                         if (seq != _update_seq) {
520                                 console.log("parse_headlines: wrong sequence received.");
521                                 return;
522                         }
523                 } else {
524                         return;
525                 }
526
527                 var headlines = transport.responseXML.getElementsByTagName('headlines')[0];
528                 var headlines_title = transport.responseXML.getElementsByTagName('headlines-title')[0];
529
530                 if (headlines && headlines_title) {
531                         headlines = eval("(" + headlines.firstChild.nodeValue + ")");
532
533                         var title = headlines_title.firstChild.nodeValue;
534
535                         $("headlines-title").innerHTML = title;
536
537                         if (replace) {
538                                 $('headlines-content').innerHTML = '';
539                                 Element.hide('headlines-content');
540                         }
541
542                         var pr = $('H-MORE-PROMPT');
543
544                         if (pr) pr.parentNode.removeChild(pr);
545
546                         var inserted = false;
547
548                         for (var i = 0; i < headlines.length; i++) {
549                                 
550                                 if (!$('A-' + headlines[i].id)) {
551                                         add_headline_entry(headlines[i], 
552                                                         find_feed(last_feeds, headlines[i].feed_id), !no_effects);
553
554                                         inserted = $("A-" + headlines[i].id);
555                                 }
556                         }
557
558                         if (pr) {
559                                 $('headlines-content').appendChild(pr);
560                                 if (!no_effects) new Effect.ScrollTo(inserted);
561                         } else {
562                                 $('headlines-content').innerHTML += "<li id='H-MORE-PROMPT'>" +
563                                         "<div class='body'>" +
564                                         "<a href=\"javascript:catchup_visible_articles()\">" +
565                                         __("Mark as read") + "</a> | " + 
566                                         "<a href=\"javascript:load_more()\">" +
567                                         __("Load more...") + "</a>" + 
568                                         "<img style=\"display : none\" "+
569                                                 "id=\"H-LOADING-IMG\" src='images/indicator_tiny.gif'>" +
570                                         "</div></li>";
571                         }
572
573                         if (replace && !no_effects) 
574                                 new Effect.Appear('headlines-content', {duration : 0.3});
575
576                         //new Effect.Appear('headlines-content');
577                 }
578
579         } catch (e) {
580                 exception_error("parse_headlines", e);
581         }
582 }
583
584 function init_second_stage() {
585         try {
586                 new Ajax.Request("backend.php", {
587                         parameters: "backend.php?op=rpc&subop=digest-init",
588                         onComplete: function(transport) {
589                                 parse_feeds(transport);
590                                 window.setTimeout('viewfeed(-4)', 100);
591                                 _update_timeout = window.setTimeout('update()', 5*1000);
592                                 } });
593
594         } catch (e) {
595                 exception_error("init_second_stage", e);
596         }
597 }
598
599 function init() {
600         try {
601
602                 new Ajax.Request("backend.php", {
603                         parameters: "?op=rpc&subop=sanityCheck",
604                         onComplete: function(transport) { 
605                                 backend_sanity_check_callback(transport);
606                         } });
607
608         } catch (e) {
609                 exception_error("digest_init", e);
610         }
611 }
612
613 function toggle_mark(mark_img, id) {
614
615         try {
616
617                 var query = "?op=rpc&id=" + id + "&subop=mark";
618         
619                 query = query + "&afid=" + _active_feed_id;
620                 query = query + "&omode=c";
621
622                 if (!mark_img) return;
623
624                 if (mark_img.src.match("mark_unset")) {
625                         mark_img.src = mark_img.src.replace("mark_unset", "mark_set");
626                         mark_img.alt = __("Unstar article");
627                         query = query + "&mark=1";
628                 } else {
629                         mark_img.alt = __("Please wait...");
630                         query = query + "&mark=0";
631         
632                         mark_img.src = mark_img.src.replace("mark_set", "mark_unset");
633                         mark_img.alt = __("Star article");
634                 }
635
636                 new Ajax.Request("backend.php", {
637                         parameters: query,
638                         onComplete: function(transport) { 
639                                 update();
640                         } });
641
642         } catch (e) {
643                 exception_error("toggle_mark", e);
644         }
645 }
646
647 function toggle_pub(mark_img, id, note) {
648
649         try {
650
651                 var query = "?op=rpc&id=" + id + "&subop=publ";
652         
653                 query = query + "&afid=" + _active_feed_id;
654
655                 if (note != undefined) {
656                         query = query + "&note=" + param_escape(note);
657                 } else {
658                         query = query + "&note=undefined";
659                 }
660
661                 query = query + "&omode=c";
662
663                 if (!mark_img) return;
664
665                 if (mark_img.src.match("pub_unset") || note != undefined) {
666                         mark_img.src = mark_img.src.replace("pub_unset", "pub_set");
667                         mark_img.alt = __("Unpublish article");
668                         query = query + "&pub=1";
669
670                 } else {
671                         mark_img.alt = __("Please wait...");
672                         query = query + "&pub=0";
673         
674                         mark_img.src = mark_img.src.replace("pub_set", "pub_unset");
675                         mark_img.alt = __("Publish article");
676                 }
677
678                 new Ajax.Request("backend.php", {
679                         parameters: query,
680                         onComplete: function(transport) { 
681                                 update();
682                         } });
683
684         } catch (e) {
685                 exception_error("toggle_pub", e);
686         }
687 }
688
689 function fatal_error(code, msg) {
690         try {   
691
692                 if (code == 6) {
693                         window.location.href = "digest.php";
694                 } else if (code == 5) {
695                         window.location.href = "update.php";
696                 } else {
697         
698                         if (msg == "") msg = "Unknown error";
699
700                         console.error("Fatal error: " + code + "\n" + 
701                                 msg);
702                         
703                 }
704
705         } catch (e) {
706                 exception_error("fatalError", e);
707         }
708 }
709
710 function fatal_error_check(transport) {
711         try {
712                 if (transport.responseXML) {
713                         var error = transport.responseXML.getElementsByTagName("error")[0];
714
715                         if (error) {
716                                 var code = error.getAttribute("error-code");
717                                 var msg = error.getAttribute("error-msg");
718                                 if (code != 0) {
719                                         fatal_error(code, msg);
720                                         return false;
721                                 }
722                         }
723                 }
724         } catch (e) {
725                 exception_error("fatal_error_check", e);
726         }
727         return true;
728 }
729
730 function feed_mi(elem) {
731         try {
732                 var imgs = elem.getElementsByTagName('IMG');
733                 var spans = elem.getElementsByTagName('SPAN');
734
735                 for (var i = 0; i < imgs.length; i++) {
736                         if (imgs[i].className == "dismiss")
737                                 Element.show(imgs[i]);
738                 }
739
740                 for (var i = 0; i < spans.length; i++) {
741                         if (spans[i].className == "unread")
742                                 Element.hide(spans[i]);
743                 }
744
745
746         } catch (e) {
747                 exception_error("feed_mi", e);
748         }
749 }
750
751 function feed_mo(elem) {
752         try {
753                 var imgs = elem.getElementsByTagName('IMG');
754                 var spans = elem.getElementsByTagName('SPAN');
755
756                 for (var i = 0; i < imgs.length; i++) {
757                         if (imgs[i].className == "dismiss")
758                                 Element.hide(imgs[i]);
759                 }
760
761                 for (var i = 0; i < spans.length; i++) {
762                         if (spans[i].className == "unread")
763                                 Element.show(spans[i]);
764                 }
765
766         } catch (e) {
767                 exception_error("feed_mo", e);
768         }
769 }
770
771 function update_title(unread) {
772         try {
773                 document.title = "Tiny Tiny RSS";
774
775                 if (unread > 0)
776                         document.title += " (" + unread + ")";
777
778         } catch (e) {
779                 exception_error("update_title", e);
780         }
781 }
782