]> git.wh0rd.org Git - tt-rss.git/blob - functions.js
do not die if DEFAULT_UPDATE_INTERVAL is not found
[tt-rss.git] / functions.js
1 var hotkeys_enabled = true;
2
3 var xmlhttp_rpc = Ajax.getTransport();
4
5 function browser_has_opacity() {
6         return navigator.userAgent.match("Gecko") != null || 
7                 navigator.userAgent.match("Opera") != null;
8 }
9
10 function is_opera() {
11         return navigator.userAgent.match("Opera");
12 }
13
14 function exception_error(location, e, silent) {
15         var msg;
16
17         if (e.fileName) {
18                 var base_fname = e.fileName.substring(e.fileName.lastIndexOf("/") + 1);
19         
20                 msg = "Exception: " + e.name + ", " + e.message + 
21                         "\nFunction: " + location + "()" +
22                         "\nLocation: " + base_fname + ":" + e.lineNumber;
23                 
24         } else {
25                 msg = "Exception: " + e + "\nFunction: " + location + "()";
26         }
27
28         debug("<b>EXCEPTION: " + msg + "</b>");
29
30         if (!silent) {
31                 alert(msg);
32         }
33 }
34
35 function disableHotkeys() {
36         hotkeys_enabled = false;
37 }
38
39 function enableHotkeys() {
40         hotkeys_enabled = true;
41 }
42
43 function xmlhttp_ready(obj) {
44         return obj.readyState == 4 || obj.readyState == 0 || !obj.readyState;
45 }
46
47 function notify_callback() {
48         var container = document.getElementById('notify');
49         if (xmlhttp.readyState == 4) {
50                 container.innerHTML=xmlhttp.responseText;
51         }
52 }
53
54 function rpc_notify_callback() {
55         var container = document.getElementById('notify');
56         if (xmlhttp_rpc.readyState == 4) {
57                 container.innerHTML=xmlhttp_rpc.responseText;
58         }
59 }
60
61 function param_escape(arg) {
62         if (typeof encodeURIComponent != 'undefined')
63                 return encodeURIComponent(arg); 
64         else
65                 return escape(arg);
66 }
67
68 function param_unescape(arg) {
69         if (typeof decodeURIComponent != 'undefined')
70                 return decodeURIComponent(arg); 
71         else
72                 return unescape(arg);
73 }
74
75 function delay(gap) {
76         var then,now; 
77         then=new Date().getTime();
78         now=then;
79         while((now-then)<gap) {
80                 now=new Date().getTime();
81         }
82 }
83
84 var notify_hide_timerid = false;
85 var notify_last_doc = false;
86
87 var notify_effect = false; 
88
89 function hide_notify() {
90         if (notify_last_doc) {
91                 var n = notify_last_doc.getElementById("notify");               
92                 n.style.display = "none";
93
94 /*              if (browser_has_opacity()) {
95                         if (notify_opacity >= 0) {
96                                 notify_opacity = notify_opacity - 0.1;
97                                 n.style.opacity = notify_opacity;
98                                 notify_hide_timerid = window.setTimeout("hide_notify()", 20);   
99                         } else {
100                                 n.style.display = "none";
101                                 n.style.opacity = 1;
102                         }
103                 } else {
104                         n.style.display = "none";
105                 } */
106         }
107
108
109 function notify_real(msg, doc, no_hide, is_err) {
110
111         var n = doc.getElementById("notify");
112         var nb = doc.getElementById("notify_body");
113
114         if (!n || !nb) return;
115
116         if (notify_hide_timerid) {
117                 window.clearTimeout(notify_hide_timerid);
118         }
119
120         notify_last_doc = doc;
121         notify_opacity = 1;
122
123         if (msg == "") {
124                 if (n.style.display == "block") {
125                         notify_hide_timerid = window.setTimeout("hide_notify()", 0);
126                 }
127                 return;
128         } else {
129                 n.style.display = "block";
130         }
131
132         if (is_err) {
133                 n.style.backgroundColor = "#ffcccc";
134                 n.style.color = "black";
135                 n.style.borderColor = "#ff0000";
136         } else {
137                 n.style.backgroundColor = "#fff7d5";
138                 n.style.borderColor = "#d7c47a";
139                 n.style.color = "black";
140         }
141
142 //      msg = "<img src='images/live_com_loading.gif'> " + msg;
143
144         nb.innerHTML = msg;
145
146         if (!no_hide) {
147                 notify_hide_timerid = window.setTimeout("hide_notify()", 3000);
148         }
149 }
150
151 function p_notify(msg, no_hide, is_err) {
152         notify_real(msg, document, no_hide, is_err);
153 }
154
155 function notify(msg, no_hide, is_err) {
156         notify_real(msg, document, no_hide, is_err);
157 }
158
159 function printLockingError() {
160         notify("Please wait until operation finishes");}
161
162 function hotkey_handler(e) {
163
164         try {
165
166                 var keycode;
167         
168                 if (!hotkeys_enabled) return;
169         
170                 if (window.event) {
171                         keycode = window.event.keyCode;
172                 } else if (e) {
173                         keycode = e.which;
174                 }
175
176                 if (keycode == 82) { // r
177                         return scheduleFeedUpdate(true);
178                 }
179
180                 if (keycode == 83) { // r
181                         return displayDlg("search", getActiveFeedId());
182                 }
183
184                 if (keycode == 85) { // u
185                         if (getActiveFeedId()) {
186                                 return viewfeed(getActiveFeedId(), "ForceUpdate");
187                         }
188                 }
189         
190                 if (keycode == 65) { // a
191                         return toggleDispRead();
192                 }
193         
194                 var feedlist = document.getElementById('feedList');
195         
196                 if (keycode == 74) { // j
197                         var feed = getActiveFeedId();
198                         var new_feed = getRelativeFeedId(feedlist, feed, 'prev');
199                         if (new_feed) viewfeed(new_feed, '');
200                 }
201         
202                 if (keycode == 75) { // k
203                         var feed = getActiveFeedId();
204                         var new_feed = getRelativeFeedId(feedlist, feed, 'next');
205                         if (new_feed) viewfeed(new_feed, '');
206                 }
207
208                 if (keycode == 78 || keycode == 40) { // n, down
209                         if (typeof moveToPost != 'undefined') {
210                                 return moveToPost('next');
211                         }
212                 }
213         
214                 if (keycode == 80 || keycode == 38) { // p, up
215                         if (typeof moveToPost != 'undefined') {
216                                 return moveToPost('prev');
217                         }
218                 }
219                 
220                 if (typeof localHotkeyHandler != 'undefined') {
221                         try {
222                                 localHotkeyHandler(keycode);
223                         } catch (e) {
224                                 exception_error("hotkey_handler, local:", e);
225                         }
226                 }
227         } catch (e) {
228                 exception_error("hotkey_handler", e);
229         }
230 }
231
232 function cleanSelectedList(element) {
233         var content = document.getElementById(element);
234
235         if (!document.getElementById("feedCatHolder")) {
236                 for (i = 0; i < content.childNodes.length; i++) {
237                         var child = content.childNodes[i];
238                         try {
239                                 child.className = child.className.replace("Selected", "");
240                         } catch (e) {
241                                 //
242                         }
243                 }
244         } else {
245                 for (i = 0; i < content.childNodes.length; i++) {
246                         var child = content.childNodes[i];
247                         if (child.id == "feedCatHolder") {
248                                 debug(child.id);
249                                 var fcat = child.lastChild;
250                                 for (j = 0; j < fcat.childNodes.length; j++) {
251                                         var feed = fcat.childNodes[j];
252                                         feed.className = feed.className.replace("Selected", "");
253                                 }               
254                         }
255                 } 
256         }
257 }
258
259
260 function cleanSelected(element) {
261         var content = document.getElementById(element);
262
263         for (i = 0; i < content.rows.length; i++) {
264                 content.rows[i].className = content.rows[i].className.replace("Selected", "");
265         }
266 }
267
268 function getVisibleUnreadHeadlines() {
269         var content = document.getElementById("headlinesList");
270
271         var rows = new Array();
272
273         for (i = 0; i < content.rows.length; i++) {
274                 var row_id = content.rows[i].id.replace("RROW-", "");
275                 if (row_id.length > 0 && content.rows[i].className.match("Unread")) {
276                                 rows.push(row_id);      
277                 }
278         }
279         return rows;
280 }
281
282 function getVisibleHeadlineIds() {
283
284         var content = document.getElementById("headlinesList");
285
286         var rows = new Array();
287
288         for (i = 0; i < content.rows.length; i++) {
289                 var row_id = content.rows[i].id.replace("RROW-", "");
290                 if (row_id.length > 0) {
291                                 rows.push(row_id);      
292                 }
293         }
294         return rows;
295 }
296
297 function getFirstVisibleHeadlineId() {
298         var rows = getVisibleHeadlineIds();
299         return rows[0];
300 }
301
302 function getLastVisibleHeadlineId() {
303         var rows = getVisibleHeadlineIds();
304         return rows[rows.length-1];
305 }
306
307 function markHeadline(id) {
308         var row = document.getElementById("RROW-" + id);
309         if (row) {
310                 var is_active = false;
311         
312                 if (row.className.match("Active")) {
313                         is_active = true;
314                 }
315                 row.className = row.className.replace("Selected", "");
316                 row.className = row.className.replace("Active", "");
317                 row.className = row.className.replace("Insensitive", "");
318                 
319                 if (is_active) {
320                         row.className = row.className = "Active";
321                 }
322                 
323                 var check = document.getElementById("RCHK-" + id);
324
325                 if (check) {
326                         check.checked = true;
327                 }
328
329                 row.className = row.className + "Selected"; 
330                 
331         }
332 }
333
334 function getFeedIds() {
335         var content = document.getElementById("feedsList");
336
337         var rows = new Array();
338
339         for (i = 0; i < content.rows.length; i++) {
340                 var id = content.rows[i].id.replace("FEEDR-", "");
341                 if (id.length > 0) {
342                         rows.push(id);
343                 }
344         }
345
346         return rows;
347 }
348
349 function setCookie(name, value, lifetime, path, domain, secure) {
350         
351         var d = false;
352         
353         if (lifetime) {
354                 d = new Date();
355                 d.setTime(lifetime * 1000);
356         }
357         
358         int_setCookie(name, value, d, path, domain, secure);
359
360 }
361
362 function int_setCookie(name, value, expires, path, domain, secure) {
363         document.cookie= name + "=" + escape(value) +
364                 ((expires) ? "; expires=" + expires.toGMTString() : "") +
365                 ((path) ? "; path=" + path : "") +
366                 ((domain) ? "; domain=" + domain : "") +
367                 ((secure) ? "; secure" : "");
368 }
369
370 function delCookie(name, path, domain) {
371         if (getCookie(name)) {
372                 document.cookie = name + "=" +
373                 ((path) ? ";path=" + path : "") +
374                 ((domain) ? ";domain=" + domain : "" ) +
375                 ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
376         }
377 }
378                 
379
380 function getCookie(name) {
381
382         var dc = document.cookie;
383         var prefix = name + "=";
384         var begin = dc.indexOf("; " + prefix);
385         if (begin == -1) {
386             begin = dc.indexOf(prefix);
387             if (begin != 0) return null;
388         }
389         else {
390             begin += 2;
391         }
392         var end = document.cookie.indexOf(";", begin);
393         if (end == -1) {
394             end = dc.length;
395         }
396         return unescape(dc.substring(begin + prefix.length, end));
397 }
398
399 function disableContainerChildren(id, disable, doc) {
400
401         if (!doc) doc = document;
402
403         var container = doc.getElementById(id);
404
405         if (!container) {
406                 //alert("disableContainerChildren: element " + id + " not found");
407                 return;
408         }
409
410         for (var i = 0; i < container.childNodes.length; i++) {
411                 var child = container.childNodes[i];
412
413                 try {
414                         child.disabled = disable;
415                 } catch (E) {
416
417                 }
418
419                 if (disable) {
420                         if (child.className && child.className.match("button")) {
421                                 child.className = "disabledButton";
422                         }
423                 } else {
424                         if (child.className && child.className.match("disabledButton")) {
425                                 child.className = "button";
426                         }
427                 } 
428         }
429
430 }
431
432 function gotoPreferences() {
433         document.location.href = "prefs.php";
434 }
435
436 function gotoMain() {
437         document.location.href = "tt-rss.php";
438 }
439
440 function gotoExportOpml() {
441         document.location.href = "opml.php?op=Export";
442 }
443
444 function getActiveFeedId() {
445 //      return getCookie("ttrss_vf_actfeed");
446         try {
447                 debug("gAFID: " + active_feed_id);
448                 return active_feed_id;
449         } catch (e) {
450                 exception_error("getActiveFeedId", e);
451         }
452 }
453
454 function activeFeedIsCat() {
455         return active_feed_is_cat;
456 }
457
458 function setActiveFeedId(id) {
459 //      return setCookie("ttrss_vf_actfeed", id);
460         try {
461                 debug("sAFID(" + id + ")");
462                 active_feed_id = id;
463         } catch (e) {
464                 exception_error("setActiveFeedId", e);
465         }
466 }
467
468 function parse_counters(reply, scheduled_call) {
469         try {
470
471                 var feeds_found = 0;
472
473                 if (reply.firstChild && reply.firstChild.firstChild) {
474                         debug("<b>wrong element passed to parse_counters, adjusting.</b>");
475                         reply = reply.firstChild;
476                 }
477
478                 for (var l = 0; l < reply.childNodes.length; l++) {
479                         if (!reply.childNodes[l] ||
480                                 typeof(reply.childNodes[l].getAttribute) == "undefined") {
481                                 // where did this come from?
482                                 continue;
483                         }
484
485                         var id = reply.childNodes[l].getAttribute("id");
486                         var t = reply.childNodes[l].getAttribute("type");
487                         var ctr = reply.childNodes[l].getAttribute("counter");
488                         var error = reply.childNodes[l].getAttribute("error");
489                         var has_img = reply.childNodes[l].getAttribute("hi");
490                         var updated = reply.childNodes[l].getAttribute("updated");
491         
492                         if (id == "global-unread") {
493                                 global_unread = ctr;
494                                 updateTitle();
495                                 continue;
496                         }
497
498                         if (id == "subscribed-feeds") {
499                                 feeds_found = ctr;
500                                 continue;
501                         }
502         
503                         if (t == "category") {
504                                 var catctr = document.getElementById("FCATCTR-" + id);
505                                 if (catctr) {
506                                         catctr.innerHTML = "(" + ctr + " unread)";
507                                 }
508                                 continue;
509                         }
510                 
511                         var feedctr = document.getElementById("FEEDCTR-" + id);
512                         var feedu = document.getElementById("FEEDU-" + id);
513                         var feedr = document.getElementById("FEEDR-" + id);
514                         var feed_img = document.getElementById("FIMG-" + id);
515                         var feedlink = document.getElementById("FEEDL-" + id);
516                         var feedupd = document.getElementById("FLUPD-" + id);
517
518                         if (updated && feedlink) {
519                                 if (error) {
520                                         feedlink.title = "Error: " + error + " (" + updated + ")";
521                                 } else {
522                                         feedlink.title = "Updated: " + updated;
523                                 }
524                         }
525
526                         if (updated && feedupd) {
527                                 if (error) {
528                                         feedupd.innerHTML = updated + " (Error)";
529                                 } else {
530                                         feedupd.innerHTML = updated;
531                                 }
532                         }
533
534                         if (feedctr && feedu && feedr) {
535
536                                 if (feedu.innerHTML != ctr && id == getActiveFeedId() && scheduled_call) {
537                                         viewCurrentFeed();
538                                 }
539                 
540                                 feedu.innerHTML = ctr;
541
542                                 if (error) {
543                                         feedr.className = feedr.className.replace("feed", "error");
544                                 } else if (id > 0) {
545                                         feedr.className = feedr.className.replace("error", "feed");
546                                 }
547         
548                                 if (ctr > 0) {                                  
549                                         feedctr.className = "odd";
550                                         if (!feedr.className.match("Unread")) {
551                                                 var is_selected = feedr.className.match("Selected");
552                 
553                                                 feedr.className = feedr.className.replace("Selected", "");
554                                                 feedr.className = feedr.className.replace("Unread", "");
555                 
556                                                 feedr.className = feedr.className + "Unread";
557                 
558                                                 if (is_selected) {
559                                                         feedr.className = feedr.className + "Selected";
560                                                 }
561                 
562                                         }
563                                 } else {
564                                         feedctr.className = "invisible";
565                                         feedr.className = feedr.className.replace("Unread", "");
566                                 }                       
567                         }
568                 }
569
570                 hideOrShowFeeds(document, getInitParam("hide_read_feeds") == 1);
571
572                 var feeds_stored = number_of_feeds;
573
574                 debug("Feed counters, C: " + feeds_found + ", S:" + feeds_stored);
575
576                 if (feeds_stored != feeds_found) {
577                         number_of_feeds = feeds_found;
578
579                         if (feeds_stored != 0) {
580                                 debug("Subscribed feed number changed, refreshing feedlist");
581                                 setTimeout('updateFeedList(false, false)', 50);
582                         }
583                 }
584
585         } catch (e) {
586                 exception_error("parse_counters", e);
587         }
588 }
589
590 function all_counters_callback() {
591         if (xmlhttp_rpc.readyState == 4) {
592                 try {
593                         if (!xmlhttp_rpc.responseXML || !xmlhttp_rpc.responseXML.firstChild) {
594                                 debug("[all_counters_callback] backend did not return valid XML");
595                                 return;
596                         }
597
598                         debug("in all_counters_callback : " + xmlhttp_rpc.responseXML);
599
600                         var reply = xmlhttp_rpc.responseXML.firstChild;
601
602                         var counters = reply.firstChild;
603
604                         parse_counters(counters);
605
606                         var runtime = counters.nextSibling;
607
608                         if (runtime) {
609                                 parse_runtime_info(runtime);
610                         }
611
612                         if (getInitParam("feeds_sort_by_unread") == 1) {
613                                 resort_feedlist();              
614                         }       
615
616                         hideOrShowFeeds(document, getInitParam("hide_read_feeds") == 1);
617
618                 } catch (e) {
619                         exception_error("all_counters_callback", e);
620                 }
621         }
622 }
623
624 function get_feed_entry_unread(doc, elem) {
625
626         var id = elem.id.replace("FEEDR-", "");
627
628         if (id <= 0) {
629                 return -1;
630         }
631
632         try {
633                 return parseInt(doc.getElementById("FEEDU-" + id).innerHTML);   
634         } catch (e) {
635                 return -1;
636         }
637 }
638
639 function resort_category(doc, node) {
640         debug("resort_category: " + node);
641
642         if (node.hasChildNodes() && node.firstChild.nextSibling != false) {  
643                 for (i = 0; i < node.childNodes.length; i++) {
644                         if (node.childNodes[i].nodeName != "LI") { continue; }
645
646                         if (get_feed_entry_unread(doc, node.childNodes[i]) < 0) {
647                                 continue;
648                         }
649
650                         for (j = i+1; j < node.childNodes.length; j++) {                        
651                                 if (node.childNodes[j].nodeName != "LI") { continue; }  
652
653                                 var tmp_val = get_feed_entry_unread(doc, node.childNodes[i]);
654                                 var cur_val = get_feed_entry_unread(doc, node.childNodes[j]);
655
656                                 if (cur_val > tmp_val) {
657                                         tempnode_i = node.childNodes[i].cloneNode(true);
658                                         tempnode_j = node.childNodes[j].cloneNode(true);
659                                         node.replaceChild(tempnode_i, node.childNodes[j]);
660                                         node.replaceChild(tempnode_j, node.childNodes[i]);
661                                 }
662                         }
663
664                 }
665         }
666
667 }
668
669 function resort_feedlist() {
670         debug("resort_feedlist");
671
672         var fd = document;
673
674         if (fd.getElementById("feedCatHolder")) {
675
676                 var feeds = fd.getElementById("feedList");
677                 var child = feeds.firstChild;
678
679                 while (child) {
680
681                         if (child.id == "feedCatHolder") {
682                                 resort_category(fd, child.firstChild);
683                         }
684         
685                         child = child.nextSibling;
686                 }
687
688         } else {
689                 resort_category(fd, fd.getElementById("feedList"));
690         }
691 }
692
693 function update_all_counters(feed) {
694         if (xmlhttp_ready(xmlhttp_rpc)) {
695                 var query = "backend.php?op=rpc&subop=getAllCounters";
696
697                 if (feed > 0) {
698                         query = query + "&aid=" + feed;
699                 }
700
701                 xmlhttp_rpc.open("GET", query, true);
702                 xmlhttp_rpc.onreadystatechange=all_counters_callback;
703                 xmlhttp_rpc.send(null);
704         }
705 }
706
707 function popupHelp(tid) {
708         var w = window.open("backend.php?op=help&tid=" + tid,
709                 "Popup Help", 
710                 "menubar=no,location=no,resizable=yes,scrollbars=yes,status=no");
711 }
712
713 /** * @(#)isNumeric.js * * Copyright (c) 2000 by Sundar Dorai-Raj
714   * * @author Sundar Dorai-Raj
715   * * Email: sdoraira@vt.edu
716   * * This program is free software; you can redistribute it and/or
717   * * modify it under the terms of the GNU General Public License 
718   * * as published by the Free Software Foundation; either version 2 
719   * * of the License, or (at your option) any later version, 
720   * * provided that any use properly credits the author. 
721   * * This program is distributed in the hope that it will be useful,
722   * * but WITHOUT ANY WARRANTY; without even the implied warranty of
723   * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
724   * * GNU General Public License for more details at http://www.gnu.org * * */
725
726   var numbers=".0123456789";
727   function isNumeric(x) {
728     // is x a String or a character?
729     if(x.length>1) {
730       // remove negative sign
731       x=Math.abs(x)+"";
732       for(j=0;j<x.length;j++) {
733         // call isNumeric recursively for each character
734         number=isNumeric(x.substring(j,j+1));
735         if(!number) return number;
736       }
737       return number;
738     }
739     else {
740       // if x is number return true
741       if(numbers.indexOf(x)>=0) return true;
742       return false;
743     }
744   }
745
746
747 function hideOrShowFeeds(doc, hide) {
748
749         debug("hideOrShowFeeds: " + doc + ", " + hide);
750
751         var fd = document;
752
753         var list = fd.getElementById("feedList");
754
755         if (fd.getElementById("feedCatHolder")) {
756
757                 var feeds = fd.getElementById("feedList");
758                 var child = feeds.firstChild;
759
760                 while (child) {
761
762                         if (child.id == "feedCatHolder") {
763                                 hideOrShowFeedsCategory(fd, child.firstChild, hide, child.previousSibling);
764                         }
765         
766                         child = child.nextSibling;
767                 }
768
769         } else {
770                 hideOrShowFeedsCategory(fd, fd.getElementById("feedList"), hide);
771         }
772 }
773
774 function hideOrShowFeedsCategory(doc, node, hide, cat_node) {
775
776 //      debug("hideOrShowFeedsCategory: " + node + " (" + hide + ")");
777
778         var cat_unread = 0;
779
780         if (node.hasChildNodes() && node.firstChild.nextSibling != false) {  
781                 for (i = 0; i < node.childNodes.length; i++) {
782                         if (node.childNodes[i].nodeName != "LI") { continue; }
783
784                         if (node.childNodes[i].style != undefined) {
785
786                                 var has_unread = (node.childNodes[i].className != "feed");
787         
788         //                      debug(node.childNodes[i].id + " --> " + has_unread);
789         
790                                 if (hide && !has_unread) {
791                                         node.childNodes[i].style.display = "none";
792                                 }
793         
794                                 if (!hide) {
795                                         node.childNodes[i].style.display = "list-item";
796                                 }
797         
798                                 if (has_unread) {
799                                         node.childNodes[i].style.display = "list-item";
800                                         cat_unread++;
801                                 }
802                         }
803                 }
804         }       
805
806         if (cat_unread == 0) {
807                 if (cat_node.style == undefined) {
808                         debug("ERROR: supplied cat_node " + cat_node + 
809                                 " has no styles. WTF?");
810                         return;
811                 }
812                 if (hide) {
813                         cat_node.style.display = "none";
814                 } else {
815                         cat_node.style.display = "list-item";
816                 }
817         } else {
818                 try {
819                         cat_node.style.display = "list-item";
820                 } catch (e) {
821                         debug(e);
822                 }
823         }
824
825 //      debug("unread for category: " + cat_unread);
826 }
827
828 function selectTableRow(r, do_select) {
829         r.className = r.className.replace("Selected", "");
830         
831         if (do_select) {
832                 r.className = r.className + "Selected";
833         }
834 }
835
836 function selectTableRowById(elem_id, check_id, do_select) {
837
838         try {
839
840                 var row = document.getElementById(elem_id);
841
842                 if (row) {
843                         selectTableRow(row, do_select);
844                 }               
845
846                 var check = document.getElementById(check_id);
847
848                 if (check) {
849                         check.checked = do_select;
850                 }
851         } catch (e) {
852                 exception_error("selectTableRowById", e);
853         }
854 }
855
856 function selectTableRowsByIdPrefix(content_id, prefix, check_prefix, do_select, 
857         classcheck, reset_others) {
858
859         var content = document.getElementById(content_id);
860
861         if (!content) {
862                 alert("[selectTableRows] Element " + content_id + " not found.");
863                 return;
864         }
865
866         for (i = 0; i < content.rows.length; i++) {
867                 if (!classcheck || content.rows[i].className.match(classcheck)) {
868         
869                         if (content.rows[i].id.match(prefix)) {
870                                 selectTableRow(content.rows[i], do_select);
871                         
872                                 var row_id = content.rows[i].id.replace(prefix, "");
873                                 var check = document.getElementById(check_prefix + row_id);
874
875                                 if (check) {
876                                         check.checked = do_select;
877                                 }
878                         } else if (reset_others) {
879                                 selectTableRow(content.rows[i], false);
880
881                                 var row_id = content.rows[i].id.replace(prefix, "");
882                                 var check = document.getElementById(check_prefix + row_id);
883
884                                 if (check) {
885                                         check.checked = false;
886                                 }
887
888                         }
889                 } else if (reset_others) {
890                         selectTableRow(content.rows[i], false);
891
892                         var row_id = content.rows[i].id.replace(prefix, "");
893                         var check = document.getElementById(check_prefix + row_id);
894
895                         if (check) {
896                                 check.checked = false;
897                         }
898
899                 }
900         }
901 }
902
903 function getSelectedTableRowIds(content_id, prefix) {
904
905         var content = document.getElementById(content_id);
906
907         if (!content) {
908                 alert("[getSelectedTableRowIds] Element " + content_id + " not found.");
909                 return;
910         }
911
912         var sel_rows = new Array();
913
914         for (i = 0; i < content.rows.length; i++) {
915                 if (content.rows[i].id.match(prefix) && 
916                                 content.rows[i].className.match("Selected")) {
917                                 
918                         var row_id = content.rows[i].id.replace(prefix + "-", "");
919                         sel_rows.push(row_id);  
920                 }
921         }
922
923         return sel_rows;
924
925 }
926
927 function toggleSelectRowById(sender, id) {
928         var row = document.getElementById(id);
929
930         if (sender.checked) {
931                 if (!row.className.match("Selected")) {
932                         row.className = row.className + "Selected";
933                 }
934         } else {
935                 if (row.className.match("Selected")) {
936                         row.className = row.className.replace("Selected", "");
937                 }
938         }
939 }
940
941 function toggleSelectListRow(sender) {
942         var parent_row = sender.parentNode;
943
944         if (sender.checked) {
945                 if (!parent_row.className.match("Selected")) {
946                         parent_row.className = parent_row.className + "Selected";
947                 }
948         } else {
949                 if (parent_row.className.match("Selected")) {
950                         parent_row.className = parent_row.className.replace("Selected", "");
951                 }
952         }
953 }
954
955
956 function toggleSelectRow(sender) {
957         var parent_row = sender.parentNode.parentNode;
958
959         if (sender.checked) {
960                 if (!parent_row.className.match("Selected")) {
961                         parent_row.className = parent_row.className + "Selected";
962                 }
963         } else {
964                 if (parent_row.className.match("Selected")) {
965                         parent_row.className = parent_row.className.replace("Selected", "");
966                 }
967         }
968 }
969
970 function openExternalUrl(url) {
971         var w = window.open(url);
972 }
973
974 function getRelativeFeedId(list, id, direction, unread_only) {  
975         if (!id) {
976                 if (direction == "next") {
977                         for (i = 0; i < list.childNodes.length; i++) {
978                                 var child = list.childNodes[i];
979                                 if (child.id && child.id == "feedCatHolder") {
980                                         if (child.lastChild) {
981                                                 var cr = getRelativeFeedId(child.firstChild, id, direction, unread_only);
982                                                 if (cr) return cr;                                      
983                                         }
984                                 } else if (child.id && child.id.match("FEEDR-")) {
985                                         return child.id.replace('FEEDR-', '');
986                                 }                               
987                         }
988                 }
989
990                 // FIXME select last feed doesn't work when only unread feeds are visible
991
992                 if (direction == "prev") {
993                         for (i = list.childNodes.length-1; i >= 0; i--) {
994                                 var child = list.childNodes[i];                         
995                                 if (child.id == "feedCatHolder") {
996                                         if (child.firstChild) {
997                                                 var cr = getRelativeFeedId(child.firstChild, id, direction);
998                                                 if (cr) return cr;                                      
999                                         }
1000                                 } else if (child.id.match("FEEDR-")) {
1001                                 
1002                                         if (getInitParam("hide_read_feeds") == 1) {
1003                                                 if (child.className != "feed") {
1004                                                         alert(child.className);
1005                                                         return child.id.replace('FEEDR-', '');                                          
1006                                                 }                                                       
1007                                         } else {
1008                                                         return child.id.replace('FEEDR-', '');                                          
1009                                         }                               
1010                                 }                               
1011                         }
1012                 }
1013         } else {
1014         
1015                 var feed = list.ownerDocument.getElementById("FEEDR-" + id);
1016                 
1017                 if (getInitParam("hide_read_feeds") == 1) {
1018                         unread_only = true;
1019                 }
1020
1021                 if (direction == "next") {
1022
1023                         var e = feed;
1024
1025                         while (e) {
1026
1027                                 if (e.nextSibling) {
1028                                 
1029                                         e = e.nextSibling;
1030                                         
1031                                 } else if (e.parentNode.parentNode.nextSibling) {
1032
1033                                         var this_cat = e.parentNode.parentNode;
1034
1035                                         e = false;
1036
1037                                         if (this_cat && this_cat.nextSibling) {
1038                                                 while (!e && this_cat.nextSibling) {
1039                                                         this_cat = this_cat.nextSibling;
1040                                                         if (this_cat.id == "feedCatHolder") {
1041                                                                 e = this_cat.firstChild.firstChild;
1042                                                         }
1043                                                 }
1044                                         }
1045
1046                                 } else {
1047                                         e = false;
1048                            }
1049
1050                                 if (e) {
1051                                         if (!unread_only || (unread_only && e.className != "feed" &&
1052                                                         e.className != "label" && e.className != "virt" &&
1053                                                         e.className != "error"))        {
1054                                                 return e.id.replace("FEEDR-", "");
1055                                         }
1056                                 }
1057                         }
1058                         
1059                 } else if (direction == "prev") {
1060
1061                         var e = feed;
1062
1063                         while (e) {
1064
1065                                 if (e.previousSibling) {
1066                                 
1067                                         e = e.previousSibling;
1068                                         
1069                                 } else if (e.parentNode.parentNode.previousSibling) {
1070
1071                                         var this_cat = e.parentNode.parentNode;
1072
1073                                         e = false;
1074
1075                                         if (this_cat && this_cat.previousSibling) {
1076                                                 while (!e && this_cat.previousSibling) {
1077                                                         this_cat = this_cat.previousSibling;
1078                                                         if (this_cat.id == "feedCatHolder") {
1079                                                                 e = this_cat.firstChild.lastChild;
1080                                                         }
1081                                                 }
1082                                         }
1083
1084                                 } else {
1085                                         e = false;
1086                            }
1087
1088                                 if (e) {
1089                                         if (!unread_only || (unread_only && e.className != "feed" && 
1090                                                         e.className != "error"))        {
1091                                                 return e.id.replace("FEEDR-", "");
1092                                         }
1093                                 }
1094                         }
1095                 }
1096         }
1097 }
1098
1099 function showBlockElement(id) {
1100         var elem = document.getElementById(id);
1101
1102         if (elem) {
1103                 elem.style.display = "block";
1104         } else {
1105                 alert("[showBlockElement] can't find element with id " + id);
1106         } 
1107 }
1108
1109 function hideParentElement(e) {
1110         e.parentNode.style.display = "none";
1111 }
1112
1113 function dropboxSelect(e, v) {
1114         for (i = 0; i < e.length; i++) {
1115                 if (e[i].value == v) {
1116                         e.selectedIndex = i;
1117                         break;
1118                 }
1119         }
1120 }
1121
1122 // originally stolen from http://www.11tmr.com/11tmr.nsf/d6plinks/MWHE-695L9Z
1123 // bugfixed just a little bit :-)
1124 function getURLParam(strParamName){
1125   var strReturn = "";
1126   var strHref = window.location.href;
1127
1128   if (strHref.indexOf("#") == strHref.length-1) {
1129                 strHref = strHref.substring(0, strHref.length-1);
1130   }
1131
1132   if ( strHref.indexOf("?") > -1 ){
1133     var strQueryString = strHref.substr(strHref.indexOf("?"));
1134     var aQueryString = strQueryString.split("&");
1135     for ( var iParam = 0; iParam < aQueryString.length; iParam++ ){
1136       if (aQueryString[iParam].indexOf(strParamName + "=") > -1 ){
1137         var aParam = aQueryString[iParam].split("=");
1138         strReturn = aParam[1];
1139         break;
1140       }
1141     }
1142   }
1143   return strReturn;
1144
1145
1146 function leading_zero(p) {
1147         var s = String(p);
1148         if (s.length == 1) s = "0" + s;
1149         return s;
1150 }
1151
1152 function closeInfoBox(cleanup) {
1153         var box = document.getElementById('infoBox');
1154         var shadow = document.getElementById('infoBoxShadow');
1155
1156         if (shadow) {
1157                 shadow.style.display = "none";
1158         } else if (box) {
1159                 box.style.display = "none";
1160         }
1161
1162         if (cleanup) box.innerHTML = "&nbsp;";
1163
1164         enableHotkeys();
1165
1166         return false;
1167 }
1168
1169
1170 function displayDlg(id, param) {
1171
1172         if (!xmlhttp_ready(xmlhttp)) {
1173                 printLockingError();
1174                 return
1175         }
1176
1177         notify("");
1178
1179         xmlhttp.open("GET", "backend.php?op=dlg&id=" +
1180                 param_escape(id) + "&param=" + param_escape(param), true);
1181         xmlhttp.onreadystatechange=infobox_callback;
1182         xmlhttp.send(null);
1183
1184         disableHotkeys();
1185
1186         return false;
1187 }
1188
1189 function infobox_submit_callback() {
1190         if (xmlhttp.readyState == 4) {
1191                 closeInfoBox();
1192
1193                 // called from prefs, reload tab
1194                 if (active_tab) {
1195                         selectTab(active_tab, false);
1196                 }
1197
1198                 notify(xmlhttp.responseText);
1199
1200         } 
1201 }
1202
1203 function infobox_callback() {
1204         if (xmlhttp.readyState == 4) {
1205                 var box = document.getElementById('infoBox');
1206                 var shadow = document.getElementById('infoBoxShadow');
1207                 if (box) {                      
1208                         box.innerHTML=xmlhttp.responseText;                     
1209                         if (shadow) {
1210                                 shadow.style.display = "block";
1211                         } else {
1212                                 box.style.display = "block";                            
1213                         }
1214                 }
1215         }
1216 }
1217
1218 function qaddFilter() {
1219
1220         if (!xmlhttp_ready(xmlhttp)) {
1221                 printLockingError();
1222                 return
1223         }
1224
1225         var query = Form.serialize("filter_add_form");
1226
1227         xmlhttp.open("GET", "backend.php?" + query, true);
1228         xmlhttp.onreadystatechange=infobox_submit_callback;
1229         xmlhttp.send(null);
1230
1231         return true;
1232 }
1233
1234 function toggleSubmitNotEmpty(e, submit_id) {
1235         try {
1236                 document.getElementById(submit_id).disabled = (e.value == "")
1237         } catch (e) {
1238                 exception_error("toggleSubmitNotEmpty", e);
1239         }
1240 }
1241
1242 function isValidURL(s) {
1243         return s.match("http://") != null || s.match("https://") != null;
1244 }
1245
1246 function qafAdd() {
1247
1248         if (!xmlhttp_ready(xmlhttp)) {
1249                 printLockingError();
1250                 return
1251         }
1252
1253         notify("Adding feed...");
1254
1255         closeInfoBox();
1256
1257         var feeds_doc = document;
1258
1259 //      feeds_doc.location.href = "backend.php?op=error&msg=Loading,%20please wait...";
1260         
1261         var query = Form.serialize("feed_add_form");
1262         
1263         xmlhttp.open("GET", "backend.php?" + query, true);
1264         xmlhttp.onreadystatechange=dlg_frefresh_callback;
1265         xmlhttp.send(null);
1266
1267         return false;
1268 }
1269
1270 function filterCR(e)
1271 {
1272      var key;
1273
1274      if(window.event)
1275           key = window.event.keyCode;     //IE
1276      else
1277           key = e.which;     //firefox
1278
1279      if(key == 13)
1280           return false;
1281      else
1282           return true;
1283 }
1284
1285 function getMainContext() {
1286         return this.window;
1287 }
1288
1289 function getFeedsContext() {
1290         return this.window;
1291 }
1292
1293 function getContentContext() {
1294         return this.window;
1295 }
1296
1297 function getHeadlinesContext() {
1298         return this.window;
1299 }
1300
1301 var debug_last_class = "even";
1302
1303 function debug(msg) {
1304
1305         if (debug_last_class == "even") {
1306                 debug_last_class = "odd";
1307         } else {
1308                 debug_last_class = "even";
1309         }
1310
1311         var c = document.getElementById('debug_output');
1312         if (c && c.style.display == "block") {
1313                 while (c.lastChild != 'undefined' && c.childNodes.length > 100) {
1314                         c.removeChild(c.lastChild);
1315                 }
1316         
1317                 var d = new Date();
1318                 var ts = leading_zero(d.getHours()) + ":" + leading_zero(d.getMinutes()) +
1319                         ":" + leading_zero(d.getSeconds());
1320                 c.innerHTML = "<li class=\"" + debug_last_class + "\"><span class=\"debugTS\">[" + ts + "]</span> " + 
1321                         msg + "</li>" + c.innerHTML;
1322         }
1323 }
1324
1325 function getInitParam(key) {
1326         return getMainContext().init_params[key];
1327 }
1328
1329 function storeInitParam(key, value, is_client) {
1330         try {
1331                 if (!is_client) {
1332                         if (getMainContext().init_params[key] != value) {
1333                                 debug("storeInitParam: " + key + " => " + value);
1334                                 //new Ajax.Request("backend.php?op=rpc&subop=storeParam&key=" + 
1335                                 //      param_escape(key) + "&value=" + param_escape(value));   
1336                                 var f = getMainContext().document.getElementById("backReqBox");
1337                                 f.src = "backend.php?op=rpc&subop=storeParam&key=" + 
1338                                         param_escape(key) + "&value=" + param_escape(value);
1339                         }
1340                 }
1341                 getMainContext().init_params[key] = value;
1342         } catch (e) {
1343                 exception_error("storeInitParam", e);
1344         }
1345 }
1346
1347 /*
1348 function storeInitParams(params, is_client) {
1349         try {
1350                 var s = "";
1351
1352                 for (k in params) {
1353                         if (getMainContext().init_params[k] != params[k]) {
1354                                 s += k + "=" + params[k] + ";";
1355                                 getMainContext().init_params[k] = params[k];
1356                         }
1357                 } 
1358
1359                 debug("storeInitParams: " + s);
1360         
1361                 if (!is_client) {
1362                         new Ajax.Request("backend.php?op=rpc&subop=storeParams&str=" + s);
1363                 }
1364         } catch (e) {
1365                 exception_error("storeInitParams", e);
1366         }
1367 }*/
1368
1369 function fatalError(code, message) {
1370         try {   
1371                 var fe = document.getElementById("fatal_error");
1372                 var fc = document.getElementById("fatal_error_msg");
1373
1374                 fc.innerHTML = "Code " + code + ": " + message;
1375
1376                 fe.style.display = "block";
1377
1378         } catch (e) {
1379                 exception_error("fatalError", e);
1380         }
1381 }
1382
1383 function getFeedName(id, is_cat) {      
1384         var d = getFeedsContext().document;
1385
1386         var e;
1387
1388         if (is_cat) {
1389                 e = d.getElementById("FCATN-" + id);
1390         } else {
1391                 e = d.getElementById("FEEDN-" + id);
1392         }
1393         if (e) {
1394                 return e.innerHTML.stripTags();
1395         } else {
1396                 return null;
1397         }
1398 }
1399
1400 function viewContentUrl(url) {
1401         getContentContext().location = url;
1402 }