]> git.wh0rd.org Git - tt-rss.git/blob - tt-rss.js
move offline stuff to offline.js
[tt-rss.git] / tt-rss.js
1 var SCHEMA_VERSION = 1;
2
3 var total_unread = 0;
4 var first_run = true;
5 var display_tags = false;
6 var global_unread = -1;
7 var active_title_text = "";
8 var current_subtitle = "";
9 var daemon_enabled = false;
10 var daemon_refresh_only = false;
11 //var _qfd_deleted_feed = 0;
12 var firsttime_update = true;
13 var _active_feed_id = 0;
14 var _active_feed_is_cat = false;
15 var number_of_feeds = 0;
16 var sanity_check_done = false;
17 var _hfd_scrolltop = 0;
18 var hotkey_prefix = false;
19 var init_params = new Object();
20 var ver_offset = 0;
21 var hor_offset = 0;
22 var feeds_sort_by_unread = false;
23 var feedlist_sortable_enabled = false;
24 var offline_mode = false;
25 var store = false;
26 var localServer = false;
27 var db = false;
28 var download_progress_last = 0;
29
30 function activeFeedIsCat() {
31         return _active_feed_is_cat;
32 }
33
34 function getActiveFeedId() {
35 //      return getCookie("ttrss_vf_actfeed");
36         try {
37                 debug("gAFID: " + _active_feed_id);
38                 return _active_feed_id;
39         } catch (e) {
40                 exception_error("getActiveFeedId", e);
41         }
42 }
43
44 function setActiveFeedId(id, is_cat) {
45 //      return setCookie("ttrss_vf_actfeed", id);
46         try {
47                 debug("sAFID(" + id + ", " + is_cat + ")");
48                 _active_feed_id = id;
49
50                 if (is_cat != undefined) {
51                         _active_feed_is_cat = is_cat;
52                 }
53
54         } catch (e) {
55                 exception_error("setActiveFeedId", e);
56         }
57 }
58
59
60 function isFeedlistSortable() {
61         return feedlist_sortable_enabled;
62 }
63
64 function tagsAreDisplayed() {
65         return display_tags;
66 }
67
68 function toggleTags(show_all) {
69
70         try {
71
72         debug("toggleTags: " + show_all + "; " + display_tags);
73
74         var p = document.getElementById("dispSwitchPrompt");
75
76         if (!show_all && !display_tags) {
77                 displayDlg("printTagCloud");
78         } else if (show_all) {
79                 closeInfoBox();
80                 display_tags = true;
81                 p.innerHTML = __("display feeds");
82                 notify_progress("Loading, please wait...", true);
83                 updateFeedList();
84         } else if (display_tags) {
85                 display_tags = false;
86                 p.innerHTML = __("tag cloud");
87                 notify_progress("Loading, please wait...", true);
88                 updateFeedList();
89         }
90
91         } catch (e) {
92                 exception_error("toggleTags", e);
93         }
94 }
95
96 function dlg_frefresh_callback(transport, deleted_feed) {
97         if (getActiveFeedId() == deleted_feed) {
98                 var h = document.getElementById("headlines-frame");
99                 if (h) {
100                         h.innerHTML = "<div class='whiteBox'>" + __('No feed selected.') + "</div>";
101                 }
102         }
103
104         setTimeout('updateFeedList(false, false)', 50);
105         closeInfoBox();
106 }
107
108 function refetch_callback2(transport) {
109         try {
110
111                 var date = new Date();
112
113                 parse_counters_reply(transport, true);
114
115                 debug("refetch_callback2: done");
116
117 /*              if (!daemon_enabled && !daemon_refresh_only) {
118                         notify_info("All feeds updated.");
119                         updateTitle("");
120                 } else {
121                         //notify("");
122                 } */
123         } catch (e) {
124                 exception_error("refetch_callback", e);
125                 updateTitle("");
126         }
127 }
128
129 function backend_sanity_check_callback(transport) {
130
131         try {
132
133                 if (sanity_check_done) {
134                         fatalError(11, "Sanity check request received twice. This can indicate "+
135                       "presence of Firebug or some other disrupting extension. "+
136                                 "Please disable it and try again.");
137                         return;
138                 }
139
140                 if (!transport.responseXML) {
141                         if (!window.google && !google.gears) {
142                                 fatalError(3, "Sanity check: Received reply is not XML", transport.responseText);
143                         } else {
144                                 init_offline();
145                         }
146                         return;
147                 }
148
149                 if (getURLParam("offline")) {
150                         return init_offline();
151                 }
152
153                 var reply = transport.responseXML.firstChild.firstChild;
154
155                 if (!reply) {
156                         fatalError(3, "Sanity check: invalid RPC reply", transport.responseText);
157                         return;
158                 }
159
160                 var error_code = reply.getAttribute("error-code");
161         
162                 if (error_code && error_code != 0) {
163                         return fatalError(error_code, reply.getAttribute("error-msg"));
164                 }
165
166                 debug("sanity check ok");
167
168                 var params = reply.nextSibling;
169
170                 if (params) {
171                         debug('reading init-params...');
172                         var param = params.firstChild;
173
174                         while (param) {
175                                 var k = param.getAttribute("key");
176                                 var v = param.getAttribute("value");
177                                 debug(k + " => " + v);
178                                 init_params[k] = v;                                     
179                                 param = param.nextSibling;
180                         }
181                 }
182
183                 sanity_check_done = true;
184
185                 init_second_stage();
186
187         } catch (e) {
188                 exception_error("backend_sanity_check_callback", e, transport); 
189         } 
190 }
191
192 function scheduleFeedUpdate(force) {
193
194         debug("in scheduleFeedUpdate");
195
196 /*      if (!daemon_enabled && !daemon_refresh_only) {
197                 notify_progress("Updating feeds...", true);
198         } */
199
200         var query_str = "backend.php?op=rpc&subop=";
201
202         if (force) {
203                 query_str = query_str + "forceUpdateAllFeeds";
204         } else {
205                 query_str = query_str + "updateAllFeeds";
206         }
207
208         var omode;
209
210         if (firsttime_update && !navigator.userAgent.match("Opera")) {
211                 firsttime_update = false;
212                 omode = "T";
213         } else {
214                 if (display_tags) {
215                         omode = "tl";
216                 } else {
217                         omode = "flc";
218                 }
219         }
220         
221         query_str = query_str + "&omode=" + omode;
222         query_str = query_str + "&uctr=" + global_unread;
223
224         var date = new Date();
225         var timestamp = Math.round(date.getTime() / 1000);
226         query_str = query_str + "&ts=" + timestamp
227
228         debug("REFETCH query: " + query_str);
229
230         new Ajax.Request(query_str, {
231                 onComplete: function(transport) { 
232                                 refetch_callback2(transport); 
233                         } });
234 }
235
236 function updateFeedList(silent, fetch) {
237
238 //      if (silent != true) {
239 //              notify("Loading feed list...");
240 //      }
241
242         debug("<b>updateFeedList</b>");
243
244         if (offline_mode) return render_offline_feedlist();
245
246         var query_str = "backend.php?op=feeds";
247
248         if (display_tags) {
249                 query_str = query_str + "&tags=1";
250         }
251
252         if (getActiveFeedId() && !activeFeedIsCat()) {
253                 query_str = query_str + "&actid=" + getActiveFeedId();
254         }
255
256         var date = new Date();
257         var timestamp = Math.round(date.getTime() / 1000);
258         query_str = query_str + "&ts=" + timestamp
259         
260         if (fetch) query_str = query_str + "&fetch=yes";
261
262 //      var feeds_frame = document.getElementById("feeds-frame");
263 //      feeds_frame.src = query_str;
264
265         debug("updateFeedList Q=" + query_str);
266
267         new Ajax.Request(query_str, {
268                 onComplete: function(transport) { 
269                         feedlist_callback2(transport); 
270                 } });
271
272 }
273
274 function catchupAllFeeds() {
275
276         var str = __("Mark all articles as read?");
277
278         if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
279
280                 var query_str = "backend.php?op=feeds&subop=catchupAll";
281
282                 notify_progress("Marking all feeds as read...");
283
284                 debug("catchupAllFeeds Q=" + query_str);
285
286                 new Ajax.Request(query_str, {
287                         onComplete: function(transport) { 
288                                 feedlist_callback2(transport); 
289                         } });
290
291                 global_unread = 0;
292                 updateTitle("");
293         }
294 }
295
296 function viewCurrentFeed(subop) {
297
298 //      if (getActiveFeedId()) {
299         if (getActiveFeedId() != undefined) {
300                 viewfeed(getActiveFeedId(), subop, activeFeedIsCat());
301         } else {
302                 disableContainerChildren("headlinesToolbar", false, document);
303 //              viewfeed(-1, subop); // FIXME
304         }
305         return false; // block unneeded form submits
306 }
307
308 function viewfeed(feed, subop) {
309         var f = window.frames["feeds-frame"];
310         f.viewfeed(feed, subop);
311 }
312
313 function timeout() {
314         if (getInitParam("bw_limit") == "1") return;
315
316         scheduleFeedUpdate(false);
317
318         var refresh_time = getInitParam("feeds_frame_refresh");
319
320         if (!refresh_time) refresh_time = 600; 
321
322         setTimeout("timeout()", refresh_time*1000);
323 }
324
325 function resetSearch() {
326         var searchbox = document.getElementById("searchbox")
327
328         if (searchbox.value != "" && getActiveFeedId()) {       
329                 searchbox.value = "";
330                 viewfeed(getActiveFeedId(), "");
331         }
332 }
333
334 function searchCancel() {
335         closeInfoBox(true);
336 }
337
338 function search() {
339         closeInfoBox(); 
340         viewCurrentFeed(0, "");
341 }
342
343 // if argument is undefined, current subtitle is not updated
344 // use blank string to clear subtitle
345 function updateTitle(s) {
346         var tmp = "Tiny Tiny RSS";
347
348         if (s != undefined) {
349                 current_subtitle = s;
350         }
351
352         if (global_unread > 0) {
353                 tmp = tmp + " (" + global_unread + ")";
354         }
355
356         if (current_subtitle) {
357                 tmp = tmp + " - " + current_subtitle;
358         }
359
360         if (active_title_text.length > 0) {
361                 tmp = tmp + " > " + active_title_text;
362         }
363
364         document.title = tmp;
365 }
366
367 function genericSanityCheck() {
368
369 //      if (!Ajax.getTransport()) fatalError(1);
370
371         setCookie("ttrss_vf_test", "TEST");
372         
373         if (getCookie("ttrss_vf_test") != "TEST") {
374                 fatalError(2);
375         }
376
377         return true;
378 }
379
380 function init() {
381
382         try {
383
384                 // this whole shebang is based on http://www.birnamdesigns.com/misc/busted2.html
385
386                 if (arguments.callee.done) return;
387                 arguments.callee.done = true;           
388
389                 init_gears();
390
391                 disableContainerChildren("headlinesToolbar", true);
392
393                 Form.disable("main_toolbar_form");
394
395                 if (!genericSanityCheck()) 
396                         return;
397
398                 if (getURLParam('debug')) {
399                         Element.show("debug_output");
400                         debug('debug mode activated');
401                 }
402
403                 var params = "&ua=" + param_escape(navigator.userAgent);
404
405                 loading_set_progress(30);
406
407                 new Ajax.Request("backend.php?op=rpc&subop=sanityCheck" + params,       {
408                         onComplete: function(transport) {
409                                         backend_sanity_check_callback(transport);
410                                 } });
411
412         } catch (e) {
413                 exception_error("init", e);
414         }
415 }
416
417 function resize_headlines(delta_x, delta_y) {
418
419         try {
420
421                 debug("resize_headlines: " + delta_x + ":" + delta_y);
422         
423                 var h_frame = document.getElementById("headlines-frame");
424                 var c_frame = document.getElementById("content-frame");
425                 var f_frame = document.getElementById("footer");
426                 var feeds_frame = document.getElementById("feeds-holder");
427                 var resize_grab = document.getElementById("resize-grabber");
428                 var resize_handle = document.getElementById("resize-handle");
429
430                 if (!c_frame || !h_frame) return;
431         
432                 if (feeds_frame && getInitParam("theme") == "compat") {
433                                 feeds_frame.style.bottom = f_frame.offsetHeight + "px";         
434                 }
435         
436                 if (getInitParam("theme") == "3pane") {
437         
438                         if (delta_x != undefined) {
439                                 if (c_frame.offsetLeft - delta_x > feeds_frame.offsetWidth + feeds_frame.offsetLeft + 100 && c_frame.offsetWidth + delta_x > 100) {
440                                         hor_offset = hor_offset + delta_x;
441                                 }
442                         }
443         
444                         debug("resize_headlines: HOR-mode: " + hor_offset);
445         
446                         c_frame.style.width = (400 + hor_offset) + "px";
447                         h_frame.style.right = c_frame.offsetWidth - 1 + "px";
448         
449                         resize_grab.style.top = (h_frame.offsetTop + h_frame.offsetHeight - 60) + "px";
450                         resize_grab.style.left = (h_frame.offsetLeft + h_frame.offsetWidth - 
451                                 4) + "px";
452                         resize_grab.style.display = "block";
453
454                         resize_handle.src = "themes/3pane/images/resize_handle_vert.png";
455                         resize_handle.style.paddingTop = (resize_grab.offsetHeight / 2 - 7) + "px";
456         
457                 } else {
458         
459                         if (delta_y != undefined) {
460                                 if (c_frame.offsetHeight + delta_y > 100 && h_frame.offsetHeight - delta_y > 100) {
461                                         ver_offset = ver_offset + delta_y;
462                                 }
463                         }
464         
465                         debug("resize_headlines: VER-mode: " + ver_offset);
466         
467                         h_frame.style.height = (300 - ver_offset) + "px";
468         
469                         c_frame.style.top = (h_frame.offsetTop + h_frame.offsetHeight + 0) + "px";
470                         h_frame.style.height = h_frame.offsetHeight + "px";
471         
472                         var theme_c = 0;
473         
474                         if (getInitParam("theme") == "graycube") {
475                                 theme_c = 1;
476                         }
477
478                         if (getInitParam("theme") == "graycube" || getInitParam("theme") == "compat") {
479                                 resize_handle.src = "themes/graycube/images/resize_handle_horiz.png";
480                         }
481         
482 /*                      resize_grab.style.top = (h_frame.offsetTop + h_frame.offsetHeight - 
483                                 4 - theme_c) + "px";
484                         resize_grab.style.display = "block"; */
485         
486                 }
487         
488                 if (getInitParam("cookie_lifetime") != 0) {
489                         setCookie("ttrss_offset_ver", ver_offset, 
490                                 getInitParam("cookie_lifetime"));
491                         setCookie("ttrss_offset_hor", hor_offset, 
492                                 getInitParam("cookie_lifetime"));
493                 } else {
494                         setCookie("ttrss_offset_ver", ver_offset);
495                         setCookie("ttrss_offset_hor", hor_offset);
496                 }
497
498         } catch (e) {
499                 exception_error("resize_headlines", e);
500         }
501
502 }
503
504 function init_second_stage() {
505
506         try {
507
508                 delCookie("ttrss_vf_test");
509
510 //              document.onresize = resize_headlines;
511
512                 var toolbar = document.forms["main_toolbar_form"];
513
514                 dropboxSelect(toolbar.view_mode, getInitParam("default_view_mode"));
515                 dropboxSelect(toolbar.limit, getInitParam("default_view_limit"));
516                 dropboxSelect(toolbar.order_by, getInitParam("default_view_order_by"));
517
518                 daemon_enabled = getInitParam("daemon_enabled") == 1;
519                 daemon_refresh_only = getInitParam("daemon_refresh_only") == 1;
520                 feeds_sort_by_unread = getInitParam("feeds_sort_by_unread") == 1;
521
522                 var fl = cache_find_param("FEEDLIST", getInitParam("num_feeds"));
523
524                 if (fl) {
525                         render_feedlist(fl);
526                         if (document.getElementById("feedList")) {
527                                 request_counters();
528                         } else {
529                                 setTimeout('updateFeedList(false, false)', 50);
530                         }
531                 } else {
532                         setTimeout('updateFeedList(false, false)', 50);
533                 }
534
535                 debug("second stage ok");
536
537                 loading_set_progress(60);
538
539                 ver_offset = parseInt(getCookie("ttrss_offset_ver"));
540                 hor_offset = parseInt(getCookie("ttrss_offset_hor"));
541
542                 debug("got offsets from cookies: ver " + ver_offset + " hor " + hor_offset);
543
544                 /* fuck IE */
545
546                 if (isNaN(hor_offset)) hor_offset = 0;
547                 if (isNaN(ver_offset)) ver_offset = 0;
548
549                 debug("offsets from cookies [x:y]: " + hor_offset + ":" + ver_offset);
550
551                 resize_headlines();
552
553         } catch (e) {
554                 exception_error("init_second_stage", e);
555         }
556 }
557
558 function quickMenuChange() {
559         var chooser = document.getElementById("quickMenuChooser");
560         var opid = chooser[chooser.selectedIndex].value;
561
562         chooser.selectedIndex = 0;
563         quickMenuGo(opid);
564 }
565
566 function quickMenuGo(opid) {
567         try {
568
569                 if (opid == "qmcPrefs") {
570                         gotoPreferences();
571                 }
572         
573                 if (opid == "qmcSearch") {
574                         displayDlg("search", getActiveFeedId() + ":" + activeFeedIsCat());
575                         return;
576                 }
577         
578                 if (opid == "qmcAddFeed") {
579                         displayDlg("quickAddFeed");
580                         return;
581                 }
582
583                 if (opid == "qmcEditFeed") {
584                         editFeedDlg(getActiveFeedId());
585                 }
586         
587                 if (opid == "qmcRemoveFeed") {
588                         var actid = getActiveFeedId();
589
590                         if (activeFeedIsCat()) {
591                                 alert(__("You can't unsubscribe from the category."));
592                                 return;
593                         }       
594
595                         if (!actid) {
596                                 alert(__("Please select some feed first."));
597                                 return;
598                         }
599
600                         var fn = getFeedName(actid);
601
602                         var pr = __("Unsubscribe from %s?").replace("%s", fn);
603
604                         if (confirm(pr)) {
605                                 unsubscribeFeed(actid);
606                         }
607                 
608                         return;
609                 }
610
611                 if (opid == "qmcClearFeed") {
612                         var actid = getActiveFeedId();
613
614                         if (!actid) {
615                                 alert(__("Please select some feed first."));
616                                 return;
617                         }
618
619                         if (activeFeedIsCat() || actid < 0) {
620                                 alert(__("You can't clear this type of feed."));
621                                 return;
622                         }       
623
624                         var fn = getFeedName(actid);
625
626                         var pr = __("Erase all non-starred articles in %s?").replace("%s", fn);
627
628                         if (confirm(pr)) {
629                                 clearFeedArticles(actid);
630                         }
631                 
632                         return;
633                 }
634         
635
636                 if (opid == "qmcUpdateFeeds") {
637                         scheduleFeedUpdate(true);
638                         return;
639                 }
640         
641                 if (opid == "qmcCatchupAll") {
642                         catchupAllFeeds();
643                         return;
644                 }
645         
646                 if (opid == "qmcShowOnlyUnread") {
647                         toggleDispRead();
648                         return;
649                 }
650         
651                 if (opid == "qmcAddFilter") {
652                         displayDlg("quickAddFilter", getActiveFeedId());
653                 }
654
655                 if (opid == "qmcAddLabel") {
656                         addLabel();
657                 }
658
659                 if (opid == "qmcRescoreFeed") {
660                         rescoreCurrentFeed();
661                 }
662
663                 if (opid == "qmcHKhelp") {
664                         //Element.show("hotkey_help_overlay");
665                         Effect.Appear("hotkey_help_overlay", {duration : 0.3});
666                 }
667
668                 if (opid == "qmcResetUI") {
669                         hor_offset = 0;
670                         ver_offset = 0;
671                         resize_headlines();
672                 }
673
674 /*              if (opid == "qmcDownload") {
675                         displayDlg("offlineDownload");
676                         return;
677                 } */
678
679                 if (opid == "qmcResetCats") {
680
681                         if (confirm(__("Reset category order?"))) {
682
683                                 var query = "backend.php?op=feeds&subop=catsortreset";
684
685                                 notify_progress("Loading, please wait...", true);
686
687                                 new Ajax.Request(query, {
688                                         onComplete: function(transport) { 
689                                                 window.setTimeout('updateFeedList(false, false)', 50);
690                                         } });
691                         }
692                 }
693
694         } catch (e) {
695                 exception_error("quickMenuGo", e);
696         }
697 }
698
699 function unsubscribeFeed(feed_id, title) {
700
701
702         var msg = __("Unsubscribe from %s?").replace("%s", title);
703
704         if (title == undefined || confirm(msg)) {
705                 notify_progress("Removing feed...");
706
707                 var query = "backend.php?op=pref-feeds&quiet=1&subop=remove&ids=" + feed_id;
708
709                 new Ajax.Request(query, {
710                         onComplete: function(transport) {
711                                         dlg_frefresh_callback(transport, feed_id);
712                                 } });
713         }
714
715         return false;
716 }
717
718
719 function updateFeedTitle(t) {
720         active_title_text = t;
721         updateTitle();
722 }
723
724 function toggleDispRead() {
725         try {
726
727                 var hide_read_feeds = (getInitParam("hide_read_feeds") == "1");
728
729                 hide_read_feeds = !hide_read_feeds;
730
731                 debug("toggle_disp_read => " + hide_read_feeds);
732
733                 hideOrShowFeeds(hide_read_feeds);
734
735                 storeInitParam("hide_read_feeds", hide_read_feeds, true);
736                                 
737         } catch (e) {
738                 exception_error("toggleDispRead", e);
739         }
740 }
741
742 function parse_runtime_info(elem) {
743         if (!elem) {
744                 debug("parse_runtime_info: elem is null, aborting");
745                 return;
746         }
747
748         var param = elem.firstChild;
749
750         debug("parse_runtime_info: " + param);
751
752         while (param) {
753                 var k = param.getAttribute("key");
754                 var v = param.getAttribute("value");
755
756                 debug("RI: " + k + " => " + v);
757
758                 if (k == "num_feeds") {
759                         init_params[k] = v;                                     
760                 }
761
762                 if (k == "new_version_available") {
763                         var icon = document.getElementById("newVersionIcon");
764                         if (icon) {
765                                 if (v == "1") {
766                                         icon.style.display = "inline";
767                                 } else {
768                                         icon.style.display = "none";
769                                 }
770                         }
771                 }
772
773                 var error_flag;
774
775                 if (k == "daemon_is_running" && v != 1) {
776                         notify_error("<span onclick=\"javascript:explainError(1)\">Update daemon is not running.</span>", true);
777                         error_flag = true;
778                 }
779
780                 if (k == "daemon_stamp_ok" && v != 1) {
781                         notify_error("<span onclick=\"javascript:explainError(3)\">Update daemon is not updating feeds.</span>", true);
782                         error_flag = true;
783                 }
784
785                 if (!error_flag) {
786                         notify('');
787                 }
788
789 /*              var w = document.getElementById("noDaemonWarning");
790                 
791                 if (w) {
792                         if (k == "daemon_is_running" && v != 1) {
793                                 w.style.display = "block";
794                         } else {
795                                 w.style.display = "none";
796                         }
797                 } */
798                 param = param.nextSibling;
799         }
800 }
801
802 function catchupCurrentFeed() {
803
804         var fn = getFeedName(getActiveFeedId(), activeFeedIsCat());
805         
806         var str = __("Mark all articles in %s as read?").replace("%s", fn);
807
808         if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
809                 return viewCurrentFeed('MarkAllRead')
810         }
811 }
812
813 function catchupFeedInGroup(id) {
814
815         try {
816
817                 var title = getFeedName(id);
818
819                 var str = __("Mark all articles in %s as read?").replace("%s", title);
820
821                 if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
822                         return viewCurrentFeed('MarkAllReadGR:' + id)
823                 }
824
825         } catch (e) {
826                 exception_error("catchupFeedInGroup", e);
827         }
828 }
829
830 function editFeedDlg(feed) {
831         try {
832
833                 if (!feed) {
834                         alert(__("Please select some feed first."));
835                         return;
836                 }
837         
838                 if ((feed <= 0) || activeFeedIsCat() || tagsAreDisplayed()) {
839                         alert(__("You can't edit this kind of feed."));
840                         return;
841                 }
842         
843                 var query = "";
844         
845                 if (feed > 0) {
846                         query = "backend.php?op=pref-feeds&subop=editfeed&id=" +        param_escape(feed);
847                 } else {
848                         query = "backend.php?op=pref-labels&subop=edit&id=" +   param_escape(-feed-11);
849                 }
850
851                 disableHotkeys();
852
853                 new Ajax.Request(query, {
854                         onComplete: function(transport) { 
855                                 infobox_callback2(transport); 
856                         } });
857
858         } catch (e) {
859                 exception_error("editFeedDlg", e);
860         }
861 }
862
863 /* this functions duplicate those of prefs.js feed editor, with
864         some differences because there is no feedlist */
865
866 function feedEditCancel() {
867         closeInfoBox();
868         return false;
869 }
870
871 function feedEditSave() {
872
873         try {
874         
875                 // FIXME: add parameter validation
876
877                 var query = Form.serialize("edit_feed_form");
878
879                 notify_progress("Saving feed...");
880
881                 new Ajax.Request("backend.php", {
882                         parameters: query,
883                         onComplete: function(transport) { 
884                                 dlg_frefresh_callback(transport); 
885                         } });
886
887
888                 closeInfoBox();
889
890                 return false;
891
892         } catch (e) {
893                 exception_error("feedEditSave (main)", e);
894         } 
895 }
896
897 function clearFeedArticles(feed_id) {
898
899         notify_progress("Clearing feed...");
900
901         var query = "backend.php?op=pref-feeds&quiet=1&subop=clear&id=" + feed_id;
902
903         new Ajax.Request(query, {
904                 onComplete: function(transport) {
905                                 dlg_frefresh_callback(transport, feed_id);
906                         } });
907
908         return false;
909 }
910
911 function collapse_feedlist() {
912         try {
913                 debug("toggle_feedlist");
914                 
915                 var theme = getInitParam("theme");
916                 if (theme != "" && theme != "compact" && theme != "graycube" &&
917                                 theme != "compat") return;
918
919                 var fl = document.getElementById("feeds-holder");
920                 var fh = document.getElementById("headlines-frame");
921                 var fc = document.getElementById("content-frame");
922                 var ft = document.getElementById("toolbar");
923                 var ff = document.getElementById("footer");
924                 var fhdr = document.getElementById("header");
925                 var fbtn = document.getElementById("collapse_feeds_btn");
926
927                 if (!Element.visible(fl)) {
928                         Element.show(fl);
929                         fbtn.value = "<<";
930
931                         if (theme != "graycube") {
932
933                                 fh.style.left = fl.offsetWidth + "px";
934                                 ft.style.left = fl.offsetWidth + "px";
935                                 if (fc) fc.style.left = fl.offsetWidth + "px";
936                                 if (ff && theme != "compat") ff.style.left = (fl.offsetWidth-1) + "px";
937
938                                 if (theme == "compact") fhdr.style.left = (fl.offsetWidth + 10) + "px";
939                         } else {
940                                 fh.style.left = fl.offsetWidth + 40 + "px";
941                                 ft.style.left = fl.offsetWidth + 40 +"px";
942                                 if (fc) fc.style.left = fl.offsetWidth + 40 + "px";
943                         }
944
945                         setCookie("ttrss_vf_fclps", "0");
946
947                 } else {
948                         Element.hide(fl);
949                         fbtn.value = ">>";
950
951                         if (theme != "graycube") {
952
953                                 fh.style.left = "0px";
954                                 ft.style.left = "0px";
955                                 if (fc) fc.style.left = "0px";
956                                 if (ff) ff.style.left = "0px";
957
958                                 if (theme == "compact") fhdr.style.left = "10px";
959
960                         } else {
961                                 fh.style.left = "20px";
962                                 ft.style.left = "20px";
963                                 if (fc) fc.style.left = "20px";
964
965                         }
966
967                         setCookie("ttrss_vf_fclps", "1");
968                 }
969         } catch (e) {
970                 exception_error("toggle_feedlist", e);
971         }
972 }
973
974 function viewModeChanged() {
975         cache_empty();
976         return viewCurrentFeed(0, '')
977 }
978
979 function viewLimitChanged() {
980         cache_empty();
981         return viewCurrentFeed(0, '')
982 }
983
984 /* function adjustArticleScore(id, score) {
985         try {
986
987                 var pr = prompt(__("Assign score to article:"), score);
988
989                 if (pr != undefined) {
990                         var query = "backend.php?op=rpc&subop=setScore&id=" + id + "&score=" + pr;
991
992                         new Ajax.Request(query, {
993                         onComplete: function(transport) {
994                                         viewCurrentFeed();
995                                 } });
996
997                 }
998         } catch (e) {
999                 exception_error("adjustArticleScore", e);
1000         }
1001 } */
1002
1003 function rescoreCurrentFeed() {
1004
1005         var actid = getActiveFeedId();
1006
1007         if (activeFeedIsCat() || actid < 0 || tagsAreDisplayed()) {
1008                 alert(__("You can't rescore this kind of feed."));
1009                 return;
1010         }       
1011
1012         if (!actid) {
1013                 alert(__("Please select some feed first."));
1014                 return;
1015         }
1016
1017         var fn = getFeedName(actid);
1018         var pr = __("Rescore articles in %s?").replace("%s", fn);
1019
1020         if (confirm(pr)) {
1021                 notify_progress("Rescoring articles...");
1022
1023                 var query = "backend.php?op=pref-feeds&subop=rescore&quiet=1&ids=" + actid;
1024
1025                 new Ajax.Request(query, {
1026                 onComplete: function(transport) {
1027                         viewCurrentFeed();
1028                 } });
1029         }
1030 }
1031
1032 function hotkey_handler(e) {
1033
1034         try {
1035
1036                 var keycode;
1037                 var shift_key = false;
1038
1039                 var feedlist = document.getElementById('feedList');
1040
1041                 try {
1042                         shift_key = e.shiftKey;
1043                 } catch (e) {
1044
1045                 }
1046         
1047                 if (window.event) {
1048                         keycode = window.event.keyCode;
1049                 } else if (e) {
1050                         keycode = e.which;
1051                 }
1052
1053                 var keychar = String.fromCharCode(keycode);
1054
1055                 if (keycode == 27) { // escape
1056                         if (Element.visible("hotkey_help_overlay")) {
1057                                 Element.hide("hotkey_help_overlay");
1058                         }
1059                         hotkey_prefix = false;
1060                         closeInfoBox();
1061                 } 
1062
1063                 if (!hotkeys_enabled) {
1064                         debug("hotkeys disabled");
1065                         return;
1066                 }
1067
1068                 if (keycode == 16) return; // ignore lone shift
1069
1070                 if ((keycode == 70 || keycode == 67 || keycode == 71) 
1071                                 && !hotkey_prefix) {
1072
1073                         hotkey_prefix = keycode;
1074                         debug("KP: PREFIX=" + keycode + " CHAR=" + keychar);
1075                         return true;
1076                 }
1077
1078                 if (Element.visible("hotkey_help_overlay")) {
1079                         Element.hide("hotkey_help_overlay");
1080                 }
1081
1082                 /* Global hotkeys */
1083
1084                 if (!hotkey_prefix) {
1085
1086                         if (keycode == 68 && shift_key) { // d
1087                                 if (!Element.visible("debug_output")) {
1088                                         Element.show("debug_output");
1089                                         debug('debug mode activated');
1090                                 } else {
1091                                         Element.hide("debug_output");
1092                                 }
1093         
1094                                 return;
1095                         }
1096         
1097                         if ((keycode == 191 || keychar == '?') && shift_key) { // ?
1098                                 if (!Element.visible("hotkey_help_overlay")) {
1099                                         //Element.show("hotkey_help_overlay");
1100                                         Effect.Appear("hotkey_help_overlay", {duration : 0.3});
1101                                 } else {
1102                                         Element.hide("hotkey_help_overlay");
1103                                 }
1104                                 return false;
1105                         }
1106
1107                         if (keycode == 191 || keychar == '/') { // /
1108                                 displayDlg("search", getActiveFeedId() + ":" + activeFeedIsCat());
1109                                 return false;
1110                         }
1111
1112                         if (keycode == 82 && shift_key) { // R
1113                                 scheduleFeedUpdate(true);
1114                                 return;
1115                         }
1116
1117                         if (keycode == 74) { // j
1118                                 var feed = getActiveFeedId();
1119                                 var new_feed = getRelativeFeedId2(feed, activeFeedIsCat(), 'prev');
1120 //                              alert(feed + " IC: " + activeFeedIsCat() + " => " + new_feed);
1121                                 if (new_feed) {
1122                                         var is_cat = new_feed.match("CAT:");
1123                                         if (is_cat) {
1124                                                 new_feed = new_feed.replace("CAT:", "");
1125                                                 viewCategory(new_feed);
1126                                         } else {
1127                                                 viewfeed(new_feed, '', false);
1128                                         }
1129                                 }
1130                                 return;
1131                         }
1132         
1133                         if (keycode == 75) { // k
1134                                 var feed = getActiveFeedId();
1135                                 var new_feed = getRelativeFeedId2(feed, activeFeedIsCat(), 'next');
1136 //                              alert(feed + " IC: " + activeFeedIsCat() + " => " + new_feed);
1137                                 if (new_feed) {
1138                                         var is_cat = new_feed.match("CAT:");
1139                                         if (is_cat == "CAT:") {
1140                                                 new_feed = new_feed.replace("CAT:", "");
1141                                                 viewCategory(new_feed);
1142                                         } else {
1143                                                 viewfeed(new_feed, '', false);
1144                                         }
1145                                 }
1146                                 return;
1147                         }
1148
1149                         if (shift_key && keycode == 40) { // shift-down
1150                                 catchupRelativeToArticle(1);
1151                                 return;
1152                         }
1153
1154                         if (shift_key && keycode == 38) { // shift-up
1155                                 catchupRelativeToArticle(0);
1156                                 return;
1157                         }
1158
1159                         if (shift_key && keycode == 78) { // N
1160                                 scrollArticle(50);      
1161                                 return;
1162                         }
1163
1164                         if (shift_key && keycode == 80) { // P
1165                                 scrollArticle(-50);     
1166                                 return;
1167                         }
1168
1169
1170                         if (keycode == 78 || keycode == 40) { // n, down
1171                                 if (typeof moveToPost != 'undefined') {
1172                                         moveToPost('next');
1173                                         return;
1174                                 }
1175                         }
1176         
1177                         if (keycode == 80 || keycode == 38) { // p, up
1178                                 if (typeof moveToPost != 'undefined') {
1179                                         moveToPost('prev');
1180                                         return;
1181                                 }
1182                         }
1183
1184                         if (keycode == 83 && shift_key) { // S
1185                                 var id = getActiveArticleId();
1186                                 if (id) {                               
1187                                         togglePub(id);
1188                                 }
1189                                 return;
1190                         }
1191
1192                         if (keycode == 83) { // s
1193                                 var id = getActiveArticleId();
1194                                 if (id) {                               
1195                                         toggleMark(id);
1196                                 }
1197                                 return;
1198                         }
1199
1200
1201                         if (keycode == 85) { // u
1202                                 var id = getActiveArticleId();
1203                                 if (id) {                               
1204                                         toggleUnread(id);
1205                                 }
1206                                 return;
1207                         }
1208
1209                         if (keycode == 84 && shift_key) { // T
1210                                 var id = getActiveArticleId();
1211                                 if (id) {
1212                                         editArticleTags(id, getActiveFeedId(), isCdmMode());
1213                                         return;
1214                                 }
1215                         }
1216
1217                         if (keycode == 9) { // tab
1218                                 var id = getArticleUnderPointer();
1219                                 if (id) {                               
1220                                         var cb = document.getElementById("RCHK-" + id);
1221
1222                                         if (cb) {
1223                                                 cb.checked = !cb.checked;
1224                                                 toggleSelectRowById(cb, "RROW-" + id);
1225                                                 return false;
1226                                         }
1227                                 }
1228                         }
1229
1230                         if (keycode == 79) { // o
1231                                 if (getActiveArticleId()) {
1232                                         openArticleInNewWindow(getActiveArticleId());
1233                                         return;
1234                                 }
1235                         }
1236
1237                         if (keycode == 81 && shift_key) { // Q
1238                                 if (typeof catchupAllFeeds != 'undefined') {
1239                                         catchupAllFeeds();
1240                                         return;
1241                                 }
1242                         }
1243
1244                         if (keycode == 88) { // x
1245                                 if (activeFeedIsCat()) {
1246                                         toggleCollapseCat(getActiveFeedId());
1247                                 }
1248                         }
1249                 }
1250
1251                 /* Prefix f */
1252
1253                 if (hotkey_prefix == 70) { // f 
1254
1255                         hotkey_prefix = false;
1256
1257                         if (keycode == 81) { // q
1258                                 if (getActiveFeedId()) {
1259                                         catchupCurrentFeed();
1260                                         return;
1261                                 }
1262                         }
1263
1264                         if (keycode == 82) { // r
1265                                 if (getActiveFeedId()) {
1266                                         viewfeed(getActiveFeedId(), "ForceUpdate", activeFeedIsCat());
1267                                         return;
1268                                 }
1269                         }
1270
1271                         if (keycode == 65) { // a
1272                                 toggleDispRead();
1273                                 return false;
1274                         }
1275
1276                         if (keycode == 85 && shift_key) { // U
1277                                 scheduleFeedUpdate(true);
1278                                 return false;
1279                         }
1280
1281                         if (keycode == 85) { // u
1282                                 if (getActiveFeedId()) {
1283                                         viewfeed(getActiveFeedId(), "ForceUpdate");
1284                                         return false;
1285                                 }
1286                         }
1287
1288                         if (keycode == 69) { // e
1289                                 editFeedDlg(getActiveFeedId());
1290                                 return false;
1291                         }
1292
1293                         if (keycode == 83) { // s
1294                                 displayDlg("quickAddFeed");
1295                                 return false;
1296                         }
1297
1298                         if (keycode == 67 && shift_key) { // C
1299                                 if (typeof catchupAllFeeds != 'undefined') {
1300                                         catchupAllFeeds();
1301                                         return false;
1302                                 }
1303                         }
1304
1305                         if (keycode == 67) { // c
1306                                 if (getActiveFeedId()) {
1307                                         catchupCurrentFeed();
1308                                         return false;
1309                                 }
1310                         }
1311
1312 /*                      if (keycode == 68 && shift_key) { // D
1313                                 initiate_offline_download();
1314                                 return false;
1315                         }
1316
1317                         if (keycode == 68) { // d
1318                                 displayDlg("offlineDownload");
1319                                 return false;
1320                         } */
1321
1322                         if (keycode == 87) { // w
1323                                 feeds_sort_by_unread = !feeds_sort_by_unread;
1324                                 return resort_feedlist();
1325                         }
1326
1327                         if (keycode == 72) { // h
1328                                 hideReadHeadlines();
1329                                 return;
1330                         }
1331
1332                 }
1333
1334                 /* Prefix c */
1335
1336                 if (hotkey_prefix == 67) { // c
1337                         hotkey_prefix = false;
1338
1339                         if (keycode == 70) { // f
1340                                 displayDlg("quickAddFilter", getActiveFeedId());
1341                                 return false;
1342                         }
1343
1344                         if (keycode == 76) { // l
1345                                 addLabel();
1346                                 return false;
1347                         }
1348
1349                         if (keycode == 83) { // s
1350                                 if (typeof collapse_feedlist != 'undefined') {
1351                                         collapse_feedlist();
1352                                         return false;
1353                                 }
1354                         }
1355
1356                         if (keycode == 77) { // m
1357                                 feedlist_sortable_enabled = !feedlist_sortable_enabled;
1358                                 if (feedlist_sortable_enabled) {
1359                                         notify_info("Category reordering enabled");
1360                                         toggle_sortable_feedlist(true);
1361                                 } else {
1362                                         notify_info("Category reordering disabled");
1363                                         toggle_sortable_feedlist(false);
1364                                 }
1365                         }
1366
1367                         if (keycode == 78) { // n
1368                                 catchupRelativeToArticle(1);
1369                                 return;
1370                         }
1371
1372                         if (keycode == 80) { // p
1373                                 catchupRelativeToArticle(0);
1374                                 return;
1375                         }
1376
1377
1378                 }
1379
1380                 /* Prefix g */
1381
1382                 if (hotkey_prefix == 71) { // g
1383
1384                         hotkey_prefix = false;
1385
1386
1387                         if (keycode == 65) { // a
1388                                 viewfeed(-4);
1389                                 return false;
1390                         }
1391
1392                         if (keycode == 83) { // s
1393                                 viewfeed(-1);
1394                                 return false;
1395                         }
1396
1397                         if (keycode == 80 && shift_key) { // P
1398                                 gotoPreferences();
1399                                 return false;
1400                         }
1401
1402                         if (keycode == 80) { // p
1403                                 viewfeed(-2);
1404                                 return false;
1405                         }
1406
1407                         if (keycode == 70) { // f
1408                                 viewfeed(-3);
1409                                 return false;
1410                         }
1411
1412                         if (keycode == 84 && shift_key) { // T
1413                                 toggleTags();
1414                                 return false;
1415                         }
1416                 }
1417
1418                 /* Cmd */
1419
1420                 if (hotkey_prefix == 224 || hotkey_prefix == 91) { // f 
1421                         hotkey_prefix = false;
1422                         return;
1423                 }
1424
1425                 if (hotkey_prefix) {
1426                         debug("KP: PREFIX=" + hotkey_prefix + " CODE=" + keycode + " CHAR=" + keychar);
1427                 } else {
1428                         debug("KP: CODE=" + keycode + " CHAR=" + keychar);
1429                 }
1430
1431
1432         } catch (e) {
1433                 exception_error("hotkey_handler", e);
1434         }
1435 }
1436
1437 function feedsSortByUnread() {
1438         return feeds_sort_by_unread;
1439 }
1440
1441 function addLabel() {
1442
1443         try {
1444
1445                 var caption = prompt(__("Please enter label caption:"), "");
1446
1447                 if (caption != undefined) {
1448         
1449                         if (caption == "") {
1450                                 alert(__("Can't create label: missing caption."));
1451                                 return false;
1452                         }
1453
1454                         var query = "backend.php?op=pref-labels&subop=add&caption=" + 
1455                                 param_escape(caption);
1456
1457                         notify_progress("Loading, please wait...", true);
1458
1459                         new Ajax.Request(query, {
1460                                 onComplete: function(transport) { 
1461                                         updateFeedList();
1462                         } });
1463
1464                 }
1465
1466         } catch (e) {
1467                 exception_error("addLabel", e);
1468         }
1469 }
1470
1471 function visitOfficialSite() {
1472         window.open("http://tt-rss.org/");
1473 }
1474
1475
1476 function feedBrowserSubscribe() {
1477         try {
1478
1479                 var selected = getSelectedFeedsFromBrowser();
1480
1481                 if (selected.length > 0) {
1482                         closeInfoBox();
1483
1484                         notify_progress("Loading, please wait...", true);
1485
1486                         var query =  "backend.php?op=pref-feeds&subop=massSubscribe&ids="+
1487                                 param_escape(selected.toString());
1488
1489                         new Ajax.Request(query, {
1490                                 onComplete: function(transport) { 
1491                                         updateFeedList();
1492                                 } });
1493
1494                 } else {
1495                         alert(__("No feeds are selected."));
1496                 }
1497
1498         } catch (e) {
1499                 exception_error("feedBrowserSubscribe", e);
1500         }
1501 }
1502
1503 function init_gears() {
1504         try {
1505
1506                 if (window.google && google.gears) {
1507                         localServer = google.gears.factory.create("beta.localserver");
1508                         store = localServer.createManagedStore("tt-rss");
1509                         db = google.gears.factory.create('beta.database');
1510                         db.open('tt-rss');
1511
1512                         db.execute("CREATE TABLE IF NOT EXISTS version (schema_version text)");
1513
1514                         var rs = db.execute("SELECT schema_version FROM version");
1515
1516                         var version = "";
1517
1518                         if (rs.isValidRow()) {
1519                                 version = rs.field(0);
1520                         }
1521
1522                         if (version != SCHEMA_VERSION) {
1523                                 db.execute("DROP TABLE cache");
1524                                 db.execute("DROP TABLE feeds");
1525                                 db.execute("DROP TABLE articles");
1526                                 db.execute("INSERT INTO version (schema_version) VALUES (?)", 
1527                                         [SCHEMA_VERSION]);
1528                         }
1529
1530                         db.execute("CREATE TABLE IF NOT EXISTS cache (id text, article text, param text, added text)");
1531
1532                         db.execute("CREATE TABLE if not exists feeds (id integer, title text, has_icon integer)");
1533
1534                         db.execute("CREATE TABLE if not exists articles (id integer, feed_id integer, title text, link text, guid text, updated text, content text, tags text, unread text, marked text, added text)");
1535
1536                         window.setTimeout("update_offline_data(0)", 100);
1537
1538                 }       
1539         
1540                 cache_expire();
1541
1542         } catch (e) {
1543                 exception_error("init_gears", e);
1544         }
1545 }
1546
1547