]> git.wh0rd.org Git - tt-rss.git/blob - tt-rss.js
tweak error message
[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         var h_frame = document.getElementById("headlines-frame");
391         var c_frame = document.getElementById("content-frame");
392
393         debug("resize_headlines");
394
395         if (c_frame && h_frame) {
396                 h_frame.style.height = 30 + "%";
397                 c_frame.style.top = h_frame.offsetTop + h_frame.offsetHeight + 1 + "px";
398                 h_frame.style.height = h_frame.offsetHeight + "px";
399         }
400 }
401
402 function init_second_stage() {
403
404         try {
405
406                 cookie_lifetime = getCookie("ttrss_cltime");
407
408                 delCookie("ttrss_vf_test");
409         
410                 if (!is_msie()) {
411                         document.onresize = resize_headlines;
412                         resize_headlines();
413                 }
414
415                 var toolbar = document.forms["main_toolbar_form"];
416
417                 dropboxSelect(toolbar.view_mode, getInitParam("default_view_mode"));
418                 dropboxSelect(toolbar.limit, getInitParam("default_view_limit"));
419
420                 daemon_enabled = getInitParam("daemon_enabled") == 1;
421                 daemon_refresh_only = getInitParam("daemon_refresh_only") == 1;
422
423                 setTimeout('updateFeedList(false, false)', 50);
424
425                 debug("second stage ok");
426         
427         } catch (e) {
428                 exception_error("init_second_stage", e);
429         }
430 }
431
432 function quickMenuChange() {
433         var chooser = document.getElementById("quickMenuChooser");
434         var opid = chooser[chooser.selectedIndex].value;
435
436         chooser.selectedIndex = 0;
437         quickMenuGo(opid);
438 }
439
440 function quickMenuGo(opid) {
441         try {
442
443                 if (opid == "qmcPrefs") {
444                         gotoPreferences();
445                 }
446         
447                 if (opid == "qmcSearch") {
448                         displayDlg("search", getActiveFeedId() + ":" + activeFeedIsCat());
449                         return;
450                 }
451         
452                 if (opid == "qmcAddFeed") {
453                         displayDlg("quickAddFeed");
454                         return;
455                 }
456
457                 if (opid == "qmcEditFeed") {
458                         editFeedDlg(getActiveFeedId());
459                 }
460         
461                 if (opid == "qmcRemoveFeed") {
462                         var actid = getActiveFeedId();
463         
464                         if (!actid || activeFeedIsCat()) {
465                                 alert("Please select some feed first.");
466                                 return;
467                         }
468
469                         var fn = getFeedName(actid);
470         
471                         if (confirm("Unsubscribe from " + fn + "?")) {
472                                 qfdDelete(actid);
473                         }
474                 
475                         return;
476                 }
477         
478                 if (opid == "qmcUpdateFeeds") {
479                         scheduleFeedUpdate(true);
480                         return;
481                 }
482         
483                 if (opid == "qmcCatchupAll") {
484                         catchupAllFeeds();
485                         return;
486                 }
487         
488                 if (opid == "qmcShowOnlyUnread") {
489                         toggleDispRead();
490                         return;
491                 }
492         
493                 if (opid == "qmcAddFilter") {
494                         displayDlg("quickAddFilter", getActiveFeedId());
495                 }
496         } catch (e) {
497                 exception_error("quickMenuGo", e);
498         }
499 }
500
501 function qfdDelete(feed_id) {
502
503         notify_progress("Removing feed...");
504
505         if (!xmlhttp_ready(xmlhttp)) {
506                 printLockingError();
507                 return
508         }
509
510         _qfd_deleted_feed = feed_id;
511
512         xmlhttp.open("GET", "backend.php?op=pref-feeds&quiet=1&subop=remove&ids=" + feed_id);
513         xmlhttp.onreadystatechange=dlg_frefresh_callback;
514         xmlhttp.send(null);
515
516         return false;
517 }
518
519
520 function updateFeedTitle(t) {
521         active_title_text = t;
522         updateTitle();
523 }
524
525 function toggleDispRead() {
526         try {
527
528                 if (!xmlhttp_ready(xmlhttp)) {
529                         printLockingError();
530                         return
531                 }
532
533                 var hide_read_feeds = (getInitParam("hide_read_feeds") == "1");
534
535                 hide_read_feeds = !hide_read_feeds;
536
537                 debug("toggle_disp_read => " + hide_read_feeds);
538
539                 hideOrShowFeeds(getFeedsContext().document, hide_read_feeds);
540
541                 var query = "backend.php?op=rpc&subop=setpref" +
542                         "&key=HIDE_READ_FEEDS&value=" + param_escape(hide_read_feeds);
543
544                 storeInitParam("hide_read_feeds", hide_read_feeds, true);
545
546                 new Ajax.Request(query);
547                 
548         } catch (e) {
549                 exception_error("toggleDispRead", e);
550         }
551 }
552
553 function parse_runtime_info(elem) {
554         if (!elem) {
555                 debug("parse_runtime_info: elem is null, aborting");
556                 return;
557         }
558
559         var param = elem.firstChild;
560
561         debug("parse_runtime_info: " + param);
562
563         while (param) {
564                 var k = param.getAttribute("key");
565                 var v = param.getAttribute("value");
566
567                 debug("RI: " + k + " => " + v);
568
569                 if (k == "new_version_available") {
570                         var icon = document.getElementById("newVersionIcon");
571                         if (icon) {
572                                 if (v == "1") {
573                                         icon.style.display = "inline";
574                                 } else {
575                                         icon.style.display = "none";
576                                 }
577                         }
578                 }
579
580                 if (k == "daemon_is_running" && v != 1) {
581                         notify_error("<span onclick=\"javascript:explainError(1)\">Update daemon is not runing.</span>");
582                 }
583
584 /*              var w = document.getElementById("noDaemonWarning");
585                 
586                 if (w) {
587                         if (k == "daemon_is_running" && v != 1) {
588                                 w.style.display = "block";
589                         } else {
590                                 w.style.display = "none";
591                         }
592                 } */
593                 param = param.nextSibling;
594         }
595 }
596
597 function catchupCurrentFeed() {
598
599         var fn = getFeedName(getActiveFeedId(), active_feed_is_cat);
600         
601         var str = "Mark all articles in " + fn + " as read?";
602
603 /*      if (active_feed_is_cat) {
604                 str = "Mark all articles in this category as read?";
605         } */
606
607         if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
608                 return viewCurrentFeed('MarkAllRead')
609         }
610 }
611
612 function userSwitch() {
613         var chooser = document.getElementById("userSwitch");
614         var user = chooser[chooser.selectedIndex].value;
615         window.location = "tt-rss.php?swu=" + user;
616 }
617
618 function editFeedDlg(feed) {
619
620         disableHotkeys();
621
622         if (!feed) {
623                 alert("Please select some feed first.");
624                 return;
625         }
626
627         if (feed <= 0 || active_feed_is_cat || tagsAreDisplayed()) {
628                 alert("You can't edit this kind of feed.");
629                 return;
630         }
631
632         if (xmlhttp_ready(xmlhttp)) {
633                 xmlhttp.open("GET", "backend.php?op=pref-feeds&subop=editfeed&id=" +
634                         param_escape(feed), true);
635                 xmlhttp.onreadystatechange=infobox_callback;
636                 xmlhttp.send(null);
637         } else {
638                 printLockingError();
639         }
640 }
641
642 /* this functions duplicate those of prefs.js feed editor, with
643         some differences because there is no feedlist */
644
645 function feedEditCancel() {
646         closeInfoBox();
647         return false;
648 }
649
650 function feedEditSave() {
651
652         try {
653         
654                 if (!xmlhttp_ready(xmlhttp)) {
655                         printLockingError();
656                         return
657                 }
658
659                 // FIXME: add parameter validation
660
661                 var query = Form.serialize("edit_feed_form");
662
663                 notify_progress("Saving feed...");
664
665                 xmlhttp.open("POST", "backend.php", true);
666                 xmlhttp.onreadystatechange=dlg_frefresh_callback;
667                 xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
668                 xmlhttp.send(query);
669
670                 closeInfoBox();
671
672                 return false;
673
674         } catch (e) {
675                 exception_error("feedEditSave (main)", e);
676         } 
677 }
678
679 function localHotkeyHandler(e) {
680
681         var keycode;
682
683         if (window.event) {
684                 keycode = window.event.keyCode;
685         } else if (e) {
686                 keycode = e.which;
687         }
688
689         var shift_key = false;
690
691         try {
692                 shift_key = e.shiftKey;
693         } catch (e) { }
694
695         if (keycode == 66 && shift_key) { // shift-B
696
697                 var op = history_pop();
698
699                 if (op) {
700                         var op_s = op.split(":");
701
702                         var i;
703                         for (i = 0; i < op_s.length; i++) {
704                                 if (op_s[i] == 'undefined') {
705                                         op_s[i] = undefined;
706                                 }
707
708                                 if (op_s[i] == 'false') {
709                                         op_s[i] = false;
710                                 }
711
712                                 if (op_s[i] == 'true') {
713                                         op_s[i] = true;
714                                 }
715                                 
716                         }
717
718                         debug("history split: " + op_s);
719
720                         if (op_s[0] == "ARTICLE") {
721                                 debug("history: reverting to article " + op_s[1] + "/" + op_s[2]);
722                                 view(op_s[1], op_s[2], true);
723                         }
724
725                         if (op_s[0] == "FEED") {
726                                 viewfeed(op_s[1], op_s[2], op_s[3], op_s[4], true);
727                         }
728
729                 } else {
730                         notify_error("No operation to undo");
731                 }
732
733                 return false;
734
735         }       
736
737         debug("LKP=" + keycode);
738 }
739
740 function history_push(op) {
741         debug("history_push: " + op);
742         op_history.push(op);
743
744         while (op_history.length > 30) {
745                 op_history.shift();
746         }
747 }
748
749 function history_pop() {
750         var op = op_history.pop();
751         debug("history_pop: " + op);
752         return op;
753 }
754
755 function history_clear() {
756         debug("history_clear");
757         op_history.clear();
758 }