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