]> git.wh0rd.org - 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 } */