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