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