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