/* ------------------------------------------
   RSS Replay v 1.2.3 build 51
   Coded by Ronald Northrip
   May-July 2006
   
   All code here is Copyright Protected.
   Use or distribution without permission is
   strictly prohibited.
   Copyright 2006. All Rights Reserved.
   Ronald Northrip Software Incorporated
   http://www.rnsoft.com/
   ------------------------------------------ */
   

// Global Settings --------------------------

// rssReplayFeed ----------------------------
// the function creates new feeds -----------
function rssReplayFeed(){
   // params - id, dSM, dP, dH, dHC, dS, dSC, dSx, sources, [xC, xCTTL, suprs]
   //          0    1   2   3    4   5    6    7      8      9     10    11
   var i=0
   var args=rssReplayFeed.arguments
   var feed=new rssReplay()
   if (args.length<9) { document.write(feed.localStringReplayFeedParamError); return; }
   
   //proxy
   feed.proxyHref=""
   if ((document) && (document.getElementsByTagName)) {
      var scripts=document.getElementsByTagName("script")
      if ((scripts) && (scripts.length>0)) {
         var exp="^(rssReplayProxy|.*/rssReplayProxy)\\.....?(?:\\?format=js)?$"
         var re=new RegExp(exp)
         for (i=0; i<scripts.length; i++) {
            var src=scripts[i].src
            if ((src) && (src!="") && 
                (src.substring(0,5).toLowerCase()!="file:") && 
                (re.test(src))) {
			   feed.proxyHref=scripts[i].src
			   if (feed.proxyHref.substring(feed.proxyHref.length-10,feed.proxyHref.length)=="?format=js") {
			      feed.proxyHref=feed.proxyHref.substring(0,feed.proxyHref.length-10) }
			   i=scripts.length
			}
         }
      }
   }
   if (args.length>9) { feed.proxyCache=args[9] }
   if (args.length>10) { feed.proxyCacheTTL=args[10] }
   if (args.length>11) { feed.supressErrors=args[11] }
   
   //identity
   feed.id=args[0]
   //settings
   feed.displayStoriesMax=args[1]
   feed.displayPrefix=args[2]
   feed.displayHeadline=args[3]
   feed.displayHeadlineClass=args[4]
   feed.displayStory=args[5]
   feed.displayStoryClass=args[6]
   feed.displaySuffix=args[7]
   //sources
   if (typeof(args[8])=="string") {
      feed.addSource(args[8])
   } else {
      var list=args[8]
      for (i=0; i<list.length; i++) {
         feed.addSource(list[i])
      }
   }
   
   //globally: time when the all feeds began so we have a "now" time that is the same
   //used for entries that have no dates
   if(typeof(rssReplayCurrentDate)!="object") {
      rssReplayCurrentDate=new Date()
   }
   
   //add it to the list -- create the list if we need it
   //done this way in case of multiple
   if(typeof(rssReplayFeeds)!="object") {
      rssReplayFeeds=new Array()
   }
   rssReplayFeeds[rssReplayFeeds.length]=feed
   
   //display it here
   rssReplayFeeds[rssReplayFeeds.length-1].display()
}


// Generic Functions used across objects ----

function rrpBuildSourceList() {
   var args=rrpBuildSourceList.arguments
   var out=new Array()
   for (var i=0; i<args.length; i++) {
      out[out.length]=args[i]
   }
   return out
}

// XML/HTML parser regular expressions
// define expressions for use in pattern matching
function rrpSetupRegExp() {
   var exp="(?:test)"
   try {rexTest=new RegExp(exp)} catch(err) {return false}
   //rexJustTag=/<\/?([^\s='"<>]+)/
   exp="<\\/?([^\\s='\"<>]+)"
   rexJustTag=new RegExp(exp)
   //rexJustAttributes=/(?:\s+|(?:([^\s='"<>]+)\s*=\s*([^\s='"<>]+))|(?:([^\s='"<>]+)\s*=\s*"([^"]*)")|(?:([^\s='"<>]+)\s*=\s*'([^']*)')|(?:[^\s='"<>]+))/
   exp="(?:\\s+|(?:([^\\s='\"<>]+)\\s*=\\s*([^\\s'\"<>]+))|(?:([^\\s='\"<>]+)\\s*=\\s*\"([^\"]*)\")|(?:([^\\s='\"<>]+)\\s*=\\s*'([^']*)')|(?:[^\\s='\"<>]+))"
   rexJustAttributes=new RegExp(exp)
   //rexJustSpaces=/^\s+$/
   exp="^\\s+$"
   rexJustSpaces=new RegExp(exp)
   //rexProtocol=/^\w+:\/\//
   exp="^\\w+:\\/\\/"
   rexProtocol=new RegExp(exp)
   //rexCommentSimpleG=/<!(?!\[)[\s\S]*?>/g
   exp="<!(?!\\[)[\\s\\S]*?>"
   rexCommentSimpleG=new RegExp(exp,"g")
   //rexCommentLongG=/<!--[\s\S]*?-->/g
   exp="<!--[\\s\\S]*?-->"
   rexCommentLongG=new RegExp(exp,"g")
   //rexXMLCommandG=/<\?[\s\S]*?>/g
   exp="<\\?[\\s\\S]*?>"
   rexXMLCommandG=new RegExp(exp,"g")
   //rexXMLDocTypeSBG=/<!DOCTYPE\b[^\s\S]*?>/gi
   exp="<!DOCTYPE\\b[^\\s\\S]*?>"
   rexXMLDocTypeSBG=new RegExp(exp,"gi")
   //rexXMLDocTypeG=/<!DOCTYPE\b[^\s\S]*?>/gi
   exp="<!DOCTYPE\\b[^\\s\\S]*?>"
   rexXMLDocTypeG=new RegExp(exp,"gi")
   //rexTagG=/<[^\s='"<>!]+(?:\s+|(?:[^\s='"<>]+\s*=\s*[^\s='"<>]+)|(?:[^\s='"<>]+\s*=\s*"[^"]*")|(?:[^\s='"<>]+\s*=\s*'[^']*')|(?:[^\s='"<>]+))*?>/g
   exp="<[^\\s='\"<>!]+(?:\\s+|(?:[^\\s='\"<>]+\\s*=\\s*[^\\s'\"<>]+)|(?:[^\\s='\"<>]+\\s*=\\s*\"[^\"]*\")|(?:[^\\s='\"<>]+\\s*=\\s*'[^']*')|(?:[^\\s='\"<>]+))*?>"
   rexTagG=new RegExp(exp,"g")
   //rexScriptTagContentsG=/<script\b(?:\s+|(?:[^\s='"<>]+\s*=\s*[^\s='"<>]+)|(?:[^\s='"<>]+\s*=\s*"[^"]*")|(?:[^\s='"<>]+\s*=\s*'[^']*')|(?:[^\s='"<>]+))*?>[\s\S]*?<\/script>/gi
   exp="<script\\b(?:\\s+|(?:[^\\s='\"<>]+\\s*=\\s*[^\\s'\"<>]+)|(?:[^\\s='\"<>]+\\s*=\\s*\"[^\"]*\")|(?:[^\\s='\"<>]+\\s*=\\s*'[^']*')|(?:[^\\s='\"<>]+))*?>[\\s\\S]*?<\\/script>"
   rexScriptTagContentsG=new RegExp(exp,"gi")
   //rexStyleTagContentsG=/<style\b(?:\s+|(?:[^\s='"<>]+\s*=\s*[^\s='"<>]+)|(?:[^\s='"<>]+\s*=\s*"[^"]*")|(?:[^\s='"<>]+\s*=\s*'[^']*')|(?:[^\s='"<>]+))*?>[\s\S]*?<\/script>/gi
   exp="<style\\b(?:\\s+|(?:[^\\s='\"<>]+\\s*=\\s*[^\\s'\"<>]+)|(?:[^\\s='\"<>]+\\s*=\\s*\"[^\"]*\")|(?:[^\\s='\"<>]+\\s*=\\s*'[^']*')|(?:[^\\s='\"<>]+))*?>[\\s\\S]*?<\\/script>"
   rexStyleTagContentsG=new RegExp(exp,"gi")
   //rexCDataOrTag=/<!\[CDATA\[.*?\]\]>|<[^\s='"<>!]+(?:\s+|(?:[^\s='"<>]+\s*=\s*[^\s='"<>]+)|(?:[^\s='"<>]+\s*=\s*"[^"]*")|(?:[^\s='"<>]+\s*=\s*'[^']*')|(?:[^\s='"<>]+))*?>/
   exp="<!\\[CDATA\\[[\\s\\S]*?\\]\\]>|<[^\\s='\"<>!]+(?:\\s+|(?:[^\\s='\"<>]+\\s*=\\s*[^\\s'\"<>]+)|(?:[^\\s='\"<>]+\\s*=\\s*\"[^\"]*\")|(?:[^\\s='\"<>]+\\s*=\\s*'[^']*')|(?:[^\\s='\"<>]+))*?>"
   rexCDataOrTag=new RegExp(exp)
   return true
}



//strip tags function
function rrpStripTags(text) {
   var out=text
   out=out.replace(rexXMLCommandG,"")
   out=out.replace(rexXMLDocTypeSBG,"")
   out=out.replace(rexXMLDocTypeG,"")
   out=out.replace(rexScriptTagContentsG,"")
   out=out.replace(rexStyleTagContentsG,"")
   out=out.replace(rexCommentSimpleG,"")
   out=out.replace(rexCommentLongG,"")
   out=out.replace(rexTagG,"")
   out=out.replace(/(\s)+/g,"$1")
   return out
}

// html entities
function rrpReverseCharCodes(text) {
   var out=text
   var p=out.indexOf('&#');
   while (p>-1) {
      var ps=out.indexOf(';',p+1);
      if (ps==-1) {
         return out;
      } else {
         var x=out.substring(p+2,p+3)
         //xml &#xAAAA;
         if (x=="x") {
            var code=out.substring(p+3,ps)
            code=parseInt(code,16)
         } else {
            var code=out.substring(p+2,ps)
         }
         var newchar=String.fromCharCode(code)
         var out=out.substring(0,p)+newchar+out.substring(ps+1,out.length)
      }
      p=out.indexOf('&#',p+1);
   }
   return out;
}

function rrpHtmlEntities(text) {
   var out=text
   //var enc=getDreamweaverTextEncoding()
   out=out.replace(/&/g,"&"+"amp;")
   out=out.replace(/</g,"&"+"lt;")
   out=out.replace(/>/g,"&"+"gt;")
   out=out.replace(/"/g,"&"+"quot;")
   out=out.replace(/'/g,"&"+"apos;")
   var comp=out
   out=""
	for (var i = 0; i < comp.length; i++ ) {
		var ch = comp.charAt(i);
		var charCode = ch.charCodeAt(0);
	    
	    //if ((charCode > 255) && (htmlEntitiesUseLargeNums)) {
	    //   out+="&#"+charCode+";"
	    //} else if (charCode > 255) {
	    //   out+=ch
	    //} else 
	    
	    if (charCode > 255) {
	       out+=ch
	    } else if (charCode > 127) {
	       out+="&#"+charCode+";"
	    } else {
	       out+=ch
	    }
	}
   return out
}

function rrpHtmlDecode(text) {
   var out=text
   out=rrpReverseCharCodes(out)
   out=out.replace(/&lt;/g,"<")
   out=out.replace(/&gt;/g,">")
   out=out.replace(/&quot;/g,'"')
   out=out.replace(/&apos;/g,"'")
   out=out.replace(/&amp;/g,"&")
   return out
}


// RSSReplay Object Creation --------------------------

function rssReplay() {
   //localizable string
   this.localStringLoading="RSS feeds are loading"
   this.localStringNoDisplaySettings="RSS Replay has No Display Settings"
   this.localStringLoadError="An error occured when getting feed"
   this.localString404FileError="The file is not on the server"
   this.localString404ProxyError="The proxy is not on the server"
   this.localStringBrowserError="RSS Replay does not work in this web browser"
   this.localStringNoStoriesError="There are no stories to display"
   this.localStringEmptyFeedError="The feed was empty"
   this.localStringUnrecognizedFormatError="Unrecognized format for feed"
   this.localStringNotParsedError="The feed could not be not parsed as XML"
   this.localStringReplayFeedParamError="Parameters Error in rssReplayFeed"
   this.localStringMadeWithReplay="Made with RSS Replay"
   this.localStringProxyErrorPrefix="RSS Replay Proxy Error:"
   //initial properties and method assignment
   this.isIE = false
   this.supressErrors=false
   this.id="rssReplayDefaultID"
   this.errorFlag="RRPERROR:"
   this.proxyHref=""
   //can also be set in the PHP/ASP file
   this.proxyCache=true
   this.proxyCacheTTL=-1
   this.sources=Array()
   this.sourcePages=Array()
   this.sourceXMLs=Array()
   this.sourcesLoading=Array()
   this.sourceRequests=Array()
   this.displayPrefix=""
   this.displayHeadline=""
   this.displayHeadlineClass=""
   this.displayStory=""
   this.displayStoryClass=""
   this.displaySuffix=""
   this.displayStoriesMax=0
   this.loadingString=rssReplay_loadingString
   this.display=rssReplay_display
   this.displayDate=rssReplay_displayDate
   this.displayStripTags=rssReplay_displayStripTags
   this.displayTrimText=rssReplay_displayTrimText
   this.displayClassDotCheck=rssReplay_displayClassDotCheck
   this.sortByDate=rssReplay_sortByDate
   this.sortReverseByDate=rssReplay_sortReverseByDate
   this.displayParse=rssReplay_displayParse
   this.displayPrefixSuffixParse=rssReplay_displayPrefixSuffixParse
   this.displayPostLoad=rssReplay_displayPostLoad
   this.countSourcesLoading=rssReplay_countSourcesLoading
   this.addSource=rssReplay_addSource
   this.loadXMLDoc=rssReplay_loadXMLDoc
   this.processReqChange=rssReplay_processReqChange
   
   this.stories=Array()
   this.parse=rssReplay_parse
   this.parserGetDate=rssReplay_parserGetDate
   this.parserPrepText=rssReplay_parserPrepText
   
   this.startupRegExpError=!rrpSetupRegExp()
}

// Object Methods ---------------------------

function rssReplay_loadingString() {
   //localize this
   return this.localStringLoading+" ("+this.countSourcesLoading()+")"
}

function rssReplay_displayDate(theFormat, theDate) {
   var daylist=Array("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday")
   var monthlist=Array("January","February","March","April","May","June","July","August","September","October","November","December")
   var intext=theFormat
   var out=""
   //c = ISO-8601 format = 2004-02-12T15:19:21+00:00
   intext=intext.replace(/c/,"Y-m-dTH:i:sP")
   //r = RFC-2822 format = Thu, 21 Dec 2006 16:01:07 +0200
   intext=intext.replace(/r/,"D, j M Y H:i:s O")
   var re=/[dDjlNSwFmMnYyaAgGhHisOPZU]/
   if (!re.test(intext)) {
      return intext
   }
   var ro=re.exec(intext)
   while (ro) {
      var temp=""
      var temp2=""
      out+=intext.substring(0,ro.index)
      //control formatting
      switch(ro[0]) {
         //day formatting
         case "d": //day of month, 2 digits with leading 0 - 01 to 31
            temp="00"+theDate.getDate()
            out+=temp.substring(temp.length-2, temp.length)
            break
         case "D": //short textual representation of day, 3 letters
            temp=daylist[theDate.getDay()]
            out+=temp.substring(0,3)
            break
         case "j": //day of month, no leading 0 - 1 to 31
            out+=theDate.getDate()
            break
         case "l": //full  textual representation of day
            out+=daylist[theDate.getDay()]
            break
         case "N": //ISO-8601 num rep of day of week  - 1(Mon) to 7(Sun)
            temp=theDate.getDay()
            if (temp==0) {temp=7}
            out+=temp
            break
         case "S": //ordinal suffix for day of month 2 chars - 1(st),2(nd),3(rd),4(th)
            temp="00"+theDate.getDate()
            temp=temp.substring(temp.length-1,temp.length)
            if ((theDate.getDate()>=11) && (theDate.getDate()<=13)) {temp="th"} //11th 12th 13th
            switch(temp) {
               case "1":
                  temp="st"
                  break
               case "2":
                  temp="nd"
                  break
               case "3":
                  temp="rd"
                  break
               default: temp="th"
            }
            out+=temp
            break
         case "w": //num rep of day of week  - 0(Sun) to 6(Sat)
            out+=theDate.getDay()
            break
         //month formatting
         case "F": //full  textual representation of month
            out+=monthlist[theDate.getMonth()]
            break
         case "m": //numeric representation of month, leading 0
            temp="00"+theDate.getMonth()
            out+=temp.substring(temp.length-2, temp.length)
            break
         case "M": //short textual representation of month, 3 letters
            temp=monthlist[theDate.getMonth()]
            out+=temp.substring(0,3)
            break
         case "n": //numeric representation of month, no leading 0
            out+=theDate.getMonth()
            break
         //year formatting
         case "Y": //4 digit representation of year
            out+=theDate.getFullYear()
            break
         case "y": //2 digit representation of year
            temp="00"+theDate.getFullYear()
            out+=temp.substring(4,6)
            break
         //time formatting
         case "a": //am or pm
            temp=theDate.getHours()
            if (temp>=12) {temp="pm"}
            else {temp="am"}
            out+=temp
            break
         case "A": //AM or PM
            temp=theDate.getHours()
            if (temp>=12) {temp="PM"}
            else {temp="AM"}
            out+=temp
            break
         case "g": //12 hour format of hour no leading 0 - 1 to 12
            temp=theDate.getHours()
            if (temp>12) {temp=temp-12}
            if (temp==0) {temp="12"}
            out+=temp
            break
         case "G": //24 hour format of hour with no leading 0 - 0 to 23
            out+=theDate.getHours()
            break
         case "h": //12 hour format of hour with leading 0 - 01 to 12
            temp=theDate.getHours()
            if (temp>12) {temp=temp-12}
            if (temp==0) {temp="12"}
            temp="00"+temp
            out+=temp.substring(temp.length-2, temp.length)
            break
         case "H": //24 hour format of hour with  leading 0 - 00 to 23
            temp=theDate.getHours()
            temp="00"+temp
            out+=temp.substring(temp.length-2, temp.length)
            break
         case "i": //minutes  leading 0 - 00 to 59
            temp=theDate.getMinutes()
            temp="00"+temp
            out+=temp.substring(temp.length-2, temp.length)
            break
         case "s": //seconds  leading 0 - 00 to 59
            temp=theDate.getSeconds()
            temp="00"+temp
            out+=temp.substring(temp.length-2, temp.length)
            break
         case "U": //seconds since epoch
            temp=Date.UTC(theDate.getUTCFullYear(),theDate.getUTCMonth(),theDate.getUTCDate(),theDate.getUTCHours(),theDate.getUTCMinutes(),theDate.getUTCSeconds(),theDate.getUTCMilliseconds())
            //careful - this is milliseconds - convert to seconds
            temp=Math.floor(temp/1000)
            out+=temp
            break
         //timezone formatting
         case "O": //GMT time zone offset presented as +0200
            temp=theDate.getTimezoneOffset()
            if (temp<0) {out+="-";temp=temp*-1;} else {out+="+"}
            temp2="00"+Math.floor(temp/60)
            out+=temp2.substring(temp2.length-2,temp2.length)
            temp2="00"+(temp%60)
            out+=temp2.substring(temp2.length-2,temp2.length)
            break
         case "P": //GMT time zone offset presented as +02:00
            temp=theDate.getTimezoneOffset()
            if (temp<0) {out+="-";temp=temp*-1;} else {out+="+"}
            temp2="00"+Math.floor(temp/60)
            out+=temp2.substring(temp2.length-2,temp2.length)
            out+=":"
            temp2="00"+(temp%60)
            out+=temp2.substring(temp2.length-2,temp2.length)
            break
         case "Z": //GMT time zone offset in seconds (mintues+00)
            temp=theDate.getTimezoneOffset()+"00"
            out+=temp
            break
      }
      intext=intext.substring(ro.index+ro[0].length,intext.length)
      ro=re.exec(intext)
      if ((!ro) && (intext!="")) {
         out+=intext
      }
   }
   return out
}

function rssReplay_displayStripTags(text) {
   //commented out the things that shouldn't be in the content
   //rexXMLCommandG=/<\?[\s\S]*?>/g
   //rexXMLDocTypeSBG=/<!DOCTYPE\b[^\s\S]*?>/gi
   //rexXMLDocTypeG=/<!DOCTYPE\b[^\s\S]*?>/gi
   //var rexScriptTagContentsG=/<script\b(?:\s+|(?:[^\s='"<>]+\s*=\s*[^\s='"<>]+)|(?:[^\s='"<>]+\s*=\s*"[^"]*")|(?:[^\s='"<>]+\s*=\s*'[^']*')|(?:[^\s='"<>]+))*?>[\s\S]*?<\/script>/gi
   //var rexCommentSimpleG=/<![\s\S]*?>/g
   //var rexCommentLongG=/<!--[\s\S]*?-->/g
   //var rexTagG=/<[^\s='"<>]+(?:\s+|(?:[^\s='"<>]+\s*=\s*[^\s='"<>]+)|(?:[^\s='"<>]+\s*=\s*"[^"]*")|(?:[^\s='"<>]+\s*=\s*'[^']*')|(?:[^\s='"<>]+))*?>/g

   var out=text
   out=out.replace(rexXMLCommandG,"")
   out=out.replace(rexXMLDocTypeSBG,"")
   out=out.replace(rexXMLDocTypeG,"")
   out=out.replace(rexScriptTagContentsG,"")
   out=out.replace(rexStyleTagContentsG,"")
   out=out.replace(rexCommentSimpleG,"")
   out=out.replace(rexCommentLongG,"")
   out=out.replace(rexTagG,"")
   out=out.replace(/(\s)+/g,"$1")
   return out
}

function rssReplay_displayTrimText(type, count, text) {
   var out=text
   out=this.displayStripTags(out)
   //type: w(ords) or c(haracters)
   if (type=="w") {
      out=out.replace(/\s+/g," ")
      var outstack=out.split(" ")
      if (outstack.length>count) {
         outstack=outstack.slice(0,count)
         out=outstack.join(" ")+"..."
      }
   } else if (type=="c") {
      if (out.length>count) {
         out=out.substring(0,count)+"..."
      }
   }
   return out
}

function rssReplay_displayClassDotCheck(text) {
   var out=text
   if (out=="") { return out; }
   if (out.substring(0,1)==".") {
      out=out.substring(1,out.length)
   }
   return out
}

function rssReplay_displayPrefixSuffixParse(text) {
   var intext=text
   if (intext=="") { return intext }
   var out=""
   var exp="<<([^>]*?)>>"
   try {var re=new RegExp(exp) }
   catch(err) { return intext }
   if (!re.test(intext)) {
      return intext
   }
   //now do the job with <<XXX>>
   var ro=re.exec(intext)
   while (ro) {
      out+=intext.substring(0,ro.index)
	  if (ro[1].substring(0,5)=="date_") {
		 out+=this.displayDate(ro[1].substring(5,ro[1].length),new Date())
	  } else if (ro[1]=="madewithrssreplay") {
		 out+=this.localStringMadeWithReplay
	  } else {
		 out+=ro[0]
	  }
      intext=intext.substring(ro.index+ro[0].length,intext.length)
      //intext=""
      ro=re.exec(intext)
      if ((!ro) && (intext!="")) {
         out+=intext
      }
   }
   return out
}

function rssReplay_displayParse(text,story) {
   var intext=text
   if (intext=="") { return intext }
   var out=""
   var exp="<<([^>]*?)>>"
   try {var re=new RegExp(exp) }
   catch(err) { return intext }
   if (!re.test(intext)) {
      return intext
   }
   //replace <<headline>>:<<title>> and <<story>>:<<description>>
   exp="<<headline>>"
   var r2=new RegExp(exp,"g")
   intext=intext.replace(r2,"<<title>>")
   exp="<<story>>"
   var r2=new RegExp(exp,"g")
   intext=intext.replace(r2,"<<description>>")
   exp="<<([^>]*?)_headline>>"
   var r2=new RegExp(exp,"g")
   intext=intext.replace(r2,"<<$1_title>>")
   exp="<<([^>]*?)_story>>"
   var r2=new RegExp(exp,"g")
   intext=intext.replace(r2,"<<$1_description>>")
   //now do the job with <<XXX>>
   var ro=re.exec(intext)
   while (ro) {
      out+=intext.substring(0,ro.index)
	  if (ro[1].substring(0,5)=="date_") {
		 out+=this.displayDate(ro[1].substring(5,ro[1].length),story.date)
	  } else if (ro[1].substring(0,5)=="trim_") {
	     var i=ro[1].indexOf("_",5)
	     if (i>-1) {
	        var n=ro[1].substring(5,i-1)
	        var t=ro[1].substring(i-1,i)
		    var l=ro[1].substring(i+1,ro[1].length)
			if (story[l]) {
			   out+=this.displayTrimText(t,n,story[l]) }
		 }
	  } else if (ro[1].substring(0,6)=="strip_") {
		 var l=ro[1].substring(6,ro[1].length)
		 if (story[l]) {
		    out+=this.displayStripTags(story[l])}
	  } else if (ro[1]=="headlineclass") {
	     if (this.displayHeadlineClass!="") {
		    out+='class="'+this.displayClassDotCheck(this.displayHeadlineClass)+'"' }
	  } else if (ro[1]=="storyclass") {
	     if (this.displayStoryClass!="") {
		    out+='class="'+this.displayClassDotCheck(this.displayStoryClass)+'"' }
	  } else if (ro[1]=="enclosureorlink") {
	     if (story.enclosure!="") {
		    out+=story.enclosure }
	     else if (story.link!="") {
		    out+=story.link }
	  } else if (ro[1]=="thumbnailorfirstimage") {
	     if (story.thumbnail!="") {
		    out+=story.thumbnail }
	     else if (story.firstimage!="") {
		    out+=story.firstimage }
	  } else if (story[ro[1]]!=null) {
		 out+=story[ro[1]]
	  } else {
		 out+=ro[0]
	  }
      intext=intext.substring(ro.index+ro[0].length,intext.length)
      //intext=""
      ro=re.exec(intext)
      if ((!ro) && (intext!="")) {
         out+=intext
      }
   }
   return out
}

function rssReplay_sortByDate(a,b) {
   //a and b are story objects with .date properties
   //.date can be converted to milliseconds UTC and substracted
   if ((a.date) && (b.date) && (a.date!=null) && (b.date!=null)) {
	  var atemp=Date.UTC(a.date.getUTCFullYear(),a.date.getUTCMonth(),a.date.getUTCDate(),a.date.getUTCHours(),a.date.getUTCMinutes(),a.date.getUTCSeconds(),a.date.getUTCMilliseconds())
	  var btemp=Date.UTC(b.date.getUTCFullYear(),b.date.getUTCMonth(),b.date.getUTCDate(),b.date.getUTCHours(),b.date.getUTCMinutes(),b.date.getUTCSeconds(),b.date.getUTCMilliseconds())
	  return (atemp-btemp)
   }
   return 0
}

function rssReplay_sortReverseByDate(a,b) {
   //a and b are story objects with .date properties
   //.date can be converted to milliseconds UTC and substracted
   if ((a.date) && (b.date) && (a.date!=null) && (b.date!=null)) {
	  var atemp=Date.UTC(a.date.getUTCFullYear(),a.date.getUTCMonth(),a.date.getUTCDate(),a.date.getUTCHours(),a.date.getUTCMinutes(),a.date.getUTCSeconds(),a.date.getUTCMilliseconds())
	  var btemp=Date.UTC(b.date.getUTCFullYear(),b.date.getUTCMonth(),b.date.getUTCDate(),b.date.getUTCHours(),b.date.getUTCMinutes(),b.date.getUTCSeconds(),b.date.getUTCMilliseconds())
	  //if they are the same leave them in original order
	  if (btemp-atemp==0) {
	     return (a.origIndex-b.origIndex)
	  }
	  return (btemp-atemp)
   }
   return 0
}

function rssReplay_displayPostLoad(num) {
   //check if I have already loaded this one - it might be a product of onload and onreadystatechange being called together
   if (this.sourcesLoading[num]==0) { return; }
   this.sourcesLoading[num]=0
   var l=this.countSourcesLoading()
   if (l>0) {
      //still loading
      if (document.getElementById) {
         document.getElementById(this.id).innerHTML=this.loadingString()
      }
   } else {
      //done loading - format and place in layout
      if (document.getElementById) {
         var out=""
         //create stories and capture errors at the top of the space
		 var i=0
		 var j=this.sources.length
		 var p=""
		 if (j>0) {
			for (i=0; i<j; i++) {
			   if (this.sourcePages[i].substring(0,this.errorFlag.length)==this.errorFlag) {
				  if (!this.supressErrors) {
					 out+="<p style='color: red; font-weight: bold;'>"+
						this.sourcePages[i].substring(this.errorFlag.length,this.sourcePages[i].length)+"</p>"
				  }
			   } else {
				  p=this.parse(i)
				  if ((p) && (!this.supressErrors)) {
					 out+="<p style='color: red; font-weight: bold;'>"+p+"</p>"
				  }
			   }
			}
		 }
         //loop through the stories, sort by date, and then format them
		 var i=0
		 var j=this.stories.length
		 out+=this.displayPrefixSuffixParse(this.displayPrefix)
		 if (j>0) {
		    //sort the array using the new function
		    this.stories.sort(this.sortReverseByDate)
			for (i=0; i<j; i++) {
			   //out+=this.stories[i].title+"<br />"
			   if ((this.displayStoriesMax==0) || (i<this.displayStoriesMax)) {
				  if (this.displayHeadline!="") {
					 out+=this.displayParse(this.displayHeadline,this.stories[i])
				  }
				  if (this.displayStory!="") {
					 out+=this.displayParse(this.displayStory,this.stories[i])
				  }
			   }
			}
		 } else {
		    out+=this.localStringNoStoriesError
		 }
		 out+=this.displayPrefixSuffixParse(this.displaySuffix)
         document.getElementById(this.id).innerHTML=out
      }
   }
}

function rssReplay_display() {
   if (this.startupRegExpError) {
	  document.write("<div id='"+this.id+"'>"+this.localStringBrowserError+"</div>")
   } else {
	  document.write("<div id='"+this.id+"'>"+this.loadingString()+"</div>")
	  var i=0
	  var j=this.sources.length
	  for (i=0; i<j; i++) {
		 if (this.sources[i]!="") {
			this.loadXMLDoc(i)
		 }
	  }
   }
}

function rssReplay_countSourcesLoading() {
   var out=0
   var i=0
   var j=this.sourcesLoading.length
   if (j>0) {
      for (i=0; i<j; i++) {
         out+=this.sourcesLoading[i]
      }
   }
   return out
}

function rssReplay_addSource(src) {
   var href=src
   var proto=src.toLowerCase()
   var num=this.sources.length
   if ((proto.substring(0,7)=="http://") || (proto.substring(0,8)=="https://")){
      if (this.proxyHref!="") {
		 href=this.proxyHref+"?"
		 if (this.proxyCache) { href=href+"cache=true&" }
		 if ((this.proxyCache) && (this.proxyCacheTTL>-1)) { href=href+"cachettl="+this.proxyCacheTTL+"&" }
		 href=href+"request="
		 href=href+escape(src)
      }
   } 
   this.sources[num]=href
   this.sourcePages[num]=""
   this.sourceXMLs[num]=""
   this.sourcesLoading[num]=1
}


// retrieve XML document (reusable generic function);
// parameter is URL string (relative or complete) to
// an .xml file whose Content-Type is a valid XML
// type, such as text/xml; XML source must be from
// same domain as HTML file

function rssReplay_loadXMLDoc(num) {
    var url=this.sources[num]
    var iam=this
    // branch for native XMLHttpRequest object
    if (window.XMLHttpRequest) {
        this.sourceRequests[num] = new XMLHttpRequest();
		this.sourceRequests[num].onreadystatechange = function() {rssReplay_processReqChange(iam,num)};
		//NS6 uses onload in multiple request situations - but so do others so there may be overlap
		this.sourceRequests[num].onload = function() {rssReplay_processReqChange(iam,num)};
        this.sourceRequests[num].open("GET", url, true);
        this.sourceRequests[num].send(null);
    // branch for IE/Windows ActiveX version
    } else if (window.ActiveXObject) {
        isIE = true;
        //use try because it might not work with older IE/Mac IE
        try {this.sourceRequests[num] = new ActiveXObject("Microsoft.XMLHTTP")} 
        catch(err) {this.sourceRequests[num] = null}
        if (this.sourceRequests[num]) {
            this.sourceRequests[num].onreadystatechange = function() {rssReplay_processReqChange(iam,num)};
            this.sourceRequests[num].open("GET", url, true);
            this.sourceRequests[num].send();
        } else {
            //error message - no HTTP object
            this.sourcePages[num]=this.errorFlag + this.localStringBrowserError
            this.displayPostLoad(num)
        }
    } else {
	  //error message - no HTTP object
	  this.sourcePages[num]=this.errorFlag + this.localStringBrowserError
	  this.displayPostLoad(num)
    }
}

// handle onreadystatechange event of req object
function rssReplay_processReqChange(obj,num) {
    // only if req shows "loaded"
    // N6 has no readyState property
    var docHref=document.location.href
    docHref=docHref.substring(0,5)
    var url=obj.sources[num]
    url=url.substring(0,5)
    if (obj.sourceRequests[num].readyState) {
	   if (obj.sourceRequests[num].readyState == 4) {
		   // only if "OK"
		   if (obj.sourceRequests[num].status == 200) {
			   obj.sourcePages[num]=obj.sourceRequests[num].responseText
			   obj.sourceXMLs[num]=obj.sourceRequests[num].responseXML
			   obj.displayPostLoad(num)
			} else if ((docHref.toLowerCase()=="file:") && (url.toLowerCase()!="http:")) {
			   //local load so don't worry about status
			   obj.sourcePages[num]=obj.sourceRequests[num].responseText
			   obj.sourceXMLs[num]=obj.sourceRequests[num].responseXML
			   obj.displayPostLoad(num)
			} else if (obj.sourceRequests[num].status == 404) {
			   //localize error message - proxy/file is missing
			   obj.sourcePages[num]=obj.errorFlag +
				  obj.localStringLoadError+" "+num+":\n"+
				  obj.sources[num]+
				  ":\n" + obj.sourceRequests[num].status+" "
			   if (obj.sources[num].indexOf("rssReplayProxy")==-1) {
				  obj.sourcePages[num]+=obj.localString404FileError
			   } else {
				  obj.sourcePages[num]+=obj.localString404ProxyError
			   }
			   obj.displayPostLoad(num)
			} else {
			   //localize error message
			   obj.sourcePages[num]=obj.errorFlag +
				  obj.localStringLoadError+" "+num+":\n"+
				  obj.sources[num]+
				  ":\n" + obj.sourceRequests[num].status+" "+obj.sourceRequests[num].statusText
			   obj.displayPostLoad(num)
			}
	   }
	} else if (obj.sourceRequests[num].status) {
	    //works with N6 and onload
		if (obj.sourceRequests[num].status == 200) {
			obj.sourcePages[num]=obj.sourceRequests[num].responseText
			obj.sourceXMLs[num]=obj.sourceRequests[num].responseXML
			obj.displayPostLoad(num)
		 } else {
			//localize error message
			obj.sourcePages[num]=obj.errorFlag +
			   obj.localStringLoadError+" "+num+":\n"+
			   obj.sources[num]+
			   ":\n" + obj.sourceRequests[num].status+" "+obj.sourceRequests[num].statusText
			obj.displayPostLoad(num)
		 }
    } else {
       obj.sourcePages[num]=obj.errorFlag + obj.localStringBrowserError
    }
}


// Story Object Creation --------------------------
function rssReplayStory() {
   //elements
   this.title=""
   this.link=""
   this.description=""
   this.enclosure=""
   this.thumbnail=""
   this.firstimage=""
   this.origIndex=0
   this.date=rssReplayCurrentDate
   //this.date=new Date()
}

// Parse Methods ----------------------------------

function rssReplay_parserPrepText(po) {
   var attr=po.getAttribute("type")
   if (attr!=null) {
      //a03:text/plain:text/html:application/xhtml+xml
      //a10:text/html/xhtml
      if ((attr=="text") || (attr=="text/plain")) { 
         return po.innerXMLContent() }
      else { //assume its html escaped mode
         return rrpHtmlDecode(po.innerXMLContent()) }
   }
   return rrpHtmlDecode(po.innerXMLContent())
}

function rssReplay_parserGetDate(text) {
    //convert date string to a date
    //var re=/(\d+)-(\d+)-(\d+)[T\s](\d+):(\d+):(\d+)([\+\-\Z]*)(\d*):?(\d*)/
    var re=/(\d+)-(\d+)-(\d+)[T\s](\d+):(\d+):(\d+)\s*([\+\-Z]*)(\d*):?(\d*)/
    var out=new Date(text)
    //check to see if text failed to parse into a date object
    if (isNaN(out.getFullYear())) {
       if (re.test(text)) {
          var ro=re.exec(text)
          out=new Date("9/1/1973")
          out.setFullYear(ro[1])
          out.setMonth(ro[2]-1)
          out.setDate(ro[3])
          out.setHours(ro[4])
          out.setMinutes(ro[5])
          out.setSeconds(ro[6])
          //note: getTime and setTime are in milliseconds
          //note: getTimesonzeOffset is in minutes: m*60s*1000m
          if (ro[7]=="Z") {
             //adjust for GMT
             out.setTime(out.getTime()-(out.getTimezoneOffset()*60000))
          } else if (ro[7]=="+")  {
             if (ro[9]=="") {
                var tms=((60*ro[8].substring(0,2))+ro[8].substring(2,4))*600
             } else {
                var tms=((60*ro[8])+ro[9])*600
             }
             //adjust for positive offest
             out.setTime(out.getTime()-tms-(out.getTimezoneOffset()*60000))
          } else if (ro[7]=="-")  {
             if (ro[9]=="") {
                var tms=((60*ro[8].substring(0,2))+ro[8].substring(2,4))*600
             } else {
                var tms=((60*ro[8])+ro[9])*600
             }
             //adjust for negative offset
             out.setTime(out.getTime()+tms-(out.getTimezoneOffset()*60000))
          }
       } else {
          //doesn't work - so just use today
          out=new Date()
       }
    }
	return out
}

function rssReplay_parse(num) {
   //parser returns error strings
   //return this.localStringEmptyFeedError
   //this.localStringUnrecognizedFormatError
   //this.localStringEmptyFeedError
   var sourcetext=this.sourcePages[num]
   //var sourcexml=this.sourceXMLs[num]
   if (sourcetext=="") {
      return this.localStringEmptyFeedError+":\n"+this.sources[num]
   }
   //proxy error check
   if (sourcetext.substring(0,this.localStringProxyErrorPrefix.length)==this.localStringProxyErrorPrefix) {
      return sourcetext
   }
   //root options: rss|rdf:rdf|feed
   var rexRoot=/\<(rss|rdf:rdf|feed)\W/i
   if (!rexRoot.test(sourcetext)) {
      return this.localStringUnrecognizedFormatError+":\n"+this.sources[num]
   }
   var ro=rexRoot.exec(sourcetext)
   var rootTag=(ro[1]).toLowerCase()
   //remove the content before the tag (should be all the <?xml & <!DOCTYPE garbagio
   sourcetext=sourcetext.substring(ro.index,sourcetext.length)
   //now parse the dang thing
   var sourceParsed=new ParsedPage(sourcetext)
   var itemsTag="item"
   if (rootTag=="feed") { itemsTag="entry" }
   var items=sourceParsed.getElementsByTagName(itemsTag)
   if ((items==null) || (items.length==0)) {
      return this.localStringEmptyFeedError+":\n"+this.sources[num]
   }
   //now look for the content that I want
   var i=0
   var j=items.length
   //iterate across all items
   for (i=0; i<j; i++) {
	  var theStory=new rssReplayStory()
	  theStory.origIndex=i
	  var itemParsed=new ParsedPage(items[i].innerHTML())
	  //get the pieces
	  var t=itemParsed.getElementsByTagName("title")
	  if ((t!=null) && (t.length>0)) {
	     theStory.title=this.parserPrepText(t[0]) }
	  var t=itemParsed.getElementsByTagName("summary")
	  if ((t!=null) && (t.length>0)) {
	     theStory.description=this.parserPrepText(t[0]) }
	  var t=itemParsed.getElementsByTagName("description")
	  if ((t!=null) && (t.length>0)) {
	     theStory.description=this.parserPrepText(t[0]) }
	  var t=itemParsed.getElementsByTagName("content")
	  if ((t!=null) && (t.length>0)) {
	     theStory.description=this.parserPrepText(t[0]) }
	  var t=itemParsed.getElementsByTagName("content:encoded")
	  if ((t!=null) && (t.length>0)) {
	     theStory.description=this.parserPrepText(t[0]) }
	  var t=itemParsed.getElementsByTagName("pubdate")
	  if ((t!=null) && (t.length>0)) {
	     theStory.date=this.parserGetDate(t[0].innerXMLContent()) }
	  var t=itemParsed.getElementsByTagName("updated")
	  if ((t!=null) && (t.length>0)) {
	     theStory.date=this.parserGetDate(t[0].innerXMLContent()) }
	  var t=itemParsed.getElementsByTagName("published")
	  if ((t!=null) && (t.length>0)) {
	     theStory.date=this.parserGetDate(t[0].innerXMLContent()) }
	  var t=itemParsed.getElementsByTagName("dc:date")
	  if ((t!=null) && (t.length>0)) {
	     theStory.date=this.parserGetDate(t[0].innerXMLContent()) }
	  var t=itemParsed.getElementsByTagName("modified")
	  if ((t!=null) && (t.length>0)) {
	     theStory.date=this.parserGetDate(t[0].innerXMLContent()) }
	  var t=itemParsed.getElementsByTagName("issued")
	  if ((t!=null) && (t.length>0)) {
	     theStory.date=this.parserGetDate(t[0].innerXMLContent()) }
	  var t=itemParsed.getElementsByTagName("link")
	  if ((t!=null) && (t.length>0)) {
	     var k=0
	     for (k=0; k<t.length; k++) {
	        if (theStory.link=="") {
	           theStory.link=t[k].innerXMLContent()
	           if ((rootTag=="feed") && (t[k].getAttribute("href")!=null ) && 
	              ((t[k].getAttribute("rel")==null) || (t[k].getAttribute("rel")=="alternate")) ) { 
	              theStory.link=t[k].getAttribute("href") }
	        }
	        if (theStory.enclosure=="") {
			   if ((rootTag=="feed") && (t[k].getAttribute("rel")!=null) && (t[k].getAttribute("rel")=="enclosure")) { 
					theStory.enclosure=t[k].getAttribute("href") }
	        }
	     }
	  }
	  var t=itemParsed.getElementsByTagName("guid")
	  if ((t!=null) && (t.length>0)) {
	     if ((t[0].getAttribute("isPermaLink")!="false") && (theStory.link=="")) {
	        theStory.link=t[0].innerXMLContent() } }
	  //break out enclosure as its own thing
	  var t=itemParsed.getElementsByTagName("enclosure")
	  if ((t!=null) && (t.length>0)) {
	     if (t[0].getAttribute("url")!=null) { theStory.enclosure=t[0].getAttribute("url") } }
	  //specials - like a thumbnail
	  var t=itemParsed.getElementsByTagName("apple-wallpapers:thumbnail")
	  if ((t!=null) && (t.length>0)) {
	     theStory.thumbnail=t[0].innerXMLContent() }
	  //specials - firstimage
	  var descrParse=new ParsedPage(theStory.description)
	  var t=descrParse.getElementsByTagName("img")
	  if ((t!=null) && (t.length>0)) {
	     theStory.firstimage=t[0].getAttribute("src") }
	  else if (theStory.link!="") { 
	     var imgext=theStory.link.toLowerCase()
	     imgext=imgext.substring(imgext.lastIndexOf(".")+1,imgext.length)
	     if ((imgext=="gif") || (imgext=="png") || (imgext=="jpeg") || (imgext=="jpg") || (imgext=="bmp") ||
	         (imgext=="pdf") || (imgext=="swf") || (imgext=="mov")  || (imgext=="wmv")) {
	         theStory.thumbnail=theStory.link 
	     }
	  }
	  //if the story is not empty add it to the end of the stories array
	  if (theStory.title!="") {
		 this.stories[this.stories.length]=theStory
	  }
   }
}


// ParsedPage & ParseElement Object Creation --------------------------

// XML/HTML parser based on regular expressions
// Includes objects for ParsedPage and ParseElement (a tag/content parser)
// Version 1.5 - trimmed to core elements for Replay
// Coded by Ron Northrip 
// Jul 2006

//methods for parsing object
function ParseElement_isContent() {
   if (this.type==0) return true
   return false
}
function ParseElement_isTag() {
   if (this.type==1) return true
   return false
}
function ParseElement_isCloseTag() {
   if (this.type==-1) return true
   return false
}
function ParseElement_isCData() {
   if (this.type==-2) return true
   return false
}
function ParseElement_sourceCData() {
   var out=this.source
   if (this.type==-2) {
      out=out.substring(9,out.length-3)
      out=rrpHtmlEntities(out)
   }
   return out
}
function ParseElement_getTag() {
   var re=this.source.match(rexJustTag)
   if (re!=null) this.tag=re[1].toLowerCase()
}
function ParseElement_getCloseTag() {
   var re=this.source.match(rexJustTag)
   if (re!=null) this.tag=re[1].toLowerCase()
}
function ParseElement_getAttributes() {
   var text=this.source
   this.attributes=new Array
   this.casedAttributes=new Array
   if (text.substring(0,1)=="<") {
      //trim off < > & the tag
      text=text.substring(1,text.length-1)
      if (this.tag==null) {
         this.tag=""
         //alert(getLocalizedText("A serious error occured when parsing JavaScript in your HTML pages"))
      }
      if (this.tag!="") text=text.substring(this.tag.length,text.length)
      //now get the attributes
      var p=0
      var re=text.match(rexJustAttributes)
      while (re!=null) {
         //check for just spaces - not included in attributes list
         if (! re[0].match(rexJustSpaces)) {
            if ((re[1]) && (re[1]!="")) {
               this.attributes[re[1].toLowerCase()]=re[2].toLowerCase()
               this.casedAttributes[re[1].toLowerCase()]=re[2]
            } else if ((re[3]) && (re[3]!="")) {
               this.attributes[re[3].toLowerCase()]=re[4].toLowerCase()
               this.casedAttributes[re[3].toLowerCase()]=re[4]
            } else if ((re[5]) && (re[5]!="")) {
               this.attributes[re[5].toLowerCase()]=re[6].toLowerCase()
               this.casedAttributes[re[5].toLowerCase()]=re[6]
            } else {
               this.attributes[re[0].toLowerCase()]=""
               this.casedAttributes[re[0].toLowerCase()]=""
            }
         }
         //skip position and rematch
         p=re.index+re[0].length
         text=text.substring(p,text.length)
         re=text.match(rexJustAttributes)
      }
   }
}
function ParseElement_getAttribute(attr) {
   if (this.isTag()) {
      if (this.attributes[attr]!=null) {
         return this.attributes[attr]
      }
   }
   return null
}
function ParseElement_getCasedAttribute(attr) {
   if (this.isTag()) {
      if (this.casedAttributes[attr]!=null) {
         return this.casedAttributes[attr]
      }
   }
   return null
}
function ParseElement_setAttribute(attr,val) {
   if (this.isTag()) {
      //set in list
      var attrlower=attr.toLowerCase()
      this.attributes[attrlower]=val
      //change the source
      var text=this.source
      var op=0
      var mp=-1
      var ml=-1
      //now find the attribute to change
      var p=0
      var re=text.match(rexJustAttributes)
      while (re!=null) {
         //check for just spaces - not included in attributes list
         if (! re[0].match(rexJustSpaces)) {
            if ((re[1]) && (re[1]!="")) { //no quotes
               if (re[1].toLowerCase()==attr.toLowerCase()) { mp=op+re.index; ml=re[0].length }
            } else if ((re[3]) && (re[3]!="")) { //dble quotes
               if (re[3].toLowerCase()==attr.toLowerCase()) { mp=op+re.index; ml=re[0].length }
            } else if ((re[5]) && (re[5]!="")) { //single quotes
               if (re[5].toLowerCase()==attr.toLowerCase()) { mp=op+re.index; ml=re[0].length }
            } else { //just the word
               if (re[0].toLowerCase()==attr.toLowerCase()) { mp=op+re.index; ml=re[0].length }
            }
         }
         //skip position and rematch
         p=re.index+re[0].length
         op=op+p
         text=text.substring(p,text.length)
         if (mp==-1) { re=text.match(rexJustAttributes) }
         else { re=null }
      }
      //if there's no attribute then add it to the end of the source
      //otherwise change the source at the mp, with ml length
      text=this.source
      if (mp==-1) {
         p=text.lastIndexOf(">")
         if (p.substring(p-1,p)=="/") p=p-1
         p=p-1
         var start=text.substring(0,p)
         var end=text.substring(p,text.length)
         if (val=="") { //no content
            this.source=start+" "+attr+" "+end
         } else if (val.indexOf('"')==-1) { //no double
            this.source=start+" "+attr+'="'+val+'" '+end
         } else { //use singles
            this.source=start+" "+attr+"='"+val+"' "+end
         }
      } else {
         var start=text.substring(0,mp)
         var end=text.substring(mp+ml,text.length)
         if (val=="") { //no content
            this.source=start+attr+end
         } else if (val.indexOf('"')==-1) { //no double
            this.source=start+attr+'="'+val+'"'+end
         } else { //use singles
            this.source=start+attr+"='"+val+"'"+end
         }
      }
   }
}
function ParseElement_innerHTML() {
   if (this.isTag()) {
	  if (this.page!=null) {
	     var parseStack=this.page.parseStack
		 if (parseStack.length>0) {
		    var out=""
			var i=0
			var j=parseStack.length
			if (parseStack[this.index]!=this) {
			   while ((parseStack[i]!=this) && (i<j)) {i++}
			   this.index=i
			}
			i=this.index+1
			var opens=1
			while ((opens>0) && (i<j)) {
			   if (parseStack[i].tag==this.tag) {
			      if (parseStack[i].isCloseTag()) {opens--}
			      else if (parseStack[i].isTag()) {opens++}
			   }
			   if (opens>0) {out+=parseStack[i].source}
			   if (opens==0) {this.closeIndex=i}
			   i++
			}
			if (opens>0) {
			   //unmatched close
			   out=this.source
			}
			return out
		 }
	  }
   } else {
      return this.source
   }
}
function ParseElement_innerXMLContent() {
   if (this.isTag()) {
	  if (this.page!=null) {
	     var parseStack=this.page.parseStack
		 if (parseStack.length>0) {
		    var out=""
			var i=0
			var j=parseStack.length
			if (parseStack[this.index]!=this) {
			   while ((parseStack[i]!=this) && (i<j)) {i++}
			   this.index=i
			}
			i=this.index+1
			var opens=1
			while ((opens>0) && (i<j)) {
			   if (parseStack[i].tag==this.tag) {
			      if (parseStack[i].isCloseTag()) {opens--}
			      else if (parseStack[i].isTag()) {opens++}
			   }
			   if (opens>0) {
			      if (parseStack[i].isCData()) {
			         out+=parseStack[i].sourceCData()
			      } else {
			         out+=parseStack[i].source
			      }
			   }
			   if (opens==0) {this.closeIndex=i}
			   i++
			}
			if (opens>0) {
			   //unmatched close
			   out=""
			}
			return out
		 }
	  }
   } else {
      return ""
   }
}


function ParseElement_outerHTML() {
   if (this.isTag()) {
	  if (this.page!=null) {
	     var parseStack=this.page.parseStack
		 if (parseStack.length>0) {
		    var out=this.source
			var i=0
			var j=parseStack.length
			if (parseStack[this.index]!=this) {
			   while ((parseStack[i]!=this) && (i<j)) {i++}
			   this.index=i
			}
			i=this.index+1
			var opens=1
			while ((opens>0) && (i<j)) {
			   if (parseStack[i].tag==this.tag) {
			      if (parseStack[i].isCloseTag()) {opens--}
			      else if (parseStack[i].isTag()) {opens++}
			   }
			   out+=parseStack[i].source
			   i++
			}
			this.closeIndex=i
			return out
		 }
	  }
   } else {
      return this.source
   }
}

//object for parsing the source
function ParseElement(text) {
   this.source=text
   this.index=0
   this.closeIndex=-1
   this.page=null
   this.type=0 //0-content: 1/-1 tag/close //-2 cdata
   this.tag=null
   this.attributes=null
   this.casedAttributes=null
   this.isContent=ParseElement_isContent
   this.isTag=ParseElement_isTag
   this.isCloseTag=ParseElement_isCloseTag
   this.isCData=ParseElement_isCData
   this.sourceCData=ParseElement_sourceCData
   this.getTag=ParseElement_getTag
   this.getCloseTag=ParseElement_getCloseTag
   this.getAttributes=ParseElement_getAttributes
   this.getAttribute=ParseElement_getAttribute
   this.getCasedAttribute=ParseElement_getCasedAttribute
   this.setAttribute=ParseElement_setAttribute
   this.innerHTML=ParseElement_innerHTML
   this.outerHTML=ParseElement_outerHTML
   this.innerXMLContent=ParseElement_innerXMLContent
   //doin the work
   if (this.source.substring(0,1)=="<") {
   //if ((this.source.substring(0,1)=="<") && (this.source.substring(this.source.length-1,this.source.length)==">")) {
      //its a tag
      if (this.source.substring(1,2)=="/") {
         //close tag
         this.type=-1
         this.getCloseTag()
      } else if (this.source.substring(1,9)=="![CDATA[") {
         //cdata
         this.type=-2
      } else {
         //regular tag - parse the attributes
         this.type=1
         this.getTag()
         this.getAttributes()
      }
   }
}

//methods for parsedpage object
function ParsedPage_getElementsByTagName(text) {
   var comptext=text.toLowerCase()
   var out=new Array()
   if (this.parseStack!=null) {
	  var i=0
	  var j=this.parseStack.length
	  if (j>0) {
	     for (i=0; i<j; i++) {
	        if (this.parseStack[i].isTag()) {
	           if (this.parseStack[i].tag==comptext) {
	              out[out.length]=this.parseStack[i]
	           }
	        }
	     }
      }
   }
   return out
}
function ParsedPage_getSource() {
   var out=""
   if (this.parseStack!=null) {
	  var i=0
	  var j=this.parseStack.length
	  if (j>0) {
	     for (i=0; i<j; i++) {
			out+=this.parseStack[i].source
	     }
      }
   }
   return out
}
function ParsedPage_subSource(a,b) {
   var out=""
   if (this.parseStack!=null) {
	  var i=a
	  for (i=a; i<b; i++) {
		 out+=this.parseStack[i].source
	  }
   }
   return out
}
//parse page object
function ParsedPage(text) {
   this.source=text
   var data=this.source
   this.parseStack=new Array()
   this.getElementsByTagName=ParsedPage_getElementsByTagName
   this.getSource=ParsedPage_getSource
   this.subSource=ParsedPage_subSource
   //clean out tags/content that I don't want
   //data=data.replace(rexPHPCodeG,"")
   //data=data.replace(rexASPJSPCodeG,"")
   data=data.replace(rexXMLCommandG,"")
   data=data.replace(rexXMLDocTypeSBG,"")
   data=data.replace(rexXMLDocTypeG,"")
   data=data.replace(rexScriptTagContentsG,"")
   data=data.replace(rexStyleTagContentsG,"")
   data=data.replace(rexCommentSimpleG,"")
   data=data.replace(rexCommentLongG,"")
   //doin the loop work
   var p=0
   var pdata=""
   var pelem=null
   var re=data.match(rexCDataOrTag)
   while (re!=null) {
	  //check to see if we are not leading with a match
	  //grab intermediate content - put on end of parse stack
	  if (re.index!=0) {
	     pdata=data.substring(0,re.index)
	     pelem=new ParseElement(pdata)
	     pelem.index=this.parseStack.length
	     pelem.page=this
	     this.parseStack[this.parseStack.length]=pelem
	  }
	  //add my match to the end of the parseStack
	  pelem=new ParseElement(re[0])
	  pelem.index=this.parseStack.length
	  pelem.page=this
	  this.parseStack[this.parseStack.length]=pelem
	  //skip position and rematch
	  p=re.index+re[0].length
	  data=data.substring(p,data.length)
	  re=data.match(rexCDataOrTag)
   }
}

