var org = {p3k: ({url:"http://hfradio.org/propsupport/prop.rss", xml:"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\n<rss version=\"2.0\">\n\n<channel>\n<title>HFRadio.org Propagation Center</title>\n<link>http://prop.hfradio.org/</link>\n<description>HFRadio.org Propagation Center - Live Space Weather and Radio Propagation Information</description>\n<language>en</language>\n<copyright>Copyright 2008, NW7US, Tomas Hood</copyright>\n<pubDate>Tue, 09 Feb 2010 15:45:02 GMT</pubDate>\n<lastBuildDate>Tue, 09 Feb 2010 15:45:02 GMT</lastBuildDate>\n\n<image>\n<title>HFRadio.org Propagation Center</title>\n<url>http://hfradio.org/graphics/hfradio_propagation.gif</url>\n<link>http://prop.hfradio.org/</link>\n<width>16</width>\n<height>16</height>\n<description>HFRadio.org</description>\n</image>\n\n\n<item>\n<guid isPermaLink=\"false\">2010020908450101</guid>\n<title>Solar Report 1545 09 Feb 2010</title>\n<description>Solar Flux: 94 | Ap: 4 | Kp 0 (04 nT) at 1500 09 Feb</description>\n<link>http://prop.hfradio.org/</link>\n</item>\n\n<item>\n<guid isPermaLink=\"false\">2010020908450102</guid>\n<title>Observed Space Weather 1545 09 Feb 2010</title>\n<description>Space weather for the past 24 hours has been minor.\nRadio blackouts reaching the R1 level occurred.</description>\n<link>http://prop.hfradio.org/</link>\n</item>\n\n<item>\n<guid isPermaLink=\"false\">2010020908450103</guid>\n<title>Forecast Space Weather 1545 09 Feb 2010</title>\n<description>Space weather for the next 24 hours is expected to be minor.\nRadio blackouts reaching the R1 level are expected.</description>\n<link>http://prop.hfradio.org/</link>\n</item>\n\n<item>\n<guid isPermaLink=\"false\">2010020908450104</guid>\n<title>Flares 1545 09 Feb 2010</title>\n<description>6h hi C2.7(0113Z 02/09) 24h hi M4.0(0736Z 02/08)</description>\n<link>http://prop.hfradio.org/</link>\n</item>\n\n<item>\n<guid isPermaLink=\"false\">2010020908450105</guid>\n<title>Sunspots 1545 09 Feb 2010</title>\n<description>SSN: 71 (02/08/2010)</description>\n<link>http://prop.hfradio.org/</link>\n</item>\n\n<item>\n<guid isPermaLink=\"false\">2010020908450106</guid>\n<title>Solar Wind 1545 09 Feb 2010</title>\n<description>Solar Wind: 352 km/s @ 2.0 protons/cm3\nBz:  -0.9 nT at 2010 Feb 09 1543Z</description>\n<link>http://prop.hfradio.org/</link>\n</item>\n\n<item>\n<guid isPermaLink=\"false\">2010020908450107</guid>\n<title>Global Conditions 1545 09 Feb 2010</title>\n<description>Global HF Propagation Conditions for 1500Z on 09 Feb, 2010\nLow Latitude: Normal\nMid Latitude: Normal\nHi  Latitude: Normal</description>\n<link>http://prop.hfradio.org/</link>\n</item>\n\n<item>\n<guid isPermaLink=\"false\">2010020908450108</guid>\n<title>Warning (68) 1545 09 Feb 2010</title>\n<description>Warning (68): Geomagnetic Sudden Impulse expected\nFrom: 2010 Feb 08 2300 UTC To: 2010 Feb 08 2359 UTC\nCondition: IP Shock Passage Observed: 2010 Feb 08 2218 UTC</description>\n<link>http://prop.hfradio.org/</link>\n</item>\n<item>\n<guid isPermaLink=\"false\">2010020908450109</guid>\n<title>Space Weather / Radio Propagation Podcast</title>\n<description>The new 'NW7US Space Weather and Radio Propagation Podcast' is online - and Edition One is posted. To hear this podcast, and for more information, see:\n&lt;br /&gt;&lt;br /&gt;\n\n&lt;a href=&quot;http://podcast.hfradio.org/&quot;&gt;NW7US Space Weather and Radio Propagation Podcast&lt;/a&gt;\n&lt;br /&gt;&lt;br /&gt;\n</description>\n<link>http://podcast.hfradio.org/</link>\n</item>\n\n<item>\n<guid isPermaLink=\"false\">2010020908450110</guid>\n<title>Please help support HFRadio.org</title>\n<description>For those of you who appreciate the resources provided by HFRadio.org (RSS feed, and so forth), would you please consider helping HFRadio.org stay online?  Your donation, today, would help keep things moving ahead.  It is greatly appreciated!\n&lt;br /&gt;&lt;br /&gt;\nPlease donate today: \n&lt;br /&gt;&lt;br /&gt;\n\n&lt;a href=&quot;http://hfradio.org/help.html&quot;&gt;Donate and help HFRadio.org stay alive!&lt;/a&gt;\n&lt;br /&gt;&lt;br /&gt;\n</description>\n<link>http://hfradio.org/help.html</link>\n</item>\n\n<item>\n<guid isPermaLink=\"false\">2010020908450111</guid>\n<title>Check it out Tomas' music</title>\n<description>For those of you who enjoy acoustic, americana, folk rock, or progressive rock, check out my musical endeavor. I'm Tomas David Hood - Musician, and I run HFRadio.org and the propagation RSS feed. Besides being an Amateur Radio Operator (NW7US), I play guitar, keyboard, and sometimes I sing. I am a songwriter.  I'd love to have you listen to some of my most recent music, and let me know what you think.\n&lt;br /&gt;&lt;br /&gt;\nPlease check out my music, and also sign up to be a fan of my music.  I won't email you that often, I promise.  Here is the link to my music:\n&lt;br /&gt;&lt;br /&gt;\n\n\n&lt;a href=&quot;http://www.soundclick.com/bands/page_music.cfm?bandID=862873&quot;&gt;Tomas David Hood at SoundClick&lt;/a&gt;\n&lt;br /&gt;&lt;br /&gt;\nYou can also read about my music at these two sites:\n&lt;br /&gt;&lt;br /&gt;\n&lt;a href=&quot;http://shoutlife.com/tomasdavidhood&quot;&gt;http://shoutlife.com/tomasdavidhood&lt;/a&gt;\n&lt;br /&gt;\n&lt;a href=&quot;http://myspace.com/tomashood&quot;&gt;http://myspace.com/tomashood&lt;/a&gt;\n</description>\n<link>http://www.soundclick.com/bands/page_music.cfm?bandID=862873</link>\n</item>\n\n\n\n\n\n</channel>\n</rss>\n\n", message:undefined, modified:(new Date(1265730557139)), baseUri:"http://p3k.org/rss/", query:"frameColor=black&textColor=#cccccc&_=1235576000930&titleBarColor=#cc6600&compact=&fontFace=Arial&align=&showXmlButton=true&maxItems=7&boxFillColor=#000000&titleBarTextColor=#ffffff&width=625&url=http://hfradio.org/propsupport/prop.rss&", param:{textColor:"#cccccc", width:"625", align:"", fontFace:"Arial", url:"http://hfradio.org/propsupport/prop.rss", compact:"", frameColor:"black", showXmlButton:"true", boxFillColor:"#000000", _:"1235576000930", titleBarColor:"#cc6600", maxItems:"7", titleBarTextColor:"#ffffff"}, box:"<table class=\"rssBox\" width=\"${width}\" bgcolor=\"${frameColor}\" \n      style=\"table-layout: fixed; overflow: hidden; font-family: ${fontFace};\" \n      align=\"${align}\" cellspacing=\"1\" cellpadding=\"7\" border=\"0\">\n<tr bgcolor=\"${titleBarColor}\">\n<td>\n   ${xmlButton}\n   <strong>${title}</strong>\n   <div>\n      <small style=\"color: ${titleBarTextColor};\">${date}</small>\n   </div>\n</td>\n</tr>\n<tr bgcolor=\"${boxFillColor}\">\n<td class=\"rssBoxContent\" style=\"color: ${textColor};\">\n   ${image}\n   ${items}\n   ${input}\n   <div class=\"rssBoxPromo\" style=\"margin-top: 0px; text-align: left; \n         font: 9px verdana, sans-serif\">\n      RSS box by <a href=\"http://p3k.org/rss\">p3k.org</a>.\n   </div>\n</td>\n</tr>\n</table>\n\n", image:"<a href=\"${link}\"><img src=\"${source}\" width=\"${width}\" height=\"${height}\" \nalt=\"${title}\" title=\"${title}\" align=\"${align}\" valign=\"${valign}\" \nhspace=\"${hspace}\" vspace=\"${vspace}\" border=\"0\" /></a>\n\n", input:"<form method=\"get\" action=\"${link}\">\n${description}\n<input type=\"text\" name=\"${name}\" size=\"15\" /> <input type=\"submit\" \nvalue=\"${title}\" />\n</form>\n\n", item:"<div class=\"rssBoxItemContent\">\n${title}\n${break}\n${description}\n${buttons}\n</div><br />\n\n", date:"${year}-${month}-${day}, ${hours}:${minutes}h\n\n", link:"<a class=\"${class}\" href=\"${link}\" style=\"${style}\">${text}</a>\n", error:"<?xml version=\"1.0\"?>\n<rss version=\"error\">\n   <channel>\n      <title>RSS Box Error</title>\n      <link>${link}</link>\n      <description>This output was automatically generated to report an error \n      that occurred during a request to the JavaScript RSS Box Viewer.</description>\n      <item>\n         <title>Oops, something went wrong...</title>\n         <description>An error occurred while processing the request to the \n         JavaScript RSS Box Viewer.</description>\n      </item>\n      <item>\n         <title>An error message was returned by the server.</title>\n         <description>${message}</description>\n      </item>\n      <item>\n         <description>Most likely, this might have happened because of a \n         non-existent or invalid RSS feed URL. Please check and possibly correct \n         your input, then try again.</description>\n      </item>\n   </channel>\n</rss>\n\n"})};
function debug(str) {
   return document.write('<p><span style="background-color: yellow;">', 
         str, '</span><p>');
}

org.p3k.RssBox = function() {
   var ref;
   var ISOPATTERN = /([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9:]+).*$/;
   
   var data = org.p3k;
   data.defaults = {
      url: "http://blog.p3k.org/rss",
      maxItems: 7,
      width: 200,
      align: "",
      frameColor: "black",
      titleBarColor: "lightblue",
      titleBarTextColor: "black",
      boxFillColor: "white",
      textColor: "black",
      showXmlButton: "",
      compact: ""
   };
   
   var baseUri = data.baseUri;
    
   var getColor = function(str) {
      if (str.length === 6 && parseInt(str, 16) && str.indexOf("#") !== 0) {
         str = "#" + str;
      }
      return str.toLowerCase();
   }

   var value;
   for (var i in data.defaults) {
      value = data.param[i];
      if (!value || value.length === 0) {
         data.param[i] = data.defaults[i];
      } else if (i.indexOf("Color") > 0) {
         data.param[i] = getColor(value);
      }
   }

   // FIXME: Ugly work-around for many boxes using too small width 
   // values because the former version did not show the exact output.
   if (data.param.javascript && data.param.width < 200) {
      data.param.width = 200;
   }
   // Remove obsolete parameters from param and query
   delete data.param.javascript;
   data.query = data.query.replace("javascript=true", "");   

   var NAMESPACES = {
      dc: "http://purl.org/dc/elements/1.1/",
      rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   };

   var getUrl = function(url) {
      url = baseUri + "proxy.r?" + encodeURIComponent(url);
   
      var HttpRequest = function() {
         return (typeof XMLHttpRequest !== "undefined") ?
            new XMLHttpRequest() : new ActiveXObject("Msxml2.XMLHTTP");
      }
   
      var request = new HttpRequest();
      request.open("GET", url, false);
      request.send(null);   
      if (!request.getResponseHeader("Date")) {
         var cached = request;
         var ifModifiedSince = cached.getResponseHeader("Last-Modified") || 
               new Date(0); // January 1, 1970
         request = new HttpRequest();
         request.open("GET", url, false);
         request.setRequestHeader("If-Modified-Since", ifModifiedSince);
         request.send("");
         if (request.status === 304) {
            request = cached; 
         }
      }
      return request;
   }
   
   var getDocument = function(source) {
      if (source) {
         if (document.implementation.createDocument) {
            var parser = new DOMParser();
            var doc = parser.parseFromString(source, "text/xml");
            return doc;
         } else if (window.ActiveXObject) {
            var doc = new ActiveXObject("Microsoft.XMLDOM");
            doc.async = "false";
            doc.loadXML(source);
            return doc;
         }
      }
      return null;
   }
   
   var getError = function() {
      var msg = null, root;
      if (!xml || data.message) {
         msg = data.message || "Unknown error.";
      } else if (xml.parseError && xml.parseError.errorCode) {
         msg = xml.parseError.reason; // IExplore
      } else if (root = xml.documentElement) {
         var errorNode;
         if (root.nodeName === "parsererror") {
            msg = xml.documentElement.textContent; // Mozilla
         } else if ((errorNode = root.childNodes[0]) && 
               errorNode.nodeName === "parsererror") {
            msg = errorNode.textContent; // Safari
         } else if (!/rss|rdf|scriptingNews/i.test(xml.documentElement.nodeName)) {
            msg = "Incompatible data format. Are you sure this is an RSS feed?";
         }
      }
      return msg;
   }
   
   var getNode = function(parent, name, namespace) {
      if (namespace) {
         if (typeof parent.getElementsByTagNameNS === "undefined") {
            var elements = parent.getElementsByTagName(namespace + ":" + name);
         } else {
            var elements = parent.getElementsByTagNameNS(NAMESPACES[namespace], name);
         }
      } else {
         var elements = parent.getElementsByTagName(name);
      }
      if (elements && elements[0]) {
         return elements[0];
      }
      return null;
   }
   
   var getText = function(node) {
      if (node && node.childNodes && node.childNodes.length > 0) {
         return node.childNodes[0].nodeValue || "";
      }
      return "";
   }
   
   var trim = function(str) {
      if (str) {
         return str.replace(/^\s*(\S*)\s*$/, "$1");
      }
      return "";
   }
   
   var padZero = function(n) {
      if (n < 10) {
         return "0" + n
      }
      return n;
   }

   var encodeXml = function(str) {
      if (!str) {
         return "";
      }
      return str.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/&/g, "&amp;");
   }
      
   var render = function(template, param) {
      if (!template || !param) {
         return template;
      }
      template = template.replace(/\$\{([^}]+)\}/g, function() {
         var key = arguments[1];
         return param[key] || "";
      });
      return template;
   }
   
   var renderDate = function(date) {
      if (date.constructor !== Date) {
         var str = String(date);
         var millis = Date.parse(str.replace(ISOPATTERN, "$1/$2/$3 $4"));
         if (millis) {
            date = new Date(millis);
         } else {
            date = new Date;
         }
      }

      return render(data.date, {
         year: date.getFullYear(),
         month: padZero(date.getMonth() + 1),
         day: padZero(date.getDate()),
         hours: padZero(date.getHours()),
         minutes: padZero(date.getMinutes()),
         seconds: padZero(date.getSeconds()),
         timeZone: "" // date.getTimezoneOffset()
      });
   }
   
   var renderButtons = function(enclosure, source) {
      var result = "";
      if (enclosure && enclosure.link) {
         result += render(data.image, {
            source: baseUri + "enclosure.gif",
            title: enclosure.type,
            link: encodeURI(enclosure.link),
            width: 13,
            height: 16
         });
      }
      if (source && source.link) {
         result += render(data.image, {
            source: baseUri + "source.gif",
            title: source.title,
            link: encodeURI(source.link),
            width: 15,
            height: 15
         });
      }
      return result;         
   }
   
   var param = data.param;
   var rss = data.rss = {items: []};
   var xml = getDocument(data.xml);

   rss.error = getError();
   if (rss.error !== null) {
      xml = getDocument(render(data.error, {
         link: baseUri + "?" + encodeXml(data.query),
         message: encodeXml(rss.error)
      }));
      param.compact = 0;
      param.showXmlButton = 1;
   }

   var root = xml.documentElement;
   var type = root.nodeName;

   if (type === "scriptingNews") {
      var channel = getNode(xml, "header");
      rss.format = "Scripting News";
      rss.version = getText(getNode(channel, "scriptingNewsVersion"));
      rss.title = getText(getNode(channel, "channelTitle"));
      rss.description = getText(getNode(channel, "channelDescription"));
      rss.link = getText(getNode(channel, "channelLink"));
      if (ref = getText(getNode(channel, "imageUrl"))) {
         ref = rss.image = {source: ref};
         ref.title = getText(getNode(channel, "imageTitle"));
         ref.link = getText(getNode(channel, "imageLink"));
         ref.width = getText(getNode(channel, "imageWidth"));
         ref.height = getText(getNode(channel, "imageHeight"));
         ref.description = getText(getNode(channel, "imageCaption"));
      }
   } else {
      var channel = getNode(xml, "channel");
      rss.format = "RSS";
      rss.version = (type === "rdf:RDF") ? "1.0" : 
            root.getAttribute("version");
      rss.title = getText(getNode(channel, "title"));
      rss.description = getText(getNode(channel, "description"));
      rss.link = getText(getNode(channel, "link"));
      var image = getNode(xml, "image");
      if (image) {
         ref = rss.image = {};
         ref.source = getText(getNode(image, "url"));
         ref.title = getText(getNode(image, "title"));
         ref.link = getText(getNode(image, "link"));
         ref.width = getText(getNode(image, "width"));
         ref.height = getText(getNode(image, "height"));
         ref.description = getText(getNode(image, "description"));
      }
   }
   
   if (type === "rdf:RDF") {
      rss.date = renderDate(getText(getNode(channel, "date", "dc")) || data.modified);
      rss.rights = getText(getNode(channel, "creator", "dc"));
      var input = getNode(root, "textinput");
      if (input && !getNode(input, "link")) {
         input = root.getElementsByTagName("textinput")[1];
      }
      if (input) {
         ref = rss.input = {};
         ref.link = getText(getNode(input, "link"));
         ref.description = getText(getNode(input, "description"));
         ref.name = getText(getNode(input, "name"));
         ref.title = getText(getNode(input, "title"));
      }
   } else {
      rss.date = renderDate(getText(getNode(channel, "lastBuildDate") || getText(getNode(channel, "pubDate"))) || data.modified);
      rss.rights = getText(getNode(channel, "copyright"));
   }
   
   var item, text, node;
   var items = xml.getElementsByTagName("item");

   for (var i=0; i<Math.min(items.length, param.maxItems); i+=1) {
      item = items[i];

      if (type === "scriptingNews") {
         ref = {title: ""};
         ref.description = getText(getNode(item, "text")).replace(/\n/g, " ");
         ref.link = getText(getNode(item, "link"));
         if (text = trim(getText(getNode(item, "linetext")).replace(/\n/g, " "))) {
            ref.description = ref.description.replace(new RegExp(text), 
                  '<a href="' + getText(getNode(item, "url")) + '">' + text + '</a>');
         }
      } else {
         ref = {
            title: getText(getNode(item, "title")),
            description: getText(getNode(item, "description")),
            link: getText(getNode(item, "link") || getNode(item, "guid"))
         };
     }

     if (node = getNode(item, "source")) {
        ref.source = {
           link: node.getAttribute("url"),
           title: getText(node)
        }
     }
     
     if (node = getNode(item, "enclosure")) {
        ref.enclosure = {
           link: node.getAttribute("url"),
           length: node.getAttribute("length"),
           type: node.getAttribute("type")
        }
     }
     
     if (node = getNode(item, "category")) {
        ref.category = {
           domain: node.getAttribute("domain") || "",
           content: getText(node)
        }
     }
     
     rss.items.push(ref);
   }
   
   var item, items = "";
   for (var i=0; i<rss.items.length; i+=1) {
      item = rss.items[i];
      items += render(data.item, {
         title: new function() {
            var title = (!param.compact ? "<strong>" : "");
            if (item.link) {
               title += render(data.link, {
                  link: encodeURI(item.link),
                  text: item.title,
                  'class': "rssBoxItemTitle"
               });
            } else {
               title += item.title;
            }
            !param.compact && (title += "</strong>");
            return new String(title); // FIXME: Funny, title alone will be rendered as [object]
         }(),
         'break': item.title && item.description ? "<br />" : "",
         description: (!param.compact || !item.title) && item.description,
         buttons: renderButtons(item.enclosure, item.source)
      });
   }
   
   var box = render(data.box, {
      title: rss.link ? render(data.link, {
         link: encodeURI(rss.link),
         text: rss.title,
         'class': "rssBoxTitle",
         style: "color: " + param.titleBarTextColor
      }) : rss.title,
      description: rss.description,
      items: items,

      xmlButton: param.showXmlButton && render(data.image, {
         link: param.url,
         source: baseUri + "rss.png",
         title: rss.format + " " + rss.version,
         width: 16,
         height: 16,
         align: "right",
         hspace: 3
      }),
      
      image: !param.compact && rss.image && render(data.image, {
         link: encodeURI(rss.image.link),
         source: rss.image.source,
         width: rss.image.width,
         height: rss.image.height,
         title: rss.image.title,
         align: "right",
         valign: "baseline",
         hspace: 5,
         vspace: 5
      }),
      
      input: !param.compact && rss.input && render(data.input, {
         link: encodeURI(rss.input.link),
         description: rss.input.description,
         name: rss.input.name,
         title: rss.input.title
      }),
      
      date: rss.date,
      width: param.width,
      frameColor: param.frameColor,
      fontFace: param.fontFace,
      align: param.align,
      titleBarColor: param.titleBarColor,
      titleBarTextColor: param.titleBarTextColor,
      boxFillColor: param.boxFillColor,
      textColor: param.textColor
   });

   if (!window.rssBoxSetup) {
      document.write(box);
   }
   
   return box;
};

org.p3k.RssBox();

