]> git.wh0rd.org Git - tt-rss.git/blob - offline.js
81cc5b37b08a9febf56d90d9ead744bbd0c80315
[tt-rss.git] / offline.js
1 var SCHEMA_VERSION = 5;
2
3 var offline_mode = false;
4 var store = false;
5 var localServer = false;
6 var db = false;
7
8 function view_offline(id, feed_id) {
9         try {
10
11                 enableHotkeys();
12                 showArticleInHeadlines(id);
13
14                 db.execute("UPDATE articles SET unread = 0 WHERE id = ?", [id]);
15
16                 var rs = db.execute("SELECT * FROM articles WHERE id = ?", [id]);
17
18                 if (rs.isValidRow()) {
19
20                         var tmp = "<div class=\"postReply\">";
21
22                         tmp += "<div class=\"postHeader\" onmouseover=\"enable_resize(true)\" "+
23                                 "onmouseout=\"enable_resize(false)\">";
24
25                         tmp += "<div class=\"postDate\">"+rs.fieldByName("updated")+"</div>";
26
27                         if (rs.fieldByName("link") != "") {
28                                 tmp += "<div clear='both'><a target=\"_blank\" "+
29                                         "href=\"" + rs.fieldByName("link") + "\">" +
30                                         rs.fieldByName("title") + "</a></div>";
31                         } else {
32                                 tmp += "<div clear='both'>" + rs.fieldByName("title") + "</div>";
33                         }
34
35 /*                      tmp += "<div style='float : right'> "+
36                                 "<img src='images/tag.png' class='tagsPic' alt='Tags' title='Tags'>";
37                         tmp += rs.fieldByName("tags");
38                         tmp += "</div>"; */
39
40                         tmp += "<div clear='both'>"+
41                                 "<a target=\"_blank\" "+
42                                         "href=\"" + rs.fieldByName("comments") + "\">" +
43                                         __("comments") + "</a></div>";
44
45                         tmp += "</div>";
46
47                         tmp += "<div class=\"postContent\">"
48                         tmp += rs.fieldByName("content");
49                         tmp += "</div>";
50
51                         tmp += "</div>";
52
53                         render_article(tmp);
54                         update_local_feedlist_counters();
55                 }
56
57                 rs.close();
58
59                 return false;
60
61         } catch (e) {
62                 exception_error("view_offline", e);
63         }
64 }
65
66 function viewfeed_offline(feed_id, subop, is_cat, subop_param, skip_history, offset) {
67         try {
68                 notify('');
69
70                 if (!offset) offset = 0;
71
72                 if (offset > 0) {
73                         _feed_cur_page = parseInt(offset);
74                         if (_infscroll_request_sent) {
75                                 return;
76                         }
77                 } else {
78                         _feed_cur_page = 0;
79                         _infscroll_disable = 0;
80                 }
81
82                 if (getActiveFeedId() != feed_id) {
83                         _feed_cur_page = 0;
84                         active_post_id = 0;
85                         _infscroll_disable = 0;
86                 }
87
88                 loading_set_progress(100);
89
90                 clean_feed_selections();
91         
92                 setActiveFeedId(feed_id, is_cat);
93
94                 if (!is_cat) {
95                         var feedr = document.getElementById("FEEDR-" + feed_id);
96                         if (feedr && !feedr.className.match("Selected")) {      
97                                 feedr.className = feedr.className + "Selected";
98                         } 
99                 } else {
100                         var feedr = document.getElementById("FCAT-" + feed_id);
101                         if (feedr && !feedr.className.match("Selected")) {      
102                                 feedr.className = feedr.className + "Selected";
103                         } 
104                 }
105
106                 disableContainerChildren("headlinesToolbar", false);
107                 Form.enable("main_toolbar_form");
108
109                 var f = document.getElementById("headlines-frame");
110                 try {
111                         if (reply.offset == 0) { 
112                                 debug("resetting headlines scrollTop");
113                                 f.scrollTop = 0; 
114                         }
115                 } catch (e) { };
116
117
118                 var tmp = "";
119
120                 rs = db.execute("SELECT title FROM feeds WHERE id = ?", [feed_id]);
121
122                 if (rs.isValidRow() || feed_id == -1 || feed_id == -4) {
123
124                         feed_title = rs.field(0);
125
126                         if (feed_id == -1) {
127                                 feed_title = __("Starred articles");
128                         }
129
130                         if (feed_id == -4) {
131                                 feed_title = __("All articles");
132                         }
133
134                         if (offset == 0) {
135                                 tmp += "<div id=\"headlinesContainer\">";
136                 
137                                 tmp += "<div class=\"headlinesSubToolbar\">";
138                                 tmp += "<div id=\"subtoolbar_ftitle\">";
139                                 tmp += feed_title;
140                                 tmp += "</div>";
141
142                                 var sel_all_link;
143                                 var sel_unread_link;
144                                 var sel_none_link;
145                                 var sel_inv_link;
146
147                                 if (document.getElementById("content-frame")) {
148                                         sel_all_link = "javascript:selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', true, '', true)";
149                                         sel_unread_link = "javascript:selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', true, 'Unread', true)";
150                                         sel_none_link = "javascript:selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', false)";
151                                         sel_inv_link = "javascript:invertHeadlineSelection()";
152                                 } else {
153                                         sel_all_link = "javascript:cdmSelectArticles('all')";
154                                         sel_unread_link = "javascript:cdmSelectArticles('unread')";
155                                         sel_none_link = "javascript:cdmSelectArticles('none')";
156                                         sel_inv_link = "javascript:invertHeadlineSelection()";
157                                 }
158
159                                 tmp += __('Select:')+
160                                         " <a href=\""+sel_all_link+"\">"+__('All')+"</a>, "+
161                                         "<a href=\""+sel_unread_link+"\">"+__('Unread')+"</a>, "+
162                                         "<a href=\""+sel_inv_link+"\">"+__('Invert')+"</a>, "+
163                                         "<a href=\""+sel_none_link+"\">"+__('None')+"</a>";
164         
165                                 tmp += "&nbsp;&nbsp;";
166         
167                                 tmp += "</div>";
168         
169                                 tmp += "<div id=\"headlinesInnerContainer\" onscroll=\"headlines_scroll_handler()\">";
170                                 if (document.getElementById("content-frame")) {
171                                         tmp += "<table class=\"headlinesList\" id=\"headlinesList\" cellspacing=\"0\">";
172                                 }
173                         
174                         }
175         
176                         var limit = 30;
177                 
178                         var toolbar_form = document.forms["main_toolbar_form"];
179                         
180                         var limit = toolbar_form.limit[toolbar_form.limit.selectedIndex].value;
181                         var view_mode = toolbar_form.view_mode[toolbar_form.view_mode.selectedIndex].value;
182
183                         var limit_qpart = "";
184                         var strategy_qpart = "";
185                         var mode_qpart = "";
186                         var offset_qpart = "";
187
188                         if (limit != 0) {
189                                 limit_qpart = "LIMIT " + limit;
190                         }
191
192                         if (view_mode == "all_articles") {
193                                 mode_qpart = "1";
194                         } else if (view_mode == "adaptive") {
195                                 if (get_local_feed_unread(feed_id) > 0) {
196                                         mode_qpart = "unread = 1";
197                                 } else {
198                                         mode_qpart = "1";
199                                 }
200                         } else if (view_mode == "marked") {
201                                 mode_qpart = "marked = 1";
202                         } else if (view_mode == "unread") {
203                                 mode_qpart = "unread = 1";
204                         } else {
205                                 mode_qpart = "1";
206                         }
207
208                         if (feed_id > 0) {
209                                 strategy_qpart = "feed_id = " + feed_id;
210                         } else if (feed_id == -1) {
211                                 strategy_qpart = "marked = 1";
212                         } else if (feed_id == -4) {
213                                 strategy_qpart = "1";
214                         }
215
216                         if (offset > 0) {
217                                 offset_qpart = "OFFSET " + (offset*30);
218                         } else {
219                                 offset_qpart = "";
220                         }
221
222                         var query = "SELECT *,feeds.title AS feed_title "+
223                                 "FROM articles,feeds WHERE " +
224                                 "feed_id = feeds.id AND " +
225                                 strategy_qpart +
226                                 " AND " + mode_qpart + 
227                                 " ORDER BY updated DESC "+
228                                 limit_qpart + " " +
229                                 offset_qpart;
230
231                         var rs = db.execute(query);
232
233                         var line_num = offset*30;
234
235                         var real_feed_id = feed_id;
236
237                         while (rs.isValidRow()) {
238
239                                 var id = rs.fieldByName("id");
240                                 var feed_id = rs.fieldByName("feed_id");
241
242                                 var entry_feed_title = false;
243
244                                 if (real_feed_id < 0) {
245                                         entry_feed_title = rs.fieldByName("feed_title");
246                                 }
247
248                                 var marked_pic;
249         
250                                 var row_class = (line_num % 2) ? "even" : "odd";
251
252                                 if (rs.fieldByName("unread") == "1") {
253                                         row_class += "Unread";
254                                 }
255         
256                                 if (rs.fieldByName("marked") == "1") {
257                                         marked_pic = "<img id=\"FMPIC-"+id+"\" "+
258                                                 "src=\"images/mark_set.png\" class=\"markedPic\""+
259                                                 "alt=\"Unstar article\" onclick='javascript:tMark("+id+")'>";
260                                 } else {
261                                         marked_pic = "<img id=\"FMPIC-"+id+"\" "+
262                                                 "src=\"images/mark_unset.png\" class=\"markedPic\""+
263                                                 "alt=\"Star article\" onclick='javascript:tMark("+id+")'>";
264                                 }
265
266                                 var mouseover_attrs = "onmouseover='postMouseIn($id)' "+
267                                         "onmouseout='postMouseOut($id)'";
268
269                                 var content_preview = truncate_string(strip_tags(rs.fieldByName("content")), 
270                                                 100);
271         
272                                 if (document.getElementById("content-frame")) {
273
274                                         tmp += "<tr class='"+row_class+"' id='RROW-"+id+"' "+mouseover_attrs+">";
275                                         
276                                         tmp += "<td class='hlUpdPic'> </td>";
277         
278                                         tmp += "<td class='hlSelectRow'>"+
279                                                 "<input type=\"checkbox\" onclick=\"tSR(this)\" id=\"RCHK-"+id+"\"></td>";
280                                         
281                                         tmp += "<td class='hlMarkedPic'>"+marked_pic+"</td>";
282                 
283                                         tmp += "<td onclick='view("+id+","+feed_id+")' "+
284                                                 "class='hlContent' valign='middle'>";
285                 
286                                         tmp += "<a target=\"_blank\" id=\"RTITLE-"+id+"\" href=\"" + 
287                                                 rs.fieldByName("link") + "\"" +
288                                                 "onclick=\"return view("+id+","+feed_id+");\">"+
289                                                 rs.fieldByName("title");
290         
291                                         tmp += "<span class=\"contentPreview\"> - "+content_preview+"</span>";
292         
293                                         tmp += "</a>";
294
295                                         if (entry_feed_title) {
296                                                 tmp += " <span class=\"hlFeed\">"+
297                                                         "(<a href='javascript:viewfeed("+feed_id+
298                                                         ")'>"+entry_feed_title+"</a>)</span>";
299                                         }
300
301                                         tmp += "</td>";
302
303                                         tmp += "<td class=\"hlUpdated\" onclick='view("+id+","+feed_id+")'>"+
304                                                 "<nobr>"+rs.fieldByName("updated").substring(0,16)+
305                                                 "</nobr></td>";
306         
307                                         tmp += "</tr>";
308                                 } else {
309
310                                         var add_class = "";
311
312                                         if (rs.fieldByName("unread") == "1") {
313                                                 add_class = "Unread";                                   
314                                         }
315                                 
316                                         tmp += "<div class=\"cdmArticle"+add_class+"\" id=\"RROW-"+id+"\" "+
317                                                 mouseover_attrs+"'>";
318
319                                         feed_icon_img = "<img class=\"tinyFeedIcon\" src=\""+
320                                                 getInitParam("icons_url")+"/"+feed_id+".ico\" alt=\"\">";
321                                         cdm_feed_icon = "<span style=\"cursor : pointer\" "+
322                                                 "onclick=\"viewfeed("+feed_id+")\">"+feed_icon_img+"</span>";
323
324                                         tmp += "<div class=\"cdmHeader\">";
325                                         tmp += "<div class=\"articleUpdated\">"+
326                                                 rs.fieldByName("updated").substring(0,16)+
327                                                 " "+cdm_feed_icon+"</div>";
328
329                                         tmp += "<span id=\"RTITLE-"+id+"\" class=\"titleWrap\">"+
330                                                 "<a class=\"title\" onclick=\"javascript:toggleUnread("+id+", 0)\""+
331                                                 "target=\"_blank\" href=\""+rs.fieldByName("link")+
332                                                 "\">"+rs.fieldByName("title")+"</a>";
333
334                                         if (entry_feed_title) {
335                                                 tmp += "&nbsp;(<a href='javascript:viewfeed("+feed_id+
336                                                         ")'>"+entry_feed_title+"</a>)";
337                                         }
338
339                                         tmp += "</span></div>";
340
341                                         tmp += "<div class=\"cdmContent\" onclick=\"cdmClicked("+id+")\""+
342                                                 "id=\"CICD-"+id+"\">";
343                                         tmp += rs.fieldByName("content");
344                                         tmp += "<br clear='both'>"
345                                         tmp += "</div>"; 
346
347                                         tmp += "<div class=\"cdmFooter\"><span class='s0'>";
348                                         tmp += __("Select:")+
349                                                 " <input type=\"checkbox\" "+
350                                                 "onclick=\"toggleSelectRowById(this, 'RROW-"+id+"')\" "+
351                                                 "class=\"feedCheckBox\" id=\"RCHK-"+id+"\">";
352
353                                         tmp += "</span><span class='s1'>"+marked_pic+"</span> ";
354
355 /*                                      tmp += "<span class='s1'>"+
356                                                 "<img class='tagsPic' src='images/tag.png' alt='Tags' title='Tags'>"+
357                                                 "<span id=\"ATSTR-"+id+"\">"+rs.fieldByName("tags")+"</span>"+
358                                                 "</span>"; */
359
360                                         tmp += "<span class='s2'>Toggle: <a class=\"cdmToggleLink\""+
361                                                 "href=\"javascript:toggleUnread("+id+")\">"+
362                                                 "Unread</a></span>";
363                                         tmp += "</div>";
364
365                                         tmp += "</div>";
366                                 }
367
368                                 rs.next();
369                                 line_num++;
370                         }
371
372                         if (line_num - offset*30 < 30) {
373                                 _infscroll_disable = 1;
374                         }
375
376                         rs.close();
377         
378                         if (offset == 0) {
379                                 tmp += "</table>";
380
381                                 if (line_num - offset*30 == 0) {
382                                         tmp += "<div class='whiteBox'>" +
383                                                 __("No articles found to display.") +
384                                                 "</div>";
385                                 }
386                                 tmp += "</div></div>";
387                         }
388         
389                         if (offset == 0) {
390                                 var container = document.getElementById("headlines-frame");
391                                 container.innerHTML = tmp;
392                         } else {
393                                 var ids = getSelectedArticleIds2();
394                 
395                                 var container = document.getElementById("headlinesList");
396                                 container.innerHTML = container.innerHTML + tmp;
397         
398                                 for (var i = 0; i < ids.length; i++) {
399                                         markHeadline(ids[i]);
400                                 }
401                         }
402                 }
403
404                 remove_splash();
405
406                 _infscroll_request_sent = 0;
407
408         } catch (e) {
409                 exception_error("viewfeed_offline", e);
410         }
411 }
412
413 function render_offline_feedlist() {
414         try {
415                 var cats_enabled = 1;
416
417                 var tmp = "<ul class=\"feedList\" id=\"feedList\">";
418
419                 var unread = get_local_feed_unread(-4);
420
421                 global_unread = unread;
422                 updateTitle();
423
424                 if (cats_enabled) {
425                         tmp += printCategoryHeader(-1, getCookie("ttrss_vf_vclps"), false);
426                 }
427
428                 tmp += printFeedEntry(-4, __("All articles"), "feed", unread,
429                         "images/tag.png");
430
431                 var unread = get_local_feed_unread(-1);
432
433                 tmp += printFeedEntry(-1, __("Starred articles"), "feed", unread,
434                         "images/mark_set.png");
435
436                 if (cats_enabled) {
437                         tmp += "</ul></li>";
438                 } else {
439                         tmp += "<li><hr/></li>";
440                 }
441
442 /*              var rs = db.execute("SELECT feeds.id,feeds.title,has_icon,COUNT(articles.id) "+
443                         "FROM feeds LEFT JOIN articles ON (feed_id = feeds.id) "+
444                         "WHERE unread = 1 OR unread IS NULL GROUP BY feeds.id "+
445                         "ORDER BY feeds.title"); */
446
447                 var order_by = "feeds.title";
448
449                 if (cats_enabled) order_by = "categories.title," + order_by;
450
451                 var rs = db.execute("SELECT "+
452                         "feeds.id,feeds.title,has_icon,cat_id,collapsed "+
453                         "FROM feeds,categories WHERE cat_id = categories.id "+
454                         "ORDER BY "+order_by);
455
456                 var tmp_cat_id = -1;
457
458                 while (rs.isValidRow()) {
459
460                         var id = rs.field(0);
461                         var title = rs.field(1);
462                         var has_icon = rs.field(2);
463                         var unread = get_local_feed_unread(id);
464                         var cat_id = rs.field(3);
465                         var cat_hidden = rs.field(4);
466
467                         if (cat_id != tmp_cat_id && cats_enabled) {
468                                 if (tmp_cat_id != -1) {
469                                         tmp += "</ul></li>";
470                                 }
471                                 tmp += printCategoryHeader(cat_id, cat_hidden, false);
472                                 tmp_cat_id = cat_id;
473                         }
474
475                         var icon = "";
476
477                         if (has_icon) {
478                                 icon = "icons/" + id + ".ico";
479                         }
480
481                         var feed_icon = "";
482
483                         var row_class = "feed";
484
485                         if (unread > 0) {
486                                 row_class += "Unread";
487                                 fctr_class = "feedCtrHasUnread";
488                         } else {
489                                 fctr_class = "feedCtrNoUnread";
490                         }
491
492                         tmp += printFeedEntry(id, title, "feed", unread, icon);
493
494                         rs.next();
495                 }
496
497                 rs.close();
498
499                 tmp += "</ul>";
500
501                 render_feedlist(tmp);
502         } catch (e) {
503                 exception_error("render_offline_feedlist", e);
504         }
505 }
506
507 function init_offline() {
508         try {
509                 offline_mode = true;
510
511                 Element.hide("dispSwitchPrompt");
512                 Element.hide("feedBrowserPrompt");
513
514                 Element.hide("topLinksOnline");
515                 Element.show("topLinksOffline");
516
517                 var tb_form = document.getElementById("main_toolbar_form");
518                 Element.hide(tb_form.update);
519
520                 var chooser = document.getElementById("quickMenuChooser");
521                 chooser.disabled = true;
522
523                 var rs = db.execute("SELECT key, value FROM init_params");
524
525                 while (rs.isValidRow()) {
526                         init_params[rs.field(0)] = rs.field(1);
527                         rs.next();
528                 }
529
530                 rs.close();
531
532                 var rs = db.execute("SELECT COUNT(*) FROM feeds");
533
534                 var num_feeds = 0;
535
536                 if (rs.isValidRow()) {
537                         num_feeds = rs.field(0);                        
538                 }
539                 
540                 rs.close();
541
542                 if (num_feeds == 0) {
543                         remove_splash();
544                         return fatalError(0, 
545                                 __("Data for offline browsing has not been downloaded yet."));
546                 }
547
548                 render_offline_feedlist();
549                 init_second_stage();
550                 window.setTimeout("viewfeed(-4)", 50);
551
552         } catch (e) {
553                 exception_error("init_offline", e);
554         }
555 }
556
557 function offline_download_parse(stage, transport) {
558         try {
559                 if (transport.responseXML) {
560
561                         if (stage == 0) {
562
563                                 var feeds = transport.responseXML.getElementsByTagName("feed");
564
565                                 if (feeds.length > 0) {
566                                         db.execute("DELETE FROM feeds");
567                                 }
568
569                                 for (var i = 0; i < feeds.length; i++) {
570                                         var id = feeds[i].getAttribute("id");
571                                         var has_icon = feeds[i].getAttribute("has_icon");
572                                         var title = feeds[i].firstChild.nodeValue;
573                                         var cat_id = feeds[i].getAttribute("cat_id");
574
575                                         db.execute("INSERT INTO feeds (id,title,has_icon,cat_id)"+
576                                                 "VALUES (?,?,?,?)",
577                                                 [id, title, has_icon, cat_id]);
578                                 }
579
580                                 var cats = transport.responseXML.getElementsByTagName("category");
581
582                                 if (feeds.length > 0) {
583                                         db.execute("DELETE FROM categories");
584                                 }
585
586                                 for (var i = 0; i < cats.length; i++) {
587                                         var id = cats[i].getAttribute("id");
588                                         var collapsed = cats[i].getAttribute("collapsed");
589                                         var title = cats[i].firstChild.nodeValue;
590
591                                         db.execute("INSERT INTO categories (id,title,collapsed)"+
592                                                 "VALUES (?,?,?)",
593                                                 [id, title, collapsed]);
594                                 }
595
596                                 window.setTimeout("update_offline_data("+(stage+1)+")", 10*1000);
597                         } else {
598
599                                 var articles = transport.responseXML.getElementsByTagName("article");
600
601                                 var limit = transport.responseXML.getElementsByTagName("limit")[0];
602
603                                 if (limit) {
604                                         limit = limit.getAttribute("value");
605                                 } else {
606                                         limit = 0;
607                                 }
608
609                                 var articles_found = 0;
610
611                                 for (var i = 0; i < articles.length; i++) {                                     
612                                         var a = eval("("+articles[i].firstChild.nodeValue+")");
613                                         articles_found++;
614                                         if (a) {
615
616                                                 var date = new Date();
617                                                 var ts = Math.round(date.getTime() / 1000);
618
619                                                 db.execute("DELETE FROM articles WHERE id = ?", [a.id]);
620                                                 db.execute("INSERT INTO articles "+
621                                                 "(id, feed_id, title, link, guid, updated, content, "+
622                                                         "unread, marked, tags, added, comments) "+
623                                                 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", 
624                                                         [a.id, a.feed_id, a.title, a.link, a.guid, a.updated, 
625                                                                 a.content, a.unread, a.marked, a.tags, ts,
626                                                                 a.comments]);
627
628                                         }
629                                 }
630
631                                 debug("downloaded articles: " + articles_found + " limit: " + limit);
632
633                                 if (articles_found >= limit) {
634                                         window.setTimeout("update_offline_data("+(stage+1)+")", 10*1000);
635                                         debug("update_offline_data: done " + stage);
636                                 } else {
637                                         window.setTimeout("update_offline_data(0)", 1800*1000);
638                                         debug("update_offline_data: finished");
639
640                                         var date = new Date();
641                                         var ts = Math.round(date.getTime() / 1000);
642
643                                         db.execute("DELETE FROM articles WHERE added < ? - 2592000", [ts]);
644
645                                 }
646                         }
647
648 //                      notify('');
649
650                 }
651         } catch (e) {
652                 exception_error("offline_download_parse", e);
653         }
654 }
655
656 function update_offline_data(stage) {
657         try {
658
659                 if (!stage) stage = 0;
660                 if (offline_mode) return;
661
662                 debug("update_offline_data: stage " + stage);
663
664 //              notify_progress("Updating offline data... (" + stage +")", true);
665
666                 var query = "backend.php?op=rpc&subop=download&stage=" + stage;
667
668                 var rs = db.execute("SELECT MAX(id), MIN(id) FROM articles");
669
670                 if (rs.isValidRow() && rs.field(0)) {
671                         var offline_dl_max_id = rs.field(0);
672                         var offline_dl_min_id = rs.field(1);
673
674                         query = query + "&cidt=" + offline_dl_max_id;
675                         query = query + "&cidb=" + offline_dl_min_id;
676                 }
677
678                 rs.close();
679
680                 new Ajax.Request(query, {
681                         onComplete: function(transport) { 
682                                 offline_download_parse(stage, transport);                               
683                         } });
684
685         } catch (e) {
686                 exception_error("initiate_offline_download", e);
687         }
688 }
689
690 function set_feedlist_counter(id, ctr, is_cat) {
691         try {
692
693                 var feedctr = document.getElementById("FEEDCTR-" + id);
694                 var feedu = document.getElementById("FEEDU-" + id);
695                 var feedr = document.getElementById("FEEDR-" + id);
696
697                 if (is_cat) {
698                         var catctr = document.getElementById("FCATCTR-" + id);
699                         if (catctr) {
700                                 catctr.innerHTML = "(" + ctr + ")";
701                                 if (ctr > 0) {
702                                         catctr.className = "catCtrHasUnread";
703                                 } else {
704                                         catctr.className = "catCtrNoUnread";
705                                 }
706                         }
707                 } else if (feedctr && feedu && feedr) {
708
709                         var row_needs_hl = (ctr > 0 && ctr > parseInt(feedu.innerHTML));
710
711                         feedu.innerHTML = ctr;
712
713                         if (ctr > 0) {                                  
714                                 feedctr.className = "feedCtrHasUnread";
715                                 if (!feedr.className.match("Unread")) {
716                                         var is_selected = feedr.className.match("Selected");
717         
718                                         feedr.className = feedr.className.replace("Selected", "");
719                                         feedr.className = feedr.className.replace("Unread", "");
720         
721                                         feedr.className = feedr.className + "Unread";
722         
723                                         if (is_selected) {
724                                                 feedr.className = feedr.className + "Selected";
725                                         }       
726                                         
727                                 }
728
729                                 if (row_needs_hl) { 
730                                         new Effect.Highlight(feedr, {duration: 1, startcolor: "#fff7d5",
731                                                 queue: { position:'end', scope: 'EFQ-' + id, limit: 1 } } );
732                                 }
733                         } else {
734                                 feedctr.className = "feedCtrNoUnread";
735                                 feedr.className = feedr.className.replace("Unread", "");
736                         }                       
737                 }
738
739         } catch (e) {
740                 exception_error("set_feedlist_counter", e);
741         }
742 }
743
744 function update_local_feedlist_counters() {
745         try {
746                 if (!offline_mode) return;
747
748 /*              var rs = db.execute("SELECT feeds.id,COUNT(articles.id) "+
749                         "FROM feeds LEFT JOIN articles ON (feed_id = feeds.id) "+
750                         "WHERE unread = 1 OR unread IS NULL GROUP BY feeds.id "+
751                         "ORDER BY feeds.title"); */
752
753                 var rs = db.execute("SELECT id FROM feeds "+
754                         "ORDER BY title");
755
756                 while (rs.isValidRow()) {
757                         var id = rs.field(0);
758                         var ctr = get_local_feed_unread(id);
759                         set_feedlist_counter(id, ctr, false);
760                         rs.next();
761                 }
762
763                 rs.close();
764
765                 var rs = db.execute("SELECT cat_id,SUM(unread) "+
766                         "FROM articles, feeds WHERE feeds.id = feed_id GROUP BY cat_id");
767
768                 while (rs.isValidRow()) {
769                         var id = rs.field(0);
770                         var ctr = rs.field(1);
771                         set_feedlist_counter(id, ctr, true);
772                         rs.next();
773                 }
774
775                 rs.close();
776
777                 set_feedlist_counter(-4, get_local_feed_unread(-4));
778                 set_feedlist_counter(-1, get_local_feed_unread(-1));
779
780                 hideOrShowFeeds(getInitParam("hide_read_feeds") == 1);
781
782                 global_unread = get_local_feed_unread(-4);
783                 updateTitle();
784
785         } catch (e) {
786                 exception_error("update_local_feedlist_counters", e);
787         }
788 }
789
790 function get_local_feed_unread(id) {
791         try {
792                 var rs;
793
794                 if (id == -4) {
795                         rs = db.execute("SELECT SUM(unread) FROM articles");
796                 } else if (id == -1) {
797                         rs = db.execute("SELECT SUM(unread) FROM articles WHERE marked = 1");
798                 } else {
799                         rs = db.execute("SELECT SUM(unread) FROM articles WHERE feed_id = ?", [id]);
800                 }
801
802                 var a = false;
803
804                 if (rs.isValidRow()) {
805                         a = rs.field(0);
806                 } else {
807                         a = 0;
808                 }
809
810                 rs.close();
811
812                 return a;
813
814         } catch (e) {
815                 exception_error("get_local_feed_unread", e);
816         }
817 }
818
819 function init_gears() {
820         try {
821
822                 if (window.google && google.gears) {
823                         localServer = google.gears.factory.create("beta.localserver");
824                         store = localServer.createManagedStore("tt-rss");
825                         db = google.gears.factory.create('beta.database');
826                         db.open('tt-rss');
827
828                         db.execute("CREATE TABLE IF NOT EXISTS version (schema_version text)");
829
830                         var rs = db.execute("SELECT schema_version FROM version");
831
832                         var version = "";
833
834                         if (rs.isValidRow()) {
835                                 version = rs.field(0);
836                         }
837
838                         rs.close();
839
840                         if (version != SCHEMA_VERSION) {
841                                 db.execute("DROP TABLE IF EXISTS init_params");
842                                 db.execute("DROP TABLE IF EXISTS cache");
843                                 db.execute("DROP TABLE IF EXISTS feeds");
844                                 db.execute("DROP TABLE IF EXISTS categories");
845                                 db.execute("DROP TABLE IF EXISTS articles");
846                                 db.execute("DROP TABLE IF EXISTS version");
847                                 db.execute("CREATE TABLE IF NOT EXISTS version (schema_version text)");
848                                 db.execute("INSERT INTO version (schema_version) VALUES (?)", 
849                                         [SCHEMA_VERSION]);
850                         }
851
852                         db.execute("CREATE TABLE IF NOT EXISTS init_params (key text, value text)");
853
854                         db.execute("CREATE TABLE IF NOT EXISTS cache (id text, article text, param text, added text)");
855                         db.execute("CREATE TABLE IF NOT EXISTS feeds (id integer, title text, has_icon integer, cat_id integer)");
856                         db.execute("CREATE TABLE IF NOT EXISTS categories (id integer, title text, collapsed integer)");
857                         db.execute("CREATE TABLE IF NOT EXISTS articles (id integer, feed_id integer, title text, link text, guid text, updated text, content text, tags text, unread text, marked text, added text, comments text)");
858
859                         db.execute("DELETE FROM cache WHERE id LIKE 'F:%' OR id LIKE 'C:%'");
860
861                         Element.show("restartOfflinePic");
862
863                 }       
864         
865                 cache_expire();
866
867         } catch (e) {
868                 exception_error("init_gears", e);
869         }
870 }
871
872 function gotoOffline() {
873         window.location.href = "tt-rss.php?offline=1";
874 }
875
876 function gotoOnline() {
877         window.location.href = "tt-rss.php";
878 }
879
880 function local_collapse_cat(id) {
881         try {
882                 if (db) {
883                         db.execute("UPDATE categories SET collapsed = NOT collapsed WHERE id = ?",
884                                 [id]);
885                 }       
886         } catch (e) {
887                 exception_error("local_collapse_cat", e);
888         }
889 }
890
891 function get_local_category_title(id) {
892         try {
893                 var rs = db.execute("SELECT title FROM categories WHERE id = ?", [id]);
894                 var tmp = "";
895
896                 if (rs.isValidRow()) {
897                         tmp = rs.field(0);
898                 }
899
900                 rs.close();
901
902                 return tmp;
903
904         } catch (e) {
905                 exception_error("get_local_category_title", e);
906         }
907 }
908
909 function get_local_category_unread(id) {
910         try {
911                 var rs = db.execute("SELECT SUM(unread) FROM articles, feeds "+
912                         "WHERE feeds.id = feed_id AND cat_id = ?",
913                         [id]);
914
915                 var tmp = 0;
916
917                 if (rs.isValidRow()) {
918                         tmp = rs.field(0);
919                 }
920
921                 rs.close();
922
923                 return tmp;
924
925         } catch (e) {
926                 exception_error("get_local_category_unread", e);
927         }
928 }
929
930 function printCategoryHeader(cat_id, hidden, can_browse) {
931         try {
932                 if (hidden == undefined) hidden = false;
933                 if (can_browse == undefined) can_browse = false;
934
935                         var tmp_category = get_local_category_title(cat_id);
936                         var tmp = "";
937
938                         var cat_unread = get_local_category_unread(cat_id);
939
940                         var holder_style = "";
941                         var ellipsis = "";
942
943                         if (hidden) {
944                                 holder_style = "display:none;";
945                                 ellipsis = "…";
946                         }
947
948                         var catctr_class = (cat_unread > 0) ? "catCtrHasUnread" : "catCtrNoUnread";
949
950                         var browse_cat_link = "";
951                         var inner_title_class = "catTitleNL";
952
953                         if (can_browse) {
954                                 browse_cat_link = "onclick=\"javascript:viewCategory($cat_id)\"";
955                                 inner_title_class = "catTitle";
956                         }
957
958                         var cat_class = "feedCat";
959
960                         tmp += "<li class=\""+cat_class+"\" id=\"FCAT-"+cat_id+"\">"+
961                                 "<img onclick=\"toggleCollapseCat("+cat_id+")\" class=\"catCollapse\""+
962                                         " title=\""+__('Click to collapse category')+"\""+
963                                         " src=\"images/cat-collapse.png\"><span class=\""+inner_title_class+"\" "+
964                                         " id=\"FCATN-"+cat_id+"\" "+browse_cat_link+
965                                 "\">"+tmp_category+"</span>";
966
967                         tmp += "<span id=\"FCAP-"+cat_id+"\">";
968
969                         tmp += " <span id=\"FCATCTR-"+cat_id+"\" "+
970                                 "class=\""+catctr_class+"\">("+cat_unread+")</span> "+ellipsis;
971
972                         tmp += "</span>";
973
974                         tmp += "<ul class=\"feedCatList\" id=\"FCATLIST-"+cat_id+"\" "+
975                                 "style='"+holder_style+"'>";
976
977                         return tmp;
978         } catch (e) {
979                 exception_error("printCategoryHeader", e);
980         }
981 }
982