]> git.wh0rd.org Git - tt-rss.git/blob - tt-rss.js
hideOrShowFeedList: fix
[tt-rss.git] / tt-rss.js
1 var xmlhttp = false;
2 var total_unread = 0;
3 var first_run = true;
4 var display_tags = false;
5 var global_unread = -1;
6 var active_title_text = "";
7 var current_subtitle = "";
8 var daemon_enabled = false;
9 var daemon_refresh_only = false;
10 var _qfd_deleted_feed = 0;
11 var firsttime_update = true;
12 var last_refetch = 0;
13 var cookie_lifetime = 0;
14 var active_feed_id = 0;
15 var active_feed_is_cat = false;
16 var number_of_feeds = 0;
17 var sanity_check_done = false;
18
19 var xmlhttp = Ajax.getTransport();
20 var xmlhttp_ctr = Ajax.getTransport();
21
22 var init_params = new Object();
23
24 var op_history = new Array();
25
26 function tagsAreDisplayed() {
27         return display_tags;
28 }
29
30 function toggleTags() {
31         display_tags = !display_tags;
32
33         var p = document.getElementById("dispSwitchPrompt");
34
35         if (display_tags) {
36                 p.innerHTML = __("display feeds");
37         } else {
38                 p.innerHTML = __("display tags");
39         }
40         
41         notify_progress("Loading, please wait...");
42
43         updateFeedList();
44 }
45
46 function dlg_frefresh_callback() {
47         if (xmlhttp.readyState == 4) {          
48 //              notify(xmlhttp.responseText);
49
50                 if (getActiveFeedId() == _qfd_deleted_feed) {
51                         var h = document.getElementById("headlines-frame");
52                         if (h) {
53                                 h.innerHTML = "<div class='whiteBox'>No feed selected.</div>";
54                         }
55                 }
56
57                 setTimeout('updateFeedList(false, false)', 50);
58                 closeInfoBox();
59         } 
60 }
61
62 function refetch_callback() {
63         if (xmlhttp_ctr.readyState == 4) {
64                 try {
65
66                         var date = new Date();
67
68                         last_refetch = date.getTime() / 1000;
69
70                         parse_counters_reply(xmlhttp_ctr, true);
71
72                         debug("refetch_callback: done");
73
74                         if (!daemon_enabled && !daemon_refresh_only) {
75                                 notify_info("All feeds updated.");
76                                 updateTitle("");
77                         } else {
78                                 //notify("");
79                         }
80                 } catch (e) {
81                         exception_error("refetch_callback", e);
82                         updateTitle("");
83                 }
84         }
85 }
86
87 function backend_sanity_check_callback() {
88
89         if (xmlhttp.readyState == 4) {
90
91                 try {
92         
93                         if (sanity_check_done) {
94                                 fatalError(11, "Sanity check request received twice. This can indicate "+
95                               "presence of Firebug or some other disrupting extension. "+
96                                         "Please disable it and try again.");
97                                 return;
98                         }
99
100                         if (!xmlhttp.responseXML) {
101                                 fatalError(3, "[D001, Received reply is not XML]: " + xmlhttp.responseText);
102                                 return;
103                         }
104         
105                         var reply = xmlhttp.responseXML.firstChild.firstChild;
106         
107                         if (!reply) {
108                                 fatalError(3, "[D002, Invalid RPC reply]: " + xmlhttp.responseText);
109                                 return;
110                         }
111         
112                         var error_code = reply.getAttribute("error-code");
113                 
114                         if (error_code && error_code != 0) {
115                                 return fatalError(error_code, reply.getAttribute("error-msg"));
116                         }
117         
118                         debug("sanity check ok");
119
120                         var params = reply.nextSibling;
121
122                         if (params) {
123                                 debug('reading init-params...');
124                                 var param = params.firstChild;
125
126                                 while (param) {
127                                         var k = param.getAttribute("key");
128                                         var v = param.getAttribute("value");
129                                         debug(k + " => " + v);
130                                         init_params[k] = v;                                     
131                                         param = param.nextSibling;
132                                 }
133                         }
134
135                         sanity_check_done = true;
136
137                         init_second_stage();
138
139                 } catch (e) {
140                         exception_error("backend_sanity_check_callback", e);
141                 }
142         } 
143 }
144
145 function scheduleFeedUpdate(force) {
146
147         if (!daemon_enabled && !daemon_refresh_only) {
148                 notify_progress("Updating feeds, please wait.", true);
149                 updateTitle("Updating");
150         }
151
152         var query_str = "backend.php?op=rpc&subop=";
153
154         if (force) {
155                 query_str = query_str + "forceUpdateAllFeeds";
156         } else {
157                 query_str = query_str + "updateAllFeeds";
158         }
159
160         var omode;
161
162         if (firsttime_update && !navigator.userAgent.match("Opera")) {
163                 firsttime_update = false;
164                 omode = "T";
165         } else {
166                 if (display_tags) {
167                         omode = "tl";
168                 } else {
169                         omode = "flc";
170                 }
171         }
172         
173         query_str = query_str + "&omode=" + omode;
174         query_str = query_str + "&uctr=" + global_unread;
175
176         debug("in scheduleFeedUpdate");
177
178         var date = new Date();
179
180         var timestamp = Math.round(date.getTime() / 1000);
181         query_str = query_str + "&ts=" + timestamp
182
183         if (!xmlhttp_ready(xmlhttp_ctr) && last_refetch < date.getTime() / 1000 - 60) {
184                 debug("<b>xmlhttp seems to be stuck, aborting</b>");
185                 xmlhttp_ctr.abort();
186         }
187
188         debug("REFETCH query: " + query_str);
189
190         if (xmlhttp_ready(xmlhttp_ctr)) {
191                 xmlhttp_ctr.open("GET", query_str, true);
192                 xmlhttp_ctr.onreadystatechange=refetch_callback;
193                 xmlhttp_ctr.send(null);
194         } else {
195                 debug("xmlhttp_ctr busy");
196                 //printLockingError();
197         }   
198 }
199
200 function updateFeedList(silent, fetch) {
201
202 //      if (silent != true) {
203 //              notify("Loading feed list...");
204 //      }
205
206         debug("<b>updateFeedList</b>");
207
208         var query_str = "backend.php?op=feeds";
209
210         if (display_tags) {
211                 query_str = query_str + "&tags=1";
212         }
213
214         if (getActiveFeedId() && !activeFeedIsCat()) {
215                 query_str = query_str + "&actid=" + getActiveFeedId();
216         }
217
218         var date = new Date();
219         var timestamp = Math.round(date.getTime() / 1000);
220         query_str = query_str + "&ts=" + timestamp
221         
222         if (fetch) query_str = query_str + "&fetch=yes";
223
224 //      var feeds_frame = document.getElementById("feeds-frame");
225 //      feeds_frame.src = query_str;
226
227         debug("updateFeedList Q=" + query_str);
228
229         if (xmlhttp_ready(xmlhttp)) {
230                 xmlhttp.open("GET", query_str, true);
231                 xmlhttp.onreadystatechange=feedlist_callback;
232                 xmlhttp.send(null);
233         } else {
234                 debug("xmlhttp busy");
235                 //printLockingError();
236         }   
237
238 }
239
240 function catchupAllFeeds() {
241
242         var query_str = "backend.php?op=feeds&subop=catchupAll";
243
244         notify_progress("Marking all feeds as read...");
245
246         debug("catchupAllFeeds Q=" + query_str);
247
248         if (xmlhttp_ready(xmlhttp)) {
249                 xmlhttp.open("GET", query_str, true);
250                 xmlhttp.onreadystatechange=feedlist_callback;
251                 xmlhttp.send(null);
252         } else {
253                 debug("xmlhttp busy");
254                 //printLockingError();
255         }   
256
257         global_unread = 0;
258         updateTitle("");
259
260 }
261
262 function viewCurrentFeed(subop) {
263
264 //      if (getActiveFeedId()) {
265         if (getActiveFeedId() != undefined) {
266                 viewfeed(getActiveFeedId(), subop);
267         } else {
268                 disableContainerChildren("headlinesToolbar", false, document);
269 //              viewfeed(-1, subop); // FIXME
270         }
271         return false; // block unneeded form submits
272 }
273
274 function viewfeed(feed, subop) {
275         var f = window.frames["feeds-frame"];
276         f.viewfeed(feed, subop);
277 }
278
279 function timeout() {
280         scheduleFeedUpdate(false);
281
282         var refresh_time = getInitParam("feeds_frame_refresh");
283
284         if (!refresh_time) refresh_time = 600; 
285
286         setTimeout("timeout()", refresh_time*1000);
287 }
288
289 function resetSearch() {
290         var searchbox = document.getElementById("searchbox")
291
292         if (searchbox.value != "" && getActiveFeedId()) {       
293                 searchbox.value = "";
294                 viewfeed(getActiveFeedId(), "");
295         }
296 }
297
298 function searchCancel() {
299         closeInfoBox(true);
300 }
301
302 function search() {
303         closeInfoBox(); 
304         viewCurrentFeed(0, "");
305 }
306
307 function localPiggieFunction(enable) {
308         if (enable) {
309                 var query_str = "backend.php?op=feeds&subop=piggie";
310
311                 if (xmlhttp_ready(xmlhttp)) {
312
313                         xmlhttp.open("GET", query_str, true);
314                         xmlhttp.onreadystatechange=feedlist_callback;
315                         xmlhttp.send(null);
316                 }
317         }
318 }
319
320 // if argument is undefined, current subtitle is not updated
321 // use blank string to clear subtitle
322 function updateTitle(s) {
323         var tmp = "Tiny Tiny RSS";
324
325         if (s != undefined) {
326                 current_subtitle = s;
327         }
328
329         if (global_unread > 0) {
330                 tmp = tmp + " (" + global_unread + ")";
331         }
332
333         if (current_subtitle) {
334                 tmp = tmp + " - " + current_subtitle;
335         }
336
337         if (active_title_text.length > 0) {
338                 tmp = tmp + " > " + active_title_text;
339         }
340
341         document.title = tmp;
342 }
343
344 function genericSanityCheck() {
345
346         if (!xmlhttp) fatalError(1);
347
348         setCookie("ttrss_vf_test", "TEST");
349         
350         if (getCookie("ttrss_vf_test") != "TEST") {
351                 fatalError(2);
352         }
353
354         return true;
355 }
356
357 function init() {
358
359         try {
360
361                 // this whole shebang is based on http://www.birnamdesigns.com/misc/busted2.html
362
363                 if (arguments.callee.done) return;
364                 arguments.callee.done = true;           
365
366                 disableContainerChildren("headlinesToolbar", true);
367
368                 Form.disable("main_toolbar_form");
369
370                 if (!genericSanityCheck()) 
371                         return;
372
373                 if (getURLParam('debug')) {
374                         document.getElementById('debug_output').style.display = 'block';
375                         debug('debug mode activated');
376                 }
377
378                 var params = "&ua=" + param_escape(navigator.userAgent);
379
380                 xmlhttp.open("GET", "backend.php?op=rpc&subop=sanityCheck" + params, true);
381                 xmlhttp.onreadystatechange=backend_sanity_check_callback;
382                 xmlhttp.send(null);
383
384         } catch (e) {
385                 exception_error("init", e);
386         }
387 }
388
389 function resize_headlines() {
390
391         var h_frame = document.getElementById("headlines-frame");
392         var c_frame = document.getElementById("content-frame");
393         var f_frame = document.getElementById("footer");
394
395         if (!c_frame || !h_frame) return;
396
397         debug("resize_headlines");
398
399         if (!is_msie()) {
400                 h_frame.style.height = 30 + "%";
401                 c_frame.style.top = h_frame.offsetTop + h_frame.offsetHeight + 1 + "px";
402                 h_frame.style.height = h_frame.offsetHeight + "px";
403         } else {
404                 h_frame.style.height = document.documentElement.clientHeight * 0.3 + "px";
405                 c_frame.style.top = h_frame.offsetTop + h_frame.offsetHeight + 1 + "px";
406
407                 var c_bottom = document.documentElement.clientHeight;
408
409                 if (f_frame) {
410                         c_bottom = f_frame.offsetTop;
411                 }
412
413                 c_frame.style.height = c_bottom - (h_frame.offsetTop + 
414                         h_frame.offsetHeight + 1) + "px";
415                 h_frame.style.height = h_frame.offsetHeight + "px";
416
417         }
418 }
419
420 function init_second_stage() {
421
422         try {
423
424                 cookie_lifetime = getCookie("ttrss_cltime");
425
426                 delCookie("ttrss_vf_test");
427
428 //              document.onresize = resize_headlines;
429                 resize_headlines();
430
431                 var toolbar = document.forms["main_toolbar_form"];
432
433                 dropboxSelect(toolbar.view_mode, getInitParam("default_view_mode"));
434                 dropboxSelect(toolbar.limit, getInitParam("default_view_limit"));
435
436                 daemon_enabled = getInitParam("daemon_enabled") == 1;
437                 daemon_refresh_only = getInitParam("daemon_refresh_only") == 1;
438
439                 setTimeout('updateFeedList(false, false)', 50);
440
441                 debug("second stage ok");
442         
443         } catch (e) {
444                 exception_error("init_second_stage", e);
445         }
446 }
447
448 function quickMenuChange() {
449         var chooser = document.getElementById("quickMenuChooser");
450         var opid = chooser[chooser.selectedIndex].value;
451
452         chooser.selectedIndex = 0;
453         quickMenuGo(opid);
454 }
455
456 function quickMenuGo(opid) {
457         try {
458
459                 if (opid == "qmcPrefs") {
460                         gotoPreferences();
461                 }
462         
463                 if (opid == "qmcSearch") {
464                         displayDlg("search", getActiveFeedId() + ":" + activeFeedIsCat());
465                         return;
466                 }
467         
468                 if (opid == "qmcAddFeed") {
469                         displayDlg("quickAddFeed");
470                         return;
471                 }
472
473                 if (opid == "qmcEditFeed") {
474                         editFeedDlg(getActiveFeedId());
475                 }
476         
477                 if (opid == "qmcRemoveFeed") {
478                         var actid = getActiveFeedId();
479         
480                         if (!actid || activeFeedIsCat()) {
481                                 alert("Please select some feed first.");
482                                 return;
483                         }
484
485                         var fn = getFeedName(actid);
486         
487                         if (confirm("Unsubscribe from " + fn + "?")) {
488                                 qfdDelete(actid);
489                         }
490                 
491                         return;
492                 }
493         
494                 if (opid == "qmcUpdateFeeds") {
495                         scheduleFeedUpdate(true);
496                         return;
497                 }
498         
499                 if (opid == "qmcCatchupAll") {
500                         catchupAllFeeds();
501                         return;
502                 }
503         
504                 if (opid == "qmcShowOnlyUnread") {
505                         toggleDispRead();
506                         return;
507                 }
508         
509                 if (opid == "qmcAddFilter") {
510                         displayDlg("quickAddFilter", getActiveFeedId());
511                 }
512         } catch (e) {
513                 exception_error("quickMenuGo", e);
514         }
515 }
516
517 function qfdDelete(feed_id) {
518
519         notify_progress("Removing feed...");
520
521         if (!xmlhttp_ready(xmlhttp)) {
522                 printLockingError();
523                 return
524         }
525
526         _qfd_deleted_feed = feed_id;
527
528         xmlhttp.open("GET", "backend.php?op=pref-feeds&quiet=1&subop=remove&ids=" + feed_id);
529         xmlhttp.onreadystatechange=dlg_frefresh_callback;
530         xmlhttp.send(null);
531
532         return false;
533 }
534
535
536 function updateFeedTitle(t) {
537         active_title_text = t;
538         updateTitle();
539 }
540
541 function toggleDispRead() {
542         try {
543
544                 if (!xmlhttp_ready(xmlhttp)) {
545                         printLockingError();
546                         return
547                 } 
548
549                 var hide_read_feeds = (getInitParam("hide_read_feeds") == "1");
550
551                 hide_read_feeds = !hide_read_feeds;
552
553                 debug("toggle_disp_read => " + hide_read_feeds);
554
555                 hideOrShowFeeds(getFeedsContext().document, hide_read_feeds);
556
557                 storeInitParam("hide_read_feeds", hide_read_feeds, true);
558
559 /*              var query = "backend.php?op=rpc&subop=setpref" +
560                         "&key=HIDE_READ_FEEDS&value=" + param_escape(hide_read_feeds);
561
562                 new Ajax.Request(query); */
563                                 
564         } catch (e) {
565                 exception_error("toggleDispRead", e);
566         }
567 }
568
569 function parse_runtime_info(elem) {
570         if (!elem) {
571                 debug("parse_runtime_info: elem is null, aborting");
572                 return;
573         }
574
575         var param = elem.firstChild;
576
577         debug("parse_runtime_info: " + param);
578
579         while (param) {
580                 var k = param.getAttribute("key");
581                 var v = param.getAttribute("value");
582
583                 debug("RI: " + k + " => " + v);
584
585                 if (k == "new_version_available") {
586                         var icon = document.getElementById("newVersionIcon");
587                         if (icon) {
588                                 if (v == "1") {
589                                         icon.style.display = "inline";
590                                 } else {
591                                         icon.style.display = "none";
592                                 }
593                         }
594                 }
595
596                 if (k == "daemon_is_running" && v != 1) {
597                         notify_error("<span onclick=\"javascript:explainError(1)\">Update daemon is not runing.</span>");
598                 }
599
600 /*              var w = document.getElementById("noDaemonWarning");
601                 
602                 if (w) {
603                         if (k == "daemon_is_running" && v != 1) {
604                                 w.style.display = "block";
605                         } else {
606                                 w.style.display = "none";
607                         }
608                 } */
609                 param = param.nextSibling;
610         }
611 }
612
613 function catchupCurrentFeed() {
614
615         var fn = getFeedName(getActiveFeedId(), active_feed_is_cat);
616         
617         var str = "Mark all articles in " + fn + " as read?";
618
619 /*      if (active_feed_is_cat) {
620                 str = "Mark all articles in this category as read?";
621         } */
622
623         if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
624                 return viewCurrentFeed('MarkAllRead')
625         }
626 }
627
628 function userSwitch() {
629         var chooser = document.getElementById("userSwitch");
630         var user = chooser[chooser.selectedIndex].value;
631         window.location = "tt-rss.php?swu=" + user;
632 }
633
634 function editFeedDlg(feed) {
635
636         disableHotkeys();
637
638         if (!feed) {
639                 alert("Please select some feed first.");
640                 return;
641         }
642
643         if (feed <= 0 || active_feed_is_cat || tagsAreDisplayed()) {
644                 alert("You can't edit this kind of feed.");
645                 return;
646         }
647
648         if (xmlhttp_ready(xmlhttp)) {
649                 xmlhttp.open("GET", "backend.php?op=pref-feeds&subop=editfeed&id=" +
650                         param_escape(feed), true);
651                 xmlhttp.onreadystatechange=infobox_callback;
652                 xmlhttp.send(null);
653         } else {
654                 printLockingError();
655         }
656 }
657
658 /* this functions duplicate those of prefs.js feed editor, with
659         some differences because there is no feedlist */
660
661 function feedEditCancel() {
662         closeInfoBox();
663         return false;
664 }
665
666 function feedEditSave() {
667
668         try {
669         
670                 if (!xmlhttp_ready(xmlhttp)) {
671                         printLockingError();
672                         return
673                 }
674
675                 // FIXME: add parameter validation
676
677                 var query = Form.serialize("edit_feed_form");
678
679                 notify_progress("Saving feed...");
680
681                 xmlhttp.open("POST", "backend.php", true);
682                 xmlhttp.onreadystatechange=dlg_frefresh_callback;
683                 xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
684                 xmlhttp.send(query);
685
686                 closeInfoBox();
687
688                 return false;
689
690         } catch (e) {
691                 exception_error("feedEditSave (main)", e);
692         } 
693 }
694
695 function localHotkeyHandler(e) {
696
697         var keycode;
698
699         if (window.event) {
700                 keycode = window.event.keyCode;
701         } else if (e) {
702                 keycode = e.which;
703         }
704
705         var shift_key = false;
706
707         try {
708                 shift_key = e.shiftKey;
709         } catch (e) { }
710
711         if (keycode == 66 && shift_key) { // shift-B
712
713                 var op = history_pop();
714
715                 if (op) {
716                         var op_s = op.split(":");
717
718                         var i;
719                         for (i = 0; i < op_s.length; i++) {
720                                 if (op_s[i] == 'undefined') {
721                                         op_s[i] = undefined;
722                                 }
723
724                                 if (op_s[i] == 'false') {
725                                         op_s[i] = false;
726                                 }
727
728                                 if (op_s[i] == 'true') {
729                                         op_s[i] = true;
730                                 }
731                                 
732                         }
733
734                         debug("history split: " + op_s);
735
736                         if (op_s[0] == "ARTICLE") {
737                                 debug("history: reverting to article " + op_s[1] + "/" + op_s[2]);
738                                 view(op_s[1], op_s[2], true);
739                         }
740
741                         if (op_s[0] == "FEED") {
742                                 viewfeed(op_s[1], op_s[2], op_s[3], op_s[4], true);
743                         }
744
745                 } else {
746                         notify_error("No operation to undo");
747                 }
748
749                 return false;
750
751         }       
752
753         debug("LKP=" + keycode);
754 }
755
756 function history_push(op) {
757         debug("history_push: " + op);
758         op_history.push(op);
759
760         while (op_history.length > 30) {
761                 op_history.shift();
762         }
763 }
764
765 function history_pop() {
766         var op = op_history.pop();
767         debug("history_pop: " + op);
768         return op;
769 }
770
771 function history_clear() {
772         debug("history_clear");
773         op_history.clear();
774 }