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