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