]> git.wh0rd.org - tt-rss.git/blob - offline.js
backend/rss: allow access to feeds without authentication when running in single...
[tt-rss.git] / offline.js
1 var SCHEMA_VERSION = 10;
2
3 var offline_mode = false;
4 var store = false;
5 var localServer = false;
6 var db = false;
7 var articles_synced = 0;
8 var sync_in_progress = false;
9 var sync_timer = false;
10
11 function view_offline(id, feed_id) {
12 try {
13
14 enableHotkeys();
15 showArticleInHeadlines(id);
16
17 db.execute("UPDATE articles SET unread = 0 WHERE id = ?", [id]);
18
19 var rs = db.execute("SELECT * FROM articles WHERE id = ?", [id]);
20
21 if (rs.isValidRow()) {
22
23 var tmp = "<div class=\"postReply\">";
24
25 tmp += "<div class=\"postHeader\" onmouseover=\"enable_resize(true)\" "+
26 "onmouseout=\"enable_resize(false)\">";
27
28 tmp += "<div class=\"postDate\">"+rs.fieldByName("updated")+"</div>";
29
30 if (rs.fieldByName("link") != "") {
31 tmp += "<div clear='both'><a target=\"_blank\" "+
32 "href=\"" + rs.fieldByName("link") + "\">" +
33 rs.fieldByName("title") + "</a></div>";
34 } else {
35 tmp += "<div clear='both'>" + rs.fieldByName("title") + "</div>";
36 }
37
38 /* tmp += "<div style='float : right'> "+
39 "<img src='images/tag.png' class='tagsPic' alt='Tags' title='Tags'>";
40 tmp += rs.fieldByName("tags");
41 tmp += "</div>"; */
42
43 /* tmp += "<div clear='both'>"+
44 "<a target=\"_blank\" "+
45 "href=\"" + rs.fieldByName("comments") + "\">" +
46 __("comments") + "</a></div>"; */
47
48 tmp += "</div>";
49
50 tmp += "<div class=\"postContent\">"
51 tmp += rs.fieldByName("content");
52 tmp += "</div>";
53
54 tmp += "</div>";
55
56 render_article(tmp);
57 update_local_feedlist_counters();
58 }
59
60 rs.close();
61
62 return false;
63
64 } catch (e) {
65 exception_error("view_offline", e);
66 }
67 }
68
69 function viewfeed_offline(feed_id, subop, is_cat, subop_param, skip_history, offset) {
70 try {
71 notify('');
72
73 if (!offset) offset = 0;
74
75 if (offset > 0) {
76 _feed_cur_page = parseInt(offset);
77 if (_infscroll_request_sent) {
78 return;
79 }
80 } else {
81 _feed_cur_page = 0;
82 _infscroll_disable = 0;
83 }
84
85 if (getActiveFeedId() != feed_id) {
86 _feed_cur_page = 0;
87 active_post_id = 0;
88 _infscroll_disable = 0;
89 }
90
91 loading_set_progress(100);
92
93 clean_feed_selections();
94
95 setActiveFeedId(feed_id, is_cat);
96
97 if (!is_cat) {
98 var feedr = $("FEEDR-" + feed_id);
99 if (feedr && !feedr.className.match("Selected")) {
100 feedr.className = feedr.className + "Selected";
101 }
102 } else {
103 var feedr = $("FCAT-" + feed_id);
104 if (feedr && !feedr.className.match("Selected")) {
105 feedr.className = feedr.className + "Selected";
106 }
107 }
108
109 if (subop == "MarkAllRead") {
110 catchup_local_feed(feed_id, is_cat);
111 }
112
113 disableContainerChildren("headlinesToolbar", false);
114 Form.enable("main_toolbar_form");
115
116 var f = $("headlines-frame");
117 try {
118 if (reply.offset == 0) {
119 debug("resetting headlines scrollTop");
120 f.scrollTop = 0;
121 }
122 } catch (e) { };
123
124
125 var tmp = "";
126 var feed_title = "";
127
128 if (is_cat) {
129 feed_title = get_local_category_title(feed_id);
130 } else {
131 feed_title = get_local_feed_title(feed_id);
132 }
133
134 if (feed_title) {
135
136 if (offset == 0) {
137 tmp += "<div id=\"headlinesContainer\">";
138
139 tmp += "<div class=\"headlinesSubToolbar\">";
140 tmp += "<div id=\"subtoolbar_ftitle\">";
141 tmp += feed_title;
142 tmp += "</div>";
143
144 var sel_all_link;
145 var sel_unread_link;
146 var sel_none_link;
147 var sel_inv_link;
148
149 if ($("content-frame")) {
150 sel_all_link = "javascript:selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', true, '', true)";
151 sel_unread_link = "javascript:selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', true, 'Unread', true)";
152 sel_none_link = "javascript:selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', false)";
153 sel_inv_link = "javascript:invertHeadlineSelection()";
154 } else {
155 sel_all_link = "javascript:cdmSelectArticles('all')";
156 sel_unread_link = "javascript:cdmSelectArticles('unread')";
157 sel_none_link = "javascript:cdmSelectArticles('none')";
158 sel_inv_link = "javascript:invertHeadlineSelection()";
159 }
160
161 tmp += __('Select:')+
162 " <a href=\""+sel_all_link+"\">"+__('All')+"</a>, "+
163 "<a href=\""+sel_unread_link+"\">"+__('Unread')+"</a>, "+
164 "<a href=\""+sel_inv_link+"\">"+__('Invert')+"</a>, "+
165 "<a href=\""+sel_none_link+"\">"+__('None')+"</a>";
166
167 tmp += "&nbsp;&nbsp;";
168
169 tmp += "<span onmouseover=\"enable_selection(false)\" "+
170 "onmouseout=\"enable_selection(true) \"" +
171 "onclick=\"toggleHeadlineActions()\" id=\"headlineActionsDrop\">" +
172 __("Actions...") + "&nbsp;&nbsp;<img src=\"images/down_arrow.png\">" +
173 "</span>";
174
175 tmp += "<ul id=\"headlineActionsBody\" style=\"display : none\">";
176
177 var tog_unread_link = "";
178 var tog_marked_link = "";
179 var catchup_sel_link = "";
180 var catchup_feed_link = "";
181
182 tmp += "<li class=\"insensitive\">"+__('Selection toggle:')+"</li>"+
183 "<li onclick=\""+tog_unread_link+"\">&nbsp;&nbsp;"+__('Unread')+"</li>"+
184 "<li onclick=\""+tog_marked_link+"\">&nbsp;&nbsp;"+__('Starred')+"</li>"+
185 "<li class=\"insensitive\">"+__('Mark as read:')+"</li>"+
186 "<li onclick=\""+catchup_sel_link+"\">&nbsp;&nbsp;"+__('Selection')+"</li>";
187
188 tmp += "<li onclick=\""+catchup_feed_link+"\">&nbsp;&nbsp;"+__('Entire feed')+
189 "</li>";
190
191 tmp += "</ul>";
192
193 tmp += "</div>";
194
195 tmp += "<div id=\"headlinesInnerContainer\" onscroll=\"headlines_scroll_handler()\">";
196 if ($("content-frame")) {
197 tmp += "<table class=\"headlinesList\" id=\"headlinesList\" cellspacing=\"0\">";
198 }
199
200 }
201
202 var limit = 30;
203
204 var toolbar_form = document.forms["main_toolbar_form"];
205
206 var limit = toolbar_form.limit[toolbar_form.limit.selectedIndex].value;
207 var view_mode = toolbar_form.view_mode[toolbar_form.view_mode.selectedIndex].value;
208
209 var limit_qpart = "";
210 var strategy_qpart = "";
211 var mode_qpart = "";
212 var offset_qpart = "";
213
214 if (limit != 0) {
215 limit_qpart = "LIMIT " + limit;
216 }
217
218 if (view_mode == "all_articles") {
219 mode_qpart = "1";
220 } else if (view_mode == "adaptive") {
221 if (is_cat && get_local_category_unread(feed_id) ||
222 get_local_feed_unread(feed_id) > 0) {
223 mode_qpart = "unread = 1";
224 } else {
225 mode_qpart = "1";
226 }
227
228 } else if (view_mode == "marked") {
229 mode_qpart = "marked = 1";
230 } else if (view_mode == "unread") {
231 mode_qpart = "unread = 1";
232 } else {
233 mode_qpart = "1";
234 }
235
236 var ext_tables_qpart = "";
237
238 if (is_cat) {
239 if (feed_id >= 0) {
240 strategy_qpart = "cat_id = " + feed_id;
241 } else if (feed_id == -2) {
242 strategy_qpart = "article_labels.id = articles.id";
243 ext_tables_qpart = ",article_labels";
244 }
245 } else if (feed_id > 0) {
246 strategy_qpart = "feed_id = " + feed_id;
247 } else if (feed_id == -1) {
248 strategy_qpart = "marked = 1";
249 } else if (feed_id == -4) {
250 strategy_qpart = "1";
251 } else if (feed_id < -10) {
252 var label_id = -11 - feed_id;
253 strategy_qpart = "article_labels.id = articles.id AND label_id = " + label_id;
254 ext_tables_qpart = ",article_labels";
255 }
256
257 if (offset > 0) {
258 offset_qpart = "OFFSET " + (offset*30);
259 } else {
260 offset_qpart = "";
261 }
262
263 var query = "SELECT *,feeds.title AS feed_title "+
264 "FROM articles,feeds,categories"+ext_tables_qpart+" "+
265 "WHERE " +
266 "cat_id = categories.id AND " +
267 "feed_id = feeds.id AND " +
268 strategy_qpart +
269 " AND " + mode_qpart +
270 " ORDER BY updated DESC "+
271 limit_qpart + " " +
272 offset_qpart;
273
274 var rs = db.execute(query);
275
276 var line_num = offset*30;
277
278 var real_feed_id = feed_id;
279
280 while (rs.isValidRow()) {
281
282 var id = rs.fieldByName("id");
283 var feed_id = rs.fieldByName("feed_id");
284
285 var entry_feed_title = false;
286
287 if (real_feed_id < 0 || is_cat) {
288 entry_feed_title = rs.fieldByName("feed_title");
289 }
290
291 var marked_pic;
292
293 var row_class = (line_num % 2) ? "even" : "odd";
294
295 if (rs.fieldByName("unread") == "1") {
296 row_class += "Unread";
297 }
298
299 var labels = get_local_article_labels(id);
300
301 var labels_str = "<span id=\"HLLCTR-"+id+"\">";
302 labels_str += format_article_labels(labels, id);
303 labels_str += "</span>";
304
305 if (rs.fieldByName("marked") == "1") {
306 marked_pic = "<img id=\"FMPIC-"+id+"\" "+
307 "src=\"images/mark_set.png\" class=\"markedPic\""+
308 "alt=\"Unstar article\" onclick='javascript:tMark("+id+")'>";
309 } else {
310 marked_pic = "<img id=\"FMPIC-"+id+"\" "+
311 "src=\"images/mark_unset.png\" class=\"markedPic\""+
312 "alt=\"Star article\" onclick='javascript:tMark("+id+")'>";
313 }
314
315 var mouseover_attrs = "onmouseover='postMouseIn($id)' "+
316 "onmouseout='postMouseOut($id)'";
317
318 var content_preview = truncate_string(strip_tags(rs.fieldByName("content")),
319 100);
320
321 if ($("content-frame")) {
322
323 tmp += "<tr class='"+row_class+"' id='RROW-"+id+"' "+mouseover_attrs+">";
324
325 tmp += "<td class='hlUpdPic'> </td>";
326
327 tmp += "<td class='hlSelectRow'>"+
328 "<input type=\"checkbox\" onclick=\"tSR(this)\" id=\"RCHK-"+id+"\"></td>";
329
330 tmp += "<td class='hlMarkedPic'>"+marked_pic+"</td>";
331
332 tmp += "<td onclick='view("+id+","+feed_id+")' "+
333 "class='hlContent' valign='middle'>";
334
335 tmp += "<a target=\"_blank\" id=\"RTITLE-"+id+"\" href=\"" +
336 rs.fieldByName("link") + "\"" +
337 "onclick=\"return view("+id+","+feed_id+");\">"+
338 rs.fieldByName("title");
339
340 tmp += "<span class=\"contentPreview\"> - "+content_preview+"</span>";
341
342 tmp += "</a>";
343
344 tmp += labels_str;
345
346 if (entry_feed_title) {
347 tmp += " <span class=\"hlFeed\">"+
348 "(<a href='javascript:viewfeed("+feed_id+
349 ")'>"+entry_feed_title+"</a>)</span>";
350 }
351
352 tmp += "</td>";
353
354 tmp += "<td class=\"hlUpdated\" onclick='view("+id+","+feed_id+")'>"+
355 "<nobr>"+rs.fieldByName("updated").substring(0,16)+
356 "</nobr></td>";
357
358 tmp += "</tr>";
359 } else {
360
361 var add_class = "";
362
363 if (rs.fieldByName("unread") == "1") {
364 add_class = "Unread";
365 }
366
367 tmp += "<div class=\"cdmArticle"+add_class+"\" id=\"RROW-"+id+"\" "+
368 mouseover_attrs+"'>";
369
370 feed_icon_img = "<img class=\"tinyFeedIcon\" src=\""+
371 getInitParam("icons_url")+"/"+feed_id+".ico\" alt=\"\">";
372 cdm_feed_icon = "<span style=\"cursor : pointer\" "+
373 "onclick=\"viewfeed("+feed_id+")\">"+feed_icon_img+"</span>";
374
375 tmp += "<div class=\"cdmHeader\">";
376 tmp += "<div class=\"articleUpdated\">"+
377 rs.fieldByName("updated").substring(0,16)+
378 " "+cdm_feed_icon+"</div>";
379
380 tmp += "<span id=\"RTITLE-"+id+"\" class=\"titleWrap\">"+
381 "<a class=\"title\" onclick=\"javascript:toggleUnread("+id+", 0)\""+
382 "target=\"_blank\" href=\""+rs.fieldByName("link")+
383 "\">"+rs.fieldByName("title")+"</a>";
384
385 tmp += labels_str;
386
387 if (entry_feed_title) {
388 tmp += "&nbsp;(<a href='javascript:viewfeed("+feed_id+
389 ")'>"+entry_feed_title+"</a>)";
390 }
391
392 tmp += "</span></div>";
393
394 tmp += "<div class=\"cdmContent\" onclick=\"cdmClicked("+id+")\""+
395 "id=\"CICD-"+id+"\">";
396 tmp += rs.fieldByName("content");
397 tmp += "<br clear='both'>"
398 tmp += "</div>";
399
400 tmp += "<div class=\"cdmFooter\"><span class='s0'>";
401 tmp += __("Select:")+
402 " <input type=\"checkbox\" "+
403 "onclick=\"toggleSelectRowById(this, 'RROW-"+id+"')\" "+
404 "class=\"feedCheckBox\" id=\"RCHK-"+id+"\">";
405
406 tmp += "</span><span class='s1'>"+marked_pic+"</span> ";
407
408 /* tmp += "<span class='s1'>"+
409 "<img class='tagsPic' src='images/tag.png' alt='Tags' title='Tags'>"+
410 "<span id=\"ATSTR-"+id+"\">"+rs.fieldByName("tags")+"</span>"+
411 "</span>"; */
412
413 tmp += "<span class='s2'>Toggle: <a class=\"cdmToggleLink\""+
414 "href=\"javascript:toggleUnread("+id+")\">"+
415 "Unread</a></span>";
416 tmp += "</div>";
417
418 tmp += "</div>";
419 }
420
421 rs.next();
422 line_num++;
423 }
424
425 if (line_num - offset*30 < 30) {
426 _infscroll_disable = 1;
427 }
428
429 rs.close();
430
431 if (offset == 0) {
432 tmp += "</table>";
433
434 if (line_num - offset*30 == 0) {
435 tmp += "<div class='whiteBox'>" +
436 __("No articles found to display.") +
437 "</div>";
438 }
439 tmp += "</div></div>";
440 }
441
442 if (offset == 0) {
443 var container = $("headlines-frame");
444 container.innerHTML = tmp;
445 } else {
446 var ids = getSelectedArticleIds2();
447
448 var container = $("headlinesList");
449 container.innerHTML = container.innerHTML + tmp;
450
451 for (var i = 0; i < ids.length; i++) {
452 markHeadline(ids[i]);
453 }
454 }
455 }
456
457 remove_splash();
458
459 _infscroll_request_sent = 0;
460
461 } catch (e) {
462 exception_error("viewfeed_offline", e);
463 }
464 }
465
466 function render_offline_feedlist() {
467 try {
468 var cats_enabled = getInitParam("enable_feed_cats") == "1";
469
470 var tmp = "<ul class=\"feedList\" id=\"feedList\">";
471
472 var unread = get_local_feed_unread(-4);
473
474 global_unread = unread;
475 updateTitle();
476
477 if (cats_enabled) {
478 tmp += printCategoryHeader(-1, is_local_cat_collapsed(-1), false);
479 }
480
481 tmp += printFeedEntry(-4, __("All articles"), "feed", unread,
482 "images/tag.png");
483
484 var unread = get_local_feed_unread(-1);
485
486 tmp += printFeedEntry(-1, __("Starred articles"), "feed", unread,
487 "images/mark_set.png");
488
489 if (cats_enabled) {
490 tmp += "</ul></li>";
491 } else {
492 tmp += "<li><hr/></li>";
493 }
494
495 if (cats_enabled) {
496 tmp += printCategoryHeader(-2, is_local_cat_collapsed(-2), false);
497 }
498
499 var rs = db.execute("SELECT id,caption "+
500 "FROM labels "+
501 "ORDER BY caption");
502
503 while (rs.isValidRow()) {
504 var id = -11 - parseInt(rs.field(0));
505 var caption = rs.field(1);
506 var unread = get_local_feed_unread(id);
507
508 tmp += printFeedEntry(id, caption, "feed", unread,
509 "images/label.png");
510
511 rs.next();
512 }
513
514 rs.close();
515
516 if (cats_enabled) {
517 tmp += "</ul></li>";
518 } else {
519 tmp += "<li><hr/></li>";
520 }
521
522 /* var rs = db.execute("SELECT feeds.id,feeds.title,has_icon,COUNT(articles.id) "+
523 "FROM feeds LEFT JOIN articles ON (feed_id = feeds.id) "+
524 "WHERE unread = 1 OR unread IS NULL GROUP BY feeds.id "+
525 "ORDER BY feeds.title"); */
526
527 var order_by = "feeds.title";
528
529 if (cats_enabled) order_by = "categories.title," + order_by;
530
531 var rs = db.execute("SELECT "+
532 "feeds.id,feeds.title,has_icon,cat_id,collapsed "+
533 "FROM feeds,categories WHERE cat_id = categories.id "+
534 "ORDER BY "+order_by);
535
536 var tmp_cat_id = -1;
537
538 while (rs.isValidRow()) {
539
540 var id = rs.field(0);
541 var title = rs.field(1);
542 var has_icon = rs.field(2);
543 var unread = get_local_feed_unread(id);
544 var cat_id = rs.field(3);
545 var cat_hidden = rs.field(4);
546
547 if (cat_id != tmp_cat_id && cats_enabled) {
548 if (tmp_cat_id != -1) {
549 tmp += "</ul></li>";
550 }
551 tmp += printCategoryHeader(cat_id, cat_hidden, true);
552 tmp_cat_id = cat_id;
553 }
554
555 var icon = "";
556
557 if (has_icon) {
558 icon = getInitParam("icons_url") + "/" + id + ".ico";
559 }
560
561 var feed_icon = "";
562
563 var row_class = "feed";
564
565 if (unread > 0) {
566 row_class += "Unread";
567 fctr_class = "feedCtrHasUnread";
568 } else {
569 fctr_class = "feedCtrNoUnread";
570 }
571
572 tmp += printFeedEntry(id, title, "feed", unread, icon);
573
574 rs.next();
575 }
576
577 rs.close();
578
579 if (cats_enabled) {
580 tmp += "</ul>";
581 }
582
583 tmp += "</ul>";
584
585 render_feedlist(tmp);
586 } catch (e) {
587 exception_error("render_offline_feedlist", e);
588 }
589 }
590
591 function init_offline() {
592 try {
593 offline_mode = true;
594 store.enabled = true;
595
596 Element.hide("dispSwitchPrompt");
597 Element.hide("feedBrowserPrompt");
598
599 Element.hide("topLinksOnline");
600 Element.show("topLinksOffline");
601
602 var tb_form = $("main_toolbar_form");
603 Element.hide(tb_form.update);
604
605 var chooser = $("quickMenuChooser");
606 chooser.disabled = true;
607
608 var rs = db.execute("SELECT key, value FROM init_params");
609
610 while (rs.isValidRow()) {
611 init_params[rs.field(0)] = rs.field(1);
612 rs.next();
613 }
614
615 rs.close();
616
617 var rs = db.execute("SELECT COUNT(*) FROM feeds");
618
619 var num_feeds = 0;
620
621 if (rs.isValidRow()) {
622 num_feeds = rs.field(0);
623 }
624
625 rs.close();
626
627 if (num_feeds == 0) {
628 remove_splash();
629 return fatalError(0,
630 __("Data for offline browsing has not been downloaded yet."));
631 }
632
633 render_offline_feedlist();
634 init_second_stage();
635 window.setTimeout("viewfeed(-4)", 50);
636
637 } catch (e) {
638 exception_error("init_offline", e);
639 }
640 }
641
642 function offline_download_parse(stage, transport) {
643 try {
644 if (transport.responseXML) {
645
646 if (!sync_in_progress) return;
647
648 var sync_ok = transport.responseXML.getElementsByTagName("sync-ok");
649
650 if (sync_ok.length > 0) {
651 for (var i = 0; i < sync_ok.length; i++) {
652 var id = sync_ok[i].getAttribute("id");
653 if (id) {
654 debug("synced offline info for id " + id);
655 db.execute("UPDATE articles SET modified = '' WHERE id = ?", [id]);
656 }
657 }
658 }
659
660 if (stage == 0) {
661
662 $("offlineModeSyncMsg").innerHTML = __("Synchronizing feeds...");
663
664 var feeds = transport.responseXML.getElementsByTagName("feed");
665
666 if (feeds.length > 0) {
667 db.execute("DELETE FROM feeds");
668 }
669
670 for (var i = 0; i < feeds.length; i++) {
671 var id = feeds[i].getAttribute("id");
672 var has_icon = feeds[i].getAttribute("has_icon");
673 var title = feeds[i].firstChild.nodeValue;
674 var cat_id = feeds[i].getAttribute("cat_id");
675
676 db.execute("INSERT INTO feeds (id,title,has_icon,cat_id)"+
677 "VALUES (?,?,?,?)",
678 [id, title, has_icon, cat_id]);
679 }
680
681 $("offlineModeSyncMsg").innerHTML = __("Synchronizing categories...");
682
683 var cats = transport.responseXML.getElementsByTagName("category");
684
685 if (feeds.length > 0) {
686 db.execute("DELETE FROM categories");
687 }
688
689 for (var i = 0; i < cats.length; i++) {
690 var id = cats[i].getAttribute("id");
691 var collapsed = cats[i].getAttribute("collapsed");
692 var title = cats[i].firstChild.nodeValue;
693
694 db.execute("INSERT INTO categories (id,title,collapsed)"+
695 "VALUES (?,?,?)",
696 [id, title, collapsed]);
697 }
698
699 $("offlineModeSyncMsg").innerHTML = __("Synchronizing labels...");
700
701 var labels = transport.responseXML.getElementsByTagName("label");
702
703 if (labels.length > 0) {
704 db.execute("DELETE FROM labels");
705 }
706
707 for (var i = 0; i < labels.length; i++) {
708 var id = labels[i].getAttribute("id");
709 var fg_color = labels[i].getAttribute("fg_color");
710 var bg_color = labels[i].getAttribute("bg_color");
711 var caption = labels[i].firstChild.nodeValue;
712
713 db.execute("INSERT INTO labels (id,caption,fg_color,bg_color)"+
714 "VALUES (?,?,?,?)",
715 [id, caption, fg_color, bg_color]);
716 }
717
718 $("offlineModeSyncMsg").innerHTML = __("Synchronizing articles...");
719
720 sync_timer = window.setTimeout("update_offline_data("+(stage+1)+")", 2*1000);
721 } else {
722
723 var articles = transport.responseXML.getElementsByTagName("article");
724
725 var limit = transport.responseXML.getElementsByTagName("limit")[0];
726
727 if (limit) {
728 limit = limit.getAttribute("value");
729 } else {
730 limit = 0;
731 }
732
733 var articles_found = 0;
734
735 for (var i = 0; i < articles.length; i++) {
736 var a = eval("("+articles[i].firstChild.nodeValue+")");
737 articles_found++;
738 if (a) {
739
740 db.execute("DELETE FROM articles WHERE id = ?", [a.id]);
741
742 db.execute("INSERT INTO articles "+
743 "(id, feed_id, title, link, guid, updated, content, "+
744 "unread, marked, tags, comments) "+
745 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
746 [a.id, a.feed_id, a.title, a.link, a.guid, a.updated,
747 a.content, a.unread, a.marked, a.tags,
748 a.comments]);
749
750 if (a.labels.length > 0) {
751 for (var j = 0; j < a.labels.length; j++) {
752 label_local_add_article(a.id, a.labels[j][0]);
753 }
754 }
755
756 }
757 }
758
759 debug("downloaded articles: " + articles_found + " limit: " + limit);
760
761 articles_synced += articles_found;
762
763 var msg =__("Synchronizing articles (%d)...").replace("%d", articles_synced);
764
765 $("offlineModeSyncMsg").innerHTML = msg;
766
767 var has_sync_data = has_local_sync_data();
768
769 if (articles_found >= limit || has_sync_data) {
770 sync_timer = window.setTimeout("update_offline_data("+(stage+1)+")",
771 3*1000);
772 debug("<b>update_offline_data: done " + stage + " HSD: " +
773 has_sync_data + "</b>");
774 } else {
775 window.setTimeout("offlineDownloadStart()", 180*1000);
776 debug("update_offline_data: finished");
777
778 var pic = $("offlineModePic");
779
780 if (pic) {
781 pic.src = "images/offline.png";
782
783 var rs = db.execute("SELECT value FROM syncdata WHERE key = 'last_online'");
784 var last_sync = "";
785
786 if (rs.isValidRow()) {
787 last_sync = rs.field(0).substring(0,16);
788 }
789 rs.close();
790
791 var msg = __("Last sync: %s").replace("%s", last_sync);
792
793 articles_synced = 0;
794
795 $("offlineModeSyncMsg").innerHTML = msg;
796 }
797
798 offlineSyncShowHideElems(false);
799
800 sync_in_progress = false;
801
802 db.execute("DELETE FROM articles WHERE "+
803 "updated < DATETIME('NOW', 'localtime', '-31 days')");
804
805 }
806 }
807
808 update_local_sync_data();
809
810
811 // notify('');
812
813 } else {
814 sync_in_progress = false;
815
816 var pic = $("offlineModePic");
817
818 if (pic) {
819 pic.src = "images/offline.png";
820 var msg = __("Last sync: Error receiving data.");
821 articles_synced = 0;
822 $("offlineModeSyncMsg").innerHTML = msg;
823 }
824
825 offlineSyncShowHideElems(false);
826 }
827
828 } catch (e) {
829 exception_error("offline_download_parse", e);
830 }
831 }
832
833 function update_offline_data(stage) {
834 try {
835
836 if (!stage) stage = 0;
837
838 if (!db || offline_mode || getInitParam("offline_enabled") != "1") return;
839
840 // notify_progress("Updating offline data... (" + stage +")", true);
841
842 var query = "backend.php?op=rpc&subop=download";
843
844 var rs = db.execute("SELECT MAX(id), MIN(id) FROM articles");
845
846 if (rs.isValidRow() && rs.field(0)) {
847 var offline_dl_max_id = rs.field(0);
848 var offline_dl_min_id = rs.field(1);
849
850 query = query + "&cidt=" + offline_dl_max_id;
851 query = query + "&cidb=" + offline_dl_min_id;
852
853 stage = 1;
854 }
855
856 rs.close();
857
858 debug("update_offline_data: stage " + stage);
859
860 query = query + "&stage=" + stage;
861
862 var to_sync = prepare_local_sync_data();
863
864 if (to_sync != "") {
865 to_sync = "?sync=" + param_escape(to_sync);
866 }
867
868 debug(query + "/" + to_sync);
869
870 var pic = $("offlineModePic");
871
872 if (pic) {
873 pic.src = "images/offline-sync.gif";
874 if (articles_synced == 0) {
875 $("offlineModeSyncMsg").innerHTML = __("Synchronizing...");
876 }
877 }
878
879 offlineSyncShowHideElems(true);
880 offlineUpdateStore();
881
882 sync_in_progress = true;
883
884 new Ajax.Request(query, {
885 parameters: to_sync,
886 onComplete: function(transport) {
887 offline_download_parse(stage, transport);
888 } });
889
890 } catch (e) {
891 exception_error("initiate_offline_download", e);
892 }
893 }
894
895 function set_feedlist_counter(id, ctr, is_cat) {
896 try {
897
898 var feedctr = $("FEEDCTR-" + id);
899 var feedu = $("FEEDU-" + id);
900 var feedr = $("FEEDR-" + id);
901
902 if (is_cat) {
903 var catctr = $("FCATCTR-" + id);
904 if (catctr) {
905 catctr.innerHTML = "(" + ctr + ")";
906 if (ctr > 0) {
907 catctr.className = "catCtrHasUnread";
908 } else {
909 catctr.className = "catCtrNoUnread";
910 }
911 }
912 } else if (feedctr && feedu && feedr) {
913
914 var row_needs_hl = (ctr > 0 && ctr > parseInt(feedu.innerHTML));
915
916 feedu.innerHTML = ctr;
917
918 if (ctr > 0) {
919 feedctr.className = "feedCtrHasUnread";
920 if (!feedr.className.match("Unread")) {
921 var is_selected = feedr.className.match("Selected");
922
923 feedr.className = feedr.className.replace("Selected", "");
924 feedr.className = feedr.className.replace("Unread", "");
925
926 feedr.className = feedr.className + "Unread";
927
928 if (is_selected) {
929 feedr.className = feedr.className + "Selected";
930 }
931
932 }
933
934 if (row_needs_hl) {
935 new Effect.Highlight(feedr, {duration: 1, startcolor: "#fff7d5",
936 queue: { position:'end', scope: 'EFQ-' + id, limit: 1 } } );
937 }
938 } else {
939 feedctr.className = "feedCtrNoUnread";
940 feedr.className = feedr.className.replace("Unread", "");
941 }
942 }
943
944 } catch (e) {
945 exception_error("set_feedlist_counter", e);
946 }
947 }
948
949 function update_local_feedlist_counters() {
950 try {
951 if (!offline_mode || !db) return;
952
953 /* var rs = db.execute("SELECT feeds.id,COUNT(articles.id) "+
954 "FROM feeds LEFT JOIN articles ON (feed_id = feeds.id) "+
955 "WHERE unread = 1 OR unread IS NULL GROUP BY feeds.id "+
956 "ORDER BY feeds.title"); */
957
958 var rs = db.execute("SELECT id FROM feeds "+
959 "ORDER BY title");
960
961 while (rs.isValidRow()) {
962 var id = rs.field(0);
963 var ctr = get_local_feed_unread(id);
964 set_feedlist_counter(id, ctr, false);
965 rs.next();
966 }
967
968 rs.close();
969
970 var rs = db.execute("SELECT cat_id,SUM(unread) "+
971 "FROM articles, feeds WHERE feeds.id = feed_id GROUP BY cat_id");
972
973 while (rs.isValidRow()) {
974 var id = rs.field(0);
975 var ctr = rs.field(1);
976 set_feedlist_counter(id, ctr, true);
977 rs.next();
978 }
979
980 rs.close();
981
982 set_feedlist_counter(-2, get_local_category_unread(-2), true);
983
984 set_feedlist_counter(-4, get_local_feed_unread(-4));
985 set_feedlist_counter(-1, get_local_feed_unread(-1));
986
987 var rs = db.execute("SELECT id FROM labels");
988
989 while (rs.isValidRow()) {
990 var id = -11 - rs.field(0);
991 var ctr = get_local_feed_unread(id);
992 set_feedlist_counter(id, ctr, false);
993 rs.next();
994 }
995
996 rs.close();
997
998 hideOrShowFeeds(getInitParam("hide_read_feeds") == 1);
999
1000 global_unread = get_local_feed_unread(-4);
1001 updateTitle();
1002
1003 } catch (e) {
1004 exception_error("update_local_feedlist_counters", e);
1005 }
1006 }
1007
1008 function get_local_feed_unread(id) {
1009 try {
1010 var rs;
1011
1012 if (id == -4) {
1013 rs = db.execute("SELECT SUM(unread) FROM articles");
1014 } else if (id == -1) {
1015 rs = db.execute("SELECT SUM(unread) FROM articles WHERE marked = 1");
1016 } else if (id > 0) {
1017 rs = db.execute("SELECT SUM(unread) FROM articles WHERE feed_id = ?", [id]);
1018 } else if (id < -10) {
1019 var label_id = -11 - id;
1020 rs = db.execute("SELECT SUM(unread) FROM articles,article_labels "+
1021 "WHERE article_labels.id = articles.id AND label_id = ?", [label_id]);
1022 }
1023
1024 var a = false;
1025
1026 if (rs.isValidRow()) {
1027 a = rs.field(0);
1028 } else {
1029 a = 0;
1030 }
1031
1032 rs.close();
1033
1034 return a;
1035
1036 } catch (e) {
1037 exception_error("get_local_feed_unread", e);
1038 }
1039 }
1040
1041 function enable_offline_reading() {
1042 try {
1043
1044 if (db && getInitParam("offline_enabled") == "1") {
1045 init_local_sync_data();
1046 Element.show("offlineModePic");
1047 offlineDownloadStart();
1048 }
1049
1050 } catch (e) {
1051 exception_error("enable_offline_reading", e);
1052 }
1053 }
1054
1055 function init_gears() {
1056 try {
1057
1058 if (window.google && google.gears) {
1059
1060 try {
1061 localServer = google.gears.factory.create("beta.localserver");
1062 } catch (e) {
1063 return;
1064 }
1065
1066 store = localServer.createManagedStore("tt-rss");
1067 store.manifestUrl = "manifest.json.php";
1068 store.enabled = false;
1069
1070 db = google.gears.factory.create('beta.database');
1071 db.open('tt-rss');
1072
1073 db.execute("CREATE TABLE IF NOT EXISTS version (schema_version text)");
1074
1075 var rs = db.execute("SELECT schema_version FROM version");
1076
1077 var version = "";
1078
1079 if (rs.isValidRow()) {
1080 version = rs.field(0);
1081 }
1082
1083 rs.close();
1084
1085 if (version != SCHEMA_VERSION) {
1086 db.execute("DROP TABLE IF EXISTS init_params");
1087 db.execute("DROP TABLE IF EXISTS cache");
1088 db.execute("DROP TABLE IF EXISTS feeds");
1089 db.execute("DROP TABLE IF EXISTS categories");
1090 db.execute("DROP TABLE IF EXISTS labels");
1091 db.execute("DROP TABLE IF EXISTS article_labels");
1092 db.execute("DROP TABLE IF EXISTS articles");
1093 db.execute("DROP INDEX IF EXISTS article_labels_label_id_idx");
1094 db.execute("DROP INDEX IF EXISTS articles_unread_idx");
1095 db.execute("DROP INDEX IF EXISTS articles_feed_id_idx");
1096 db.execute("DROP INDEX IF EXISTS articles_id_idx");
1097 db.execute("DROP INDEX IF EXISTS article_labels_id_idx");
1098 db.execute("DROP TABLE IF EXISTS version");
1099 db.execute("DROP TRIGGER IF EXISTS articles_update_unread");
1100 db.execute("DROP TRIGGER IF EXISTS articles_update_marked");
1101 db.execute("DROP TRIGGER IF EXISTS articles_remove_labelrefs");
1102 db.execute("CREATE TABLE IF NOT EXISTS version (schema_version text)");
1103 db.execute("DROP TABLE IF EXISTS syncdata");
1104 db.execute("INSERT INTO version (schema_version) VALUES (?)",
1105 [SCHEMA_VERSION]);
1106 }
1107
1108 db.execute("CREATE TABLE IF NOT EXISTS init_params (key text, value text)");
1109
1110 db.execute("CREATE TABLE IF NOT EXISTS cache (id integer, article text, param text, added text)");
1111 db.execute("CREATE TABLE IF NOT EXISTS feeds (id integer, title text, has_icon integer, cat_id integer)");
1112 db.execute("CREATE TABLE IF NOT EXISTS categories (id integer, title text, collapsed integer)");
1113 db.execute("CREATE TABLE IF NOT EXISTS labels (id integer, caption text, fg_color text, bg_color text)");
1114 db.execute("CREATE TABLE IF NOT EXISTS article_labels (id integer, label_id integer)");
1115 db.execute("CREATE TABLE IF NOT EXISTS articles (id integer, feed_id integer, title text, link text, guid text, updated timestamp, content text, tags text, unread integer, marked integer, added text, modified timestamp, comments text)");
1116
1117 db.execute("CREATE INDEX IF NOT EXISTS articles_unread_idx ON articles(unread)");
1118 db.execute("CREATE INDEX IF NOT EXISTS article_labels_label_id_idx ON article_labels(label_id)");
1119 db.execute("CREATE INDEX IF NOT EXISTS articles_feed_id_idx ON articles(feed_id)");
1120 db.execute("CREATE INDEX IF NOT EXISTS articles_id_idx ON articles(id)");
1121 db.execute("CREATE INDEX IF NOT EXISTS article_labels_id_idx ON article_labels(id)");
1122
1123 db.execute("CREATE TABLE IF NOT EXISTS syncdata (key integer, value text)");
1124
1125 db.execute("DELETE FROM cache WHERE id LIKE 'F:%' OR id LIKE 'C:%'");
1126
1127 db.execute("CREATE TRIGGER IF NOT EXISTS articles_update_unread "+
1128 "UPDATE OF unread ON articles "+
1129 "BEGIN "+
1130 "UPDATE articles SET modified = DATETIME('NOW', 'localtime') "+
1131 "WHERE id = OLD.id AND "+
1132 "OLD.unread != NEW.unread;"+
1133 "END;");
1134
1135 db.execute("CREATE TRIGGER IF NOT EXISTS articles_update_marked "+
1136 "UPDATE OF marked ON articles "+
1137 "BEGIN "+
1138 "UPDATE articles SET modified = DATETIME('NOW', 'localtime') "+
1139 "WHERE id = OLD.id;"+
1140 "END;");
1141
1142 db.execute("CREATE TRIGGER IF NOT EXISTS articles_remove_labelrefs "+
1143 "DELETE ON articles "+
1144 "BEGIN "+
1145 "DELETE FROM article_labels WHERE id = OLD.id; "+
1146 "END; ");
1147
1148 }
1149
1150 cache_expire();
1151
1152 } catch (e) {
1153 exception_error("init_gears", e);
1154 }
1155 }
1156
1157 function offlineArticlesStored() {
1158
1159 var rs = db.execute("SELECT COUNT(*) FROM articles");
1160 var count = 0;
1161 if (rs.isValidRow()) {
1162 count = rs.field(0);
1163 }
1164
1165 rs.close();
1166
1167 return count;
1168 }
1169
1170 function gotoOffline() {
1171
1172 // debug("[Local store] currentVersion = " + store.currentVersion);
1173
1174
1175 if (offlineArticlesStored() == 0) {
1176 notify_error("You have to synchronize some articles before going into offline mode.");
1177 return;
1178 }
1179
1180 if (confirm(__("Switch Tiny Tiny RSS into offline mode?"))) {
1181
1182 store.enabled = true;
1183 store.checkForUpdate();
1184
1185 notify_progress("Preparing offline mode...", true);
1186
1187 var timerId = window.setInterval(function() {
1188 if (store.currentVersion) {
1189 window.clearInterval(timerId);
1190 debug("[Local store] sync complete: " + store.currentVersion);
1191
1192 //window.location.href = "tt-rss.php";
1193
1194 offlineDownloadStop();
1195 init_offline();
1196
1197 notify_info("Tiny Tiny RSS is in offline mode.");
1198
1199 } else if (store.updateStatus == 3) {
1200 debug("[Local store] sync error: " + store.lastErrorMessage);
1201 notify_error(store.lastErrorMessage, true);
1202 } }, 500);
1203 }
1204 }
1205
1206 function gotoOnline() {
1207 // if (confirm(__("You won't be able to access offline version of Tiny Tiny RSS until you switch it into offline mode again. Go online?"))) {
1208 if (confirm(__("Tiny Tiny RSS will reload. Go online?"))) {
1209 //localServer.removeManagedStore("tt-rss");
1210 window.location.href = "tt-rss.php";
1211 }
1212 }
1213
1214 function local_collapse_cat(id) {
1215 try {
1216 if (db) {
1217 db.execute("UPDATE categories SET collapsed = NOT collapsed WHERE id = ?",
1218 [id]);
1219 }
1220 } catch (e) {
1221 exception_error("local_collapse_cat", e);
1222 }
1223 }
1224
1225 function get_local_category_title(id) {
1226 try {
1227
1228 var rs = db.execute("SELECT title FROM categories WHERE id = ?", [id]);
1229 var tmp = "";
1230
1231 if (rs.isValidRow()) {
1232 tmp = rs.field(0);
1233 }
1234
1235 rs.close();
1236
1237 return tmp;
1238
1239 } catch (e) {
1240 exception_error("get_local_category_title", e);
1241 }
1242 }
1243
1244 function get_local_category_unread(id) {
1245 try {
1246 var rs = false;
1247
1248 if (id >= 0) {
1249 rs = db.execute("SELECT SUM(unread) FROM articles, feeds "+
1250 "WHERE feeds.id = feed_id AND cat_id = ?", [id]);
1251 } else if (id == -2) {
1252 rs = db.execute("SELECT SUM(unread) FROM article_labels, articles "+
1253 "where article_labels.id = articles.id");
1254 } else {
1255 return 0;
1256 }
1257
1258 var tmp = 0;
1259
1260 if (rs.isValidRow()) {
1261 tmp = rs.field(0);
1262 }
1263
1264 rs.close();
1265
1266 return tmp;
1267
1268 } catch (e) {
1269 exception_error("get_local_category_unread", e);
1270 }
1271 }
1272
1273 function printCategoryHeader(cat_id, hidden, can_browse) {
1274 try {
1275 if (hidden == undefined) hidden = false;
1276 if (can_browse == undefined) can_browse = false;
1277
1278 var tmp_category = get_local_category_title(cat_id);
1279 var tmp = "";
1280
1281 var cat_unread = get_local_category_unread(cat_id);
1282
1283 var holder_style = "";
1284 var ellipsis = "";
1285
1286 if (hidden) {
1287 holder_style = "display:none;";
1288 ellipsis = "…";
1289 }
1290
1291 var catctr_class = (cat_unread > 0) ? "catCtrHasUnread" : "catCtrNoUnread";
1292
1293 var browse_cat_link = "";
1294 var inner_title_class = "catTitleNL";
1295
1296 if (can_browse) {
1297 browse_cat_link = "onclick=\"javascript:viewCategory("+cat_id+")\"";
1298 inner_title_class = "catTitle";
1299 }
1300
1301 var cat_class = "feedCat";
1302
1303 tmp += "<li class=\""+cat_class+"\" id=\"FCAT-"+cat_id+"\">"+
1304 "<img onclick=\"toggleCollapseCat("+cat_id+")\" class=\"catCollapse\""+
1305 " title=\""+__('Click to collapse category')+"\""+
1306 " src=\"images/cat-collapse.png\"><span class=\""+inner_title_class+"\" "+
1307 " id=\"FCATN-"+cat_id+"\" "+browse_cat_link+
1308 "\">"+tmp_category+"</span>";
1309
1310 tmp += "<span id=\"FCAP-"+cat_id+"\">";
1311
1312 tmp += " <span id=\"FCATCTR-"+cat_id+"\" "+
1313 "class=\""+catctr_class+"\">("+cat_unread+")</span> "+ellipsis;
1314
1315 tmp += "</span>";
1316
1317 tmp += "<ul class=\"feedCatList\" id=\"FCATLIST-"+cat_id+"\" "+
1318 "style='"+holder_style+"'>";
1319
1320 return tmp;
1321 } catch (e) {
1322 exception_error("printCategoryHeader", e);
1323 }
1324 }
1325
1326 function is_local_cat_collapsed(id) {
1327 try {
1328
1329 var rs = db.execute("SELECT collapsed FROM categories WHERE id = ?", [id]);
1330 var cat_hidden = 0;
1331
1332 if (rs.isValidRow()) {
1333 cat_hidden = rs.field(0);
1334 }
1335
1336 rs.close();
1337
1338 return cat_hidden == "1";
1339
1340 } catch (e) {
1341 exception_error("is_local_cat_collapsed", e);
1342 }
1343 }
1344
1345 function get_local_article_labels(id) {
1346 try {
1347 var rs = db.execute("SELECT DISTINCT label_id,caption,fg_color,bg_color "+
1348 "FROM labels, article_labels "+
1349 "WHERE labels.id = label_id AND article_labels.id = ?", [id]);
1350
1351 var tmp = new Array();
1352
1353 while (rs.isValidRow()) {
1354 var e = new Array();
1355
1356 e[0] = rs.field(0);
1357 e[1] = rs.field(1);
1358 e[2] = rs.field(2);
1359 e[3] = rs.field(3);
1360
1361 tmp.push(e);
1362
1363 rs.next();
1364 }
1365
1366 return tmp;
1367
1368 } catch (e) {
1369 exception_error("get_local_article_labels", e);
1370 }
1371 }
1372
1373 function label_local_add_article(id, label_id) {
1374 try {
1375 //debug("label_local_add_article " + id + " => " + label_id);
1376
1377 var rs = db.execute("SELECT COUNT(id) FROM article_labels WHERE "+
1378 "id = ? AND label_id = ?", [id, label_id]);
1379 var check = rs.field(0);
1380
1381 if (rs.isValidRow()) {
1382 var check = rs.field(0);
1383 }
1384 rs.close();
1385
1386 if (check == 0) {
1387 db.execute("INSERT INTO article_labels (id, label_id) VALUES "+
1388 "(?,?)", [id, label_id]);
1389 }
1390
1391 } catch (e) {
1392 exception_error("label_local_add_article", e);
1393 }
1394 }
1395
1396 function get_local_feed_title(id) {
1397 try {
1398
1399 var feed_title = "Unknown feed: " + id;
1400
1401 if (id > 0) {
1402 var rs = db.execute("SELECT title FROM feeds WHERE id = ?", [id]);
1403
1404 if (rs.isValidRow()) {
1405 feed_title = rs.field(0);
1406 }
1407
1408 rs.close();
1409 } else if (id == -1) {
1410 feed_title = __("Starred articles");
1411 } else if (id == -4) {
1412 feed_title = __("All articles");
1413 } else if (id < -10) {
1414
1415 var label_id = -11 - id;
1416
1417 var rs = db.execute("SELECT caption FROM labels WHERE id = ?", [label_id]);
1418
1419 if (rs.isValidRow()) {
1420 feed_title = rs.field(0);
1421 }
1422
1423 rs.close();
1424 }
1425
1426 return feed_title;
1427
1428 } catch (e) {
1429 exception_error("get_local_feed_title", e);
1430 }
1431 }
1432
1433 function format_article_labels(labels, id) {
1434 try {
1435
1436 var labels_str = "";
1437
1438 if (!labels) return "";
1439
1440 for (var i = 0; i < labels.length; i++) {
1441 var l = labels[i];
1442
1443 labels_str += "<span class='hlLabelRef' "+
1444 "style='color : "+l[2]+"; background-color : "+l[3]+"'>"+l[1]+"</span>";
1445 }
1446
1447 return labels_str;
1448
1449 } catch (e) {
1450 exception_error("format_article_labels", e);
1451 }
1452 }
1453
1454 function init_local_sync_data() {
1455 try {
1456
1457 if (!db) return;
1458
1459 var rs = db.execute("SELECT COUNT(*) FROM syncdata WHERE key = 'last_online'");
1460 var has_last_online = 0;
1461
1462 if (rs.isValidRow()) {
1463 has_last_online = rs.field(0);
1464 }
1465
1466 rs.close();
1467
1468 if (!has_last_online) {
1469 db.execute("INSERT INTO syncdata (key, value) VALUES ('last_online', '')");
1470 }
1471
1472 } catch (e) {
1473 exception_error("init_local_sync_data", e);
1474
1475 }
1476 }
1477
1478 function has_local_sync_data() {
1479 try {
1480
1481 var rs = db.execute("SELECT id FROM articles "+
1482 "WHERE modified > (SELECT value FROM syncdata WHERE key = 'last_online') "+
1483 "LIMIT 1");
1484
1485 var tmp = 0;
1486
1487 if (rs.isValidRow()) {
1488 tmp = rs.field(0);
1489 }
1490
1491 rs.close();
1492
1493 return tmp != 0;
1494
1495 } catch (e) {
1496 exception_error("has_local_sync_data", e);
1497 }
1498 }
1499
1500 function prepare_local_sync_data() {
1501 try {
1502 var rs = db.execute("SELECT value FROM syncdata WHERE key = 'last_online'");
1503
1504 var last_online = "";
1505
1506 if (rs.isValidRow()) {
1507 last_online = rs.field(0);
1508 }
1509
1510 rs.close();
1511
1512 var rs = db.execute("SELECT id,unread,marked FROM articles "+
1513 "WHERE modified > ? LIMIT 200", [last_online]);
1514
1515 var tmp = last_online + ";";
1516
1517 var entries = 0;
1518
1519 while (rs.isValidRow()) {
1520 var e = new Array();
1521
1522 tmp = tmp + rs.field(0) + "," + rs.field(1) + "," + rs.field(2) + ";";
1523 entries++;
1524
1525 rs.next();
1526 }
1527
1528 rs.close();
1529
1530 if (entries > 0) {
1531 return tmp;
1532 } else {
1533 return '';
1534 }
1535
1536 } catch (e) {
1537 exception_error("prepare_local_sync_data", e);
1538 }
1539 }
1540
1541 function update_local_sync_data() {
1542 try {
1543 if (db && !offline_mode) {
1544
1545 var rs = db.execute("SELECT id FROM articles "+
1546 "WHERE modified > (SELECT value FROM syncdata WHERE "+
1547 "key = 'last_online') LIMIT 1")
1548
1549 var f_id = 0;
1550
1551 if (rs.isValidRow()) {
1552 f_id = rs.field(0);
1553 }
1554
1555 rs.close();
1556
1557 /* no pending articles to sync */
1558
1559 if (f_id == 0) {
1560 db.execute("UPDATE syncdata SET value = DATETIME('NOW', 'localtime') "+
1561 "WHERE key = 'last_online'");
1562 }
1563
1564 }
1565 } catch (e) {
1566 exception_error("update_local_sync_data", e);
1567 }
1568 }
1569
1570 function catchup_local_feed(id, is_cat) {
1571 try {
1572 if (!db) return;
1573
1574 if (!is_cat) {
1575 if (id >= 0) {
1576 db.execute("UPDATE articles SET unread = 0 WHERE feed_id = ?", [id]);
1577 } else if (id == -1) {
1578 db.execute("UPDATE articles SET unread = 0 WHERE marked = 1");
1579 } else if (id == -4) {
1580 db.execute("UPDATE articles SET unread = 0");
1581 } else if (id < -10) {
1582 var label_id = -11-id;
1583
1584 db.execute("UPDATE articles SET unread = 0 WHERE "+
1585 "(SELECT COUNT(*) FROM article_labels WHERE "+
1586 "article_labels.id = articles.id AND label_id = ?) > 0", [label_id]);
1587 }
1588 }
1589
1590 update_local_feedlist_counters();
1591
1592 } catch (e) {
1593 exception_error("catchup_local_feed", e);
1594 }
1595 }
1596
1597 function toggleOfflineModeInfo() {
1598 try {
1599 var e = $('offlineModeDrop');
1600 var p = $('offlineModePic');
1601
1602 if (Element.visible(e)) {
1603 Element.hide(e);
1604 } else {
1605 Element.show(e);
1606 }
1607
1608 } catch (e) {
1609 exception_error("toggleOfflineModeInfo", e);
1610 }
1611 }
1612
1613 function offlineDownloadStart() {
1614 try {
1615 if (db && !sync_in_progress && getInitParam("offline_enabled") == "1") {
1616 window.setTimeout("update_offline_data(0)", 100);
1617 }
1618 } catch (e) {
1619 exception_error("offlineDownloadStart", e);
1620 }
1621 }
1622
1623 function offlineDownloadStop() {
1624 try {
1625 if (db && sync_in_progress && getInitParam("offline_enabled") == "1") {
1626
1627 sync_in_progress = false;
1628
1629 if (sync_timer) {
1630 window.clearTimeout(sync_timer);
1631 sync_timer = false;
1632 }
1633
1634 var pic = $("offlineModePic");
1635
1636 if (pic) {
1637 pic.src = "images/offline.png";
1638 var msg = __("Last sync: Cancelled.");
1639 articles_synced = 0;
1640 $("offlineModeSyncMsg").innerHTML = msg;
1641 }
1642
1643 offlineSyncShowHideElems(false);
1644
1645 }
1646 } catch (e) {
1647 exception_error("offlineDownloadStart", e);
1648 }
1649 }
1650
1651 function offlineClearData() {
1652 try {
1653 if (db) {
1654
1655 if (confirm(__("This will remove all offline data stored by Tiny Tiny RSS on this computer. Continue?"))) {
1656
1657 notify_progress("Removing offline data...");
1658
1659 localServer.removeManagedStore("tt-rss");
1660
1661 db.execute("DELETE FROM articles");
1662 db.execute("DELETE FROM article_labels");
1663 db.execute("DELETE FROM labels");
1664 db.execute("DELETE FROM feeds");
1665 db.execute("DELETE FROM cache");
1666
1667 notify_info("Local data removed.");
1668 }
1669 }
1670 } catch (e) {
1671 exception_error("offlineClearData", e);
1672 }
1673 }
1674
1675 function offlineUpdateStore() {
1676 try {
1677 if (offline_mode || !store) return;
1678
1679 store.checkForUpdate();
1680
1681 var timerId = window.setInterval(function() {
1682 if (store.currentVersion) {
1683 window.clearInterval(timerId);
1684 debug("[Local store] sync complete: " + store.currentVersion);
1685 } else if (store.updateStatus == 3) {
1686 debug("[Local store] sync error: " + store.lastErrorMessage);
1687 } }, 500);
1688
1689 } catch (e) {
1690 exception_error("offlineUpdateStore", e);
1691 }
1692 }
1693
1694 function offlineSyncShowHideElems(syncing) {
1695 try {
1696
1697 var elems = $$("div.hideWhenSyncing");
1698
1699 for (var j = 0; j < elems.length; j++) {
1700 if (syncing) {
1701 Element.hide(elems[j]);
1702 } else {
1703 Element.show(elems[j]);
1704 }
1705 }
1706
1707 var elems = $$("div.showWhenSyncing");
1708
1709 for (var j = 0; j < elems.length; j++) {
1710 if (syncing) {
1711 Element.show(elems[j]);
1712 } else {
1713 Element.hide(elems[j]);
1714 }
1715 }
1716
1717 } catch (e) {
1718 exception_error("offlineSyncShowHideElems", e);
1719 }
1720 }
1721
1722 function offlineConfirmModeChange() {
1723 try {
1724
1725 if (db && getInitParam("offline_enabled") == "1" && offlineArticlesStored() > 0) {
1726 var ok = confirm(__("Tiny Tiny RSS has trouble accessing its server. Would you like to go offline?"));
1727
1728 if (ok) {
1729 init_offline();
1730 }
1731 }
1732
1733 } catch (e) {
1734 exception_error("offlineConfirmModeChange", e);
1735 }
1736 }