xhr={go:function(method,uri,body,callback,headers){headers=headers||[]
 var i,l,x=new XMLHttpRequest()
 x.onreadystatechange=function(){if (x.readyState==4){callback(x)}}
 x.open(method,uri,true)
 for(i=0,l=headers.length;i<l;i++)
   x.setRequestHeader(headers[i][0],headers[i][1])
 x.send(body)}} // XXX this needs to exist in only one place

function formUrlEncEls(){var d={},n,v,i,l,el
 for(i=0,l=arguments.length;i<l;i++){
  el=arguments[i];n=el.name;v=el.value
  if(n===undefined||v===undefined)continue
  d[n]=v}
 return formUrlEncDict(d)}

function formUrlEncDict(dict){var i,a=[]
 for(i in dict){
  a.push(enc(i)+'='+enc(dict[i]))}
 return a.join('&')}

function enc(s){
 return encodeURIComponent(s)}

function commentHTML(user,site,body,date){date=date||new Date()
 user=htmlEsc(user);site=encodeURI(site);body=htmlEsc(body)
 user=user||'Anonymous'
 return '<li id="c_'+user+'_'
         +date.valueOf().toString().slice(0,10)
         +'">\n'
        +'<heading>'
        +'<time datetime='+dateISO(date)+'>'
        +date.toLocaleDateString()+'</time> '
        +(site
         ?'<a href="'+site+'">'+user+'</a>'
         :user)
        +'\n</heading>\n'
        +'<section class=body>\n'
        +'<p>'
        +body.replace(/(\r\n|\r|\n){2,}/g,'\n\n<p>')
        +'</p></section>'
        +'\n'}

function htmlEsc(s,re){re=re||/[<>&]/g
 var ent={'<':'&lt;'
         ,'>':'&gt;'
         ,'&':'&amp;'}
 return s.replace(re,function(m){return ent[m]})}

function dateISO(d){d=d||new Date()
 var tz=d.getTimezoneOffset(),tzh,tzm,tzs='-'
 if(tz<0){tz=-tz;tzs='+'}
 tzh=Math.floor(tz/60);tzm=(tz%60)
 return d.getFullYear()+'-'
           +pad(1+d.getMonth())+'-'
           +pad(d.getDate())
        +'T'+pad(d.getHours())+':'
            +pad(d.getMinutes())+':'
            +pad(d.getSeconds())
        +tzs+pad(tzh)+pad(tzm)}

function pad(n){return n<10 ?'0'+n :''+n}

function setupCommentUI(rBtn){var
 pLI=ancestor('LI',rBtn)
 ,pOL=getOL(pLI)
 ,pC=document.getElementById('postComment')
   if(pC)pOL.appendChild(pC)
   else pC=appendCommentUI(pOL)
 var cIn=document.getElementById('commentInput')
 ,nIn=document.getElementById('name')
 ,eIn=document.getElementById('email')
 ,sIn=document.getElementById('site')
 ,cPr=document.getElementById('commentPreview')
 ,go=document.getElementById('go')
 ,note=document.createElement('div')
 ,cancel=document.getElementById('cancel')
 ,timeout
 ,createPreview=function(){var
  li=document.createElement('li');li.id='commentPreview'
  cPr=pOL.appendChild(li)}
 ,refreshPreview=function(){
  if(!cIn.value){cPr.parentNode.removeChild(cPr);cPr=undefined;return}
  if(!cPr)createPreview()
  var div=document.createElement('div')
  div.innerHTML=commentHTML(nIn.value,sIn.value,cIn.value)
 +'<span title="This is only a preview!  Your comment has not yet been posted.">(preview)</span>'
  //div.id='commentPreview'
  //log(div.innerHTML)
  cPr.parentNode.replaceChild(div,cPr)
  div.firstChild.id='commentPreview'
  cPr=div
  //cPr.innerHTML=''
  //while(div.firstChild){cPr.appendChild(div.firstChild)}
  cPr.scrollIntoView(false)} //XXX scrollIntoView() sucks
 ,refreshSoon=function(){
  if(timeout)clearTimeout(timeout)
  timeout=setTimeout(refreshPreview,50)}
 ,post=function(){var
  iRT=(pLI?'&inReplyTo='+pLI.id:'')
  ,pURI='&post='+encodeURIComponent(location.href.split('/').pop()) // some people disable Referer
  go.disabled='disabled'
 xhr.go('POST','postComment',formUrlEncEls(cIn,nIn,eIn,sIn)+iRT+pURI,
   function(x){var b;if(x.status==200){
    note.id='commentNote'
    note.innerHTML=x.responseText
    b=document.createElement('input')
    b.type='button';b.value='close'
    b.onclick=function(){note.parentNode.removeChild(note)}
    note.appendChild(b)
    if(cPr){cPr.parentNode.removeChild(cPr);cPr=null}
    document.body.appendChild(note)
    document.body.className=''
    pC.parentNode.removeChild(pC)
    note.getElementsByTagName('input')[0].focus()
   }},[['Content-Type','application/x-www-form-urlencoded']])
  }
 ,hide=function(){
  document.body.className=''
  pC.style.display='none'
  if(cPr){cPr.parentNode.removeChild(cPr);cPr=null}}
 cIn.onkeydown=cIn.onkeypress=cIn.onmouseup=refreshSoon
 nIn.onkeydown=nIn.onkeypress=nIn.onmouseup=refreshSoon
 sIn.onkeydown=sIn.onkeypress=sIn.onmouseup=refreshSoon
 go.onclick=post
 cancel.onclick=hide
 document.body.className='replyMode'
 pC.style.display=''
 if(cIn.value)refreshPreview()
 cIn.focus()}

function getOL(li){var r,ol
 if(!li)return document.getElementById('replies')
 r=li.getElementsByTagName('ol')[0]
 if(r)return r
 ol=document.createElement('ol')
 ol.className='replies'
 li.appendChild(ol)
 return ol}

//XXX debug only
function highlight(el){el.style.background='#f00';return el}

function ancestor(tN,el){
 while(el=el.parentNode) if(el.tagName==tN) return el}

function showHelp(){var
 hdlg=document.getElementById('helpBox')
 if(!hdlg)createHelp()
 else hdlg.style.display='block'
 document.getElementById('closeHelp').focus()}

function hideHelp(){
 document.getElementById('helpBox').style.display='none'}

commentUIHTML='\
<fieldset id=postComment>\n\
<textarea id=commentInput name=comment></textarea>\n\
<label>Name: <input type=text id=name name=name></label>\n\
<label>Email: <input type=text id=email name=email></label>\n\
<label>Website: <input type=text id=site name=site></label>\n\
<fieldset id=controls>\n\
<input type=button id=go value=comment>\n\
<input type=button id=cancel value=cancel>\n\
<p id=help><a href=javascript:showHelp()>info</a></p>\n\
</fieldset>\n\
</fieldset>\n\
'

function createHelp(){var
 div=document.createElement('div')
 ,cls=document.createElement('input')
 cls.type='button';cls.value='close'
 cls.id='closeHelp'
 cls.onclick=hideHelp
 div.innerHTML='\
<p>Use a blank line to separate paragraphs.\n\
<p>Links and text formatting are not (yet) supported.\n\
<p>To prevent spam, comments are held for approval, and will not appear immediately.\n\
<p>Your email address will not be published, and is not required, but may help identify your comment as non-spam.\n\
'
 div.appendChild(cls)
 div.id='helpBox'
 document.body.appendChild(div)}

function appendCommentUI(el){var
 div=document.createElement('div')
 div.innerHTML=commentUIHTML
 while(div.firstChild){el.appendChild(div.firstChild)}
 return document.getElementById('postComment')}

function commentsOnload(){var lis,li,i,l,ol
 ,replies=document.getElementById('replies')
 replies.appendChild(rBtn('replyButton','post a comment'))
 //document.body.insertBefore(rBtn('replyButton','post a comment'),replies)
 lis=replies.getElementsByTagName('li')
 for(i=0,l=lis.length;i<l;i++){li=lis[i]
  ol=li.getElementsByTagName('section')
  if(ol.length) ol[0].appendChild(rBtn())
  else li.appendChild(rBtn())}}

function rBtn(id,text){var
 r=document.createElement('input')
 r.type='button';r.value=text||'reply'
 r.className='replyButton'
 if(id)r.id=id
 r.onclick=function(){
  if(document.body.className=='replyMode')return
  openReply(r)}
 return r}

function openReply(rBtn){
 setupCommentUI(rBtn)}