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