var org = {p3k: ({url:"http://www.ci.madison.al.us/support/newsflash.xml", xml:"<?xml version=\"1.0\"?><rss version=\"2.0\"><channel>\n\t<title>City of Madison - News Flash</title>\n\t<link>http://www.madisonal.gov/CivicAlerts.aspx</link>\n\t<lastBuildDate>Mon, 8 Feb 2010 17:16 -0600</lastBuildDate>\n\t<description>City of Madison - Get the latest news</description>\n\t<language>en-us</language>\n\t<item>\n\t\t<title>Community Support Resources</title>\n\t\t<link>http://www.madisonal.gov/CivicAlerts.aspx?aid=113</link>\n\t\t<pubDate>Mon, 8 Feb 2010 17:14 -0600</pubDate>\n\t\t<description>-- Community Support Line available 8am - 8pm.  &lt;b&gt;772-2883&lt;/b&gt;&lt;BR&gt;-- &lt;a href=&quot;http://www.youtube.com/watch?v=bKNbR6Uw15g&quot;&gt;Video from Community Meeting&lt;/a&gt; - HuntsvilleNewswire.com&lt;BR&gt;-- Press conference decision will be made at noon Tuesday</description>\n\t\t<guid>http://www.madisonal.gov/CivicAlerts.aspx?aid=113</guid>\n\t</item>\n\t<item>\n\t\t<title>Discovery Middle School Briefings- UPDATED 2/7/10 - 5:00 PM</title>\n\t\t<link>http://www.madisonal.gov/CivicAlerts.aspx?aid=110</link>\n\t\t<pubDate>Mon, 8 Feb 2010 17:16 -0600</pubDate>\n\t\t<description>School resumed today with increased security and support resources.  Madison City School board, central Office staff, and city officials were at the schools monitoring activity.  A few additional items can be provided this evening...</description>\n\t\t<guid>http://www.madisonal.gov/CivicAlerts.aspx?aid=110</guid>\n\t</item>\n\t<item>\n\t\t<title>City of Madison and SWDA Partner on Household Hazardous Waste Collection Day</title>\n\t\t<link>http://www.madisonal.gov/CivicAlerts.aspx?aid=109</link>\n\t\t<pubDate>Fri, 5 Feb 2010 09:21 -0600</pubDate>\n\t\t<description>On February 20th, the City of Madison is planning to host a Household Hazardous Waste Collection Day from 9 am - 1 pm at Madison City Hall.</description>\n\t\t<guid>http://www.madisonal.gov/CivicAlerts.aspx?aid=109</guid>\n\t</item>\n\t<item>\n\t\t<title>REMINDER - Alabama &quot;Move Over Law&quot; Now in Effect</title>\n\t\t<link>http://www.madisonal.gov/CivicAlerts.aspx?aid=108</link>\n\t\t<pubDate>Tue, 2 Feb 2010 08:30 -0600</pubDate>\n\t\t<description>Law requires drivers to change lanes or slow down for vehicles with flashing emergency lights.</description>\n\t\t<guid>http://www.madisonal.gov/CivicAlerts.aspx?aid=108</guid>\n\t</item>\n\t<item>\n\t\t<title>Balch Rd. Closure from Hwy. 72 to Wann Dr.</title>\n\t\t<link>http://www.madisonal.gov/CivicAlerts.aspx?aid=105</link>\n\t\t<pubDate>Fri, 22 Jan 2010 16:07 -0600</pubDate>\n\t\t<description>Beginning Monday, January 25, 2010, Balch Road will be closed from Hwy. 72 to Wann Dr. for approximately 10 months.</description>\n\t\t<guid>http://www.madisonal.gov/CivicAlerts.aspx?aid=105</guid>\n\t</item>\n\t<item>\n\t\t<title>Eastview Drive Traffic Signal Timing Changes</title>\n\t\t<link>http://www.madisonal.gov/CivicAlerts.aspx?aid=104</link>\n\t\t<pubDate>Fri, 22 Jan 2010 14:16 -0600</pubDate>\n\t\t<description>The City of Madison Engineering Department would like to inform the public that traffic signal timing changes have been made at the intersection of Hughes Rd. and Eastview Dr.</description>\n\t\t<guid>http://www.madisonal.gov/CivicAlerts.aspx?aid=104</guid>\n\t</item>\n\t<item>\n\t\t<title>Public Safety Survey</title>\n\t\t<link>http://www.madisonal.gov/CivicAlerts.aspx?aid=95</link>\n\t\t<pubDate>Wed, 30 Dec 2009 06:59 -0600</pubDate>\n\t\t<description>In a continued effort to improve Madison Police Department's service to our community, we ask that interested citizens please take this survey.  All comments will be appreciated and will help us to serve our citizens better.</description>\n\t\t<guid>http://www.madisonal.gov/CivicAlerts.aspx?aid=95</guid>\n\t</item>\n\t<item>\n\t\t<title>Rape Aggression Defense classes offered for 2010</title>\n\t\t<link>http://www.madisonal.gov/CivicAlerts.aspx?aid=90</link>\n\t\t<pubDate>Mon, 8 Feb 2010 15:34 -0600</pubDate>\n\t\t<description>Madison Police Department has opened registration for the Rape Aggression Defense (R.A.D.) Systems Basic Physical Defense Courses, to be offered in 2010.</description>\n\t\t<guid>http://www.madisonal.gov/CivicAlerts.aspx?aid=90</guid>\n\t</item>\n\t<item>\n\t\t<title>Public Safety Survey</title>\n\t\t<link>http://www.madisonal.gov/CivicAlerts.aspx?aid=91</link>\n\t\t<pubDate>Fri, 18 Dec 2009 09:51 -0600</pubDate>\n\t\t<description>In a continued effort to improve Madison Police Department's service to our community, we ask that interested citizens please take this survey.  All comments will be appreciated and will help us to serve our citizens better.</description>\n\t\t<guid>http://www.madisonal.gov/CivicAlerts.aspx?aid=91</guid>\n\t</item>\n\t<item>\n\t\t<title>Intersection Improvements - Hughes Rd. and U.S. Hwy. 72</title>\n\t\t<link>http://www.madisonal.gov/CivicAlerts.aspx?aid=75</link>\n\t\t<pubDate>Tue, 3 Nov 2009 10:49 -0600</pubDate>\n\t\t<description>The City of Madison would like to inform residents that beginning the first week of November work will begin at the intersection of Hughes Rd. and U.S. Hwy. 72.  Construction will last until approximately April 19, 2010.</description>\n\t\t<guid>http://www.madisonal.gov/CivicAlerts.aspx?aid=75</guid>\n\t</item>\n\t<item>\n\t\t<title>Intersection Improvements - Hughes Rd. and U.S. Hwy. 72</title>\n\t\t<link>http://www.madisonal.gov/CivicAlerts.aspx?aid=76</link>\n\t\t<pubDate>Wed, 2 Dec 2009 08:14 -0600</pubDate>\n\t\t<description>The City of Madison would like to inform residents that beginning the first week of November work will begin at the intersection of Hughes Rd. and U.S. Hwy. 72.  Construction will last until approximately April 19, 2010.</description>\n\t\t<guid>http://www.madisonal.gov/CivicAlerts.aspx?aid=76</guid>\n\t</item>\n\t<item>\n\t\t<title>Park Status - CLOSED</title>\n\t\t<link>http://www.madisonal.gov/CivicAlerts.aspx?aid=52</link>\n\t\t<pubDate>Thu, 4 Feb 2010 13:10 -0600</pubDate>\n\t\t<description>All fields at Dublin Park and Palmer Park are CLOSED</description>\n\t\t<guid>http://www.madisonal.gov/CivicAlerts.aspx?aid=52</guid>\n\t</item>\n</channel></rss>\n", message:undefined, modified:(new Date(1265730982386)), baseUri:"http://p3k.org/rss/", query:"textColor=black&width=650&align=&fontFace=&url=http://www.ci.madison.al.us/support/newsflash.xml&frameColor=black&compact=&showXmlButton=true&boxFillColor=#cccccc&_=1254679923957&titleBarColor=#0033cc&maxItems=15&titleBarTextColor=#ffffff&", param:{textColor:"black", align:"", width:"650", fontFace:"", url:"http://www.ci.madison.al.us/support/newsflash.xml", compact:"", frameColor:"black", showXmlButton:"true", boxFillColor:"#cccccc", _:"1254679923957", titleBarColor:"#0033cc", maxItems:"15", 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();

