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