var DOM = {};
/** DOM.findAncestor(elChild, {att: val[, att2: val2]})
 ** If elChild has an ancestor with specified attributes attn with values valn
 ** Return the ancestor element (or null if none found)
 **/
DOM.findAncestor = function(elChild, hAtts)
{
	var el = elChild, att = '', bMatch = true, ct = 0;
	if (elChild && hAtts)
	{
		// traverse upward as long as the parentNode is valid
		while(el = el.parentNode)
		{
			bMatch = true;

			// for each specified attribute
			ct = 0;
			for (att in hAtts)
			{
				if (el[att] !== undefined)
				{
					switch (att)
					{
						case 'className':
						bMatch = (CSS.hasClass(el, hAtts[att]));
						break;

						default:
						bMatch = (el[att] == hAtts[att]);
					}
					ct++;
				}
			}

			// this el has passed all tests, return it!
			if (ct > 0 && bMatch)
			{
				return el;
			}
		}
	}
	return null;
}

/** $(idOrEl, idOrEl2, idOrEl3, ...) -- getElement(s)ById
 ** Returns DOM elements based on their string IDs.
 ** Will return a single element given a single argument, otherwise an array.
 ** Originally from Sam Stephenson <sam@conio.net>. Much better now.
 **/
function $()
{
	var elements = [], element = null;
	for (var i = 0; i < arguments.length; i++)
	{
		element = arguments[i];
		if (typeof element == 'string')
		{
			element = document.getElementById(element);
		}
		if (arguments.length == 1)
		{
			return element;
		}
		if (element)
		{
			elements[elements.length] = element;
		}
	}
	return elements;
}

/** $class(sTagName, sClassName[, elRoot]) -- getElementsByClassName
 ** Returns all elements of a given tag name (* for all) with the given class
 ** name, optionally restricted to the subtree of a specific element, elRoot.
 **/
function $class(sTagName, sClassName, elRoot)
{
	var elsMatch = [];
	var elsAll = $tag(sTagName, elRoot);

	try {
		for (var i=0; i < elsAll.length; i++)
		{
			if (CSS.hasClass(elsAll[i], sClassName))
			{
				elsMatch[elsMatch.length] = elsAll[i];
			}
		}
	}
	catch (e) {}
	return elsMatch;
}
getElementsByClass = getElementsByClassName = $class;

/** $tag(sTagName[, elRoot]) -- getElemen tsByTagName
 ** Returns all elements of a given tag name (* for all) , optionally restricted
 ** to the subtree of a specific element, elRoot.
 **/
function $tag(sTagName, elRoot)
{
	if (!elRoot)
	{
		elRoot = document;
	}
	return elRoot.getElementsByTagName(sTagName);
}

/** Array.arrayize(el1[, el2, ...])
 ** Returns a single array containing all elements within arguments or the
 ** argument itself if it is not iterable.
 ** Think of this as "array-ize". Note: it only "collapses" one level of arrays.
 **/
Array.arrayize = $A = function()
{
	if (arguments.length > 0)
	{
		var aRet = [], cur, j=0;
		for (var i=0; i < arguments.length; i++)
		{
			if (arguments[i])
			{
				cur = arguments[i];
				// custom objects with toArray()
				if (cur.toArray)
				{
					aRet = aRet.concat(cur.toArray());
				}
				// arrays
				else if (cur instanceof Array)
				{
					aRet = aRet.concat(cur);
				}
				// other iterables
				else if (typeof cur != "string" && cur.length !== undefined)
				{
					for (j=0; j < cur.length; j++)
					{
						aRet.push(cur[j]);
					}
				}
				// non-iterables
				else
				{
					aRet.push(cur);
				}
			}
		}
		return aRet;
	}
	return [];
}

/** Events - bfults@gmail.com - 2006-09-22                                   **
 ** Code licensed under Creative Commons Attribution-ShareAlike License      **
 ** http://creativecommons.org/licenses/by-sa/2.5/                           **/
var Events = {};
Events.pageLoaded = false;
Events.eventStack = [];
Events.unloadStack = [];
Events.loadStack = [];
Events.addHandler = function(xEl, sEvt, fnCB)
{
  bCap = false;
  if (xEl == window)
  {
    if (sEvt == "load")
    {
      Events.loadStack.push(fnCB);
      return true;
    }
    else if (sEvt == "unload")
    {
      Events.unloadStack.push(fnCB);
      return true;
    }
  }

  Events.eventStack.push([!Events.pageLoaded, xEl, sEvt, fnCB, bCap]);

  if (Events.pageLoaded)
  {
    return Events._xbEventAddHandler(xEl, sEvt, fnCB, bCap);
  }
  return true;
}
Events.removeHandler = function(xEl, sEvt, fnCB)
{
  var oEl = (typeof (xEl) == "string") ? document.getElementById(xEl) : xEl;
  bCap = false;
  for (var i=0, el=null; i < Events.eventStack.length; i++)
  {
    el = Events.eventStack[i];
    if (el[1] == oEl && el[2] == sEvt && el[3] == fnCB && el[4] == bCap)
    {
      Events.eventStack.splice(i, 1);
      return _xbEventRemoveHandler(oEl, sEvt, fnCB, bCap);
    }
  }
  return false;
}
Events.getTargetElement = function(evt)
{
  if (!evt && window.event) evt = window.event;
  if (!evt) return null;
  return evt.target || evt.srcElement;
}
Events.stopPropagation = function(evt)
{
  if (!evt && window.event) evt = window.event;
  if (!evt) return false;
  if (evt.stopPropagation) evt.stopPropagation();
  evt.cancelBubble = true;
  return true;
}
Events.preventDefault = function(evt)
{
  if (!evt && window.event) evt = window.event;
  if (!evt) return false;
  if (evt.preventDefault) evt.preventDefault();
  evt.returnValue = false;
  return true;
}
// Private Functions
Events._setup = function()
{
  var el = null;
  Events.pageLoaded = true;

  try
  {
    if (Events.loadStack && Events.loadStack.length)
    {
      for (var i = Events.loadStack.length - 1; i >= 0; i--)
      {
        if (typeof Events.loadStack[i] == "function") Events.loadStack[i]();
        delete Events.loadStack[i];
      }
    }
  } catch(e) {}

  try
  {
    if (Events.eventStack && Events.eventStack.length)
    {
      for (i=0; i < Events.eventStack.length; i++)
      {
        el = Events.eventStack[i];
        if (el[0] == true)
        Events._xbEventAddHandler(el[1], el[2], el[3], el[4]);
      }
    }
  } catch(e) {}
  return true;
}
Events._cleanup = function()
{
  var el = null;
  try
  {
    if (Events.unloadStack && Events.unloadStack.length)
    {
      while (el = Events.unloadStack.pop())
      {
        if (typeof el == "function") el();
      }
    }
    if (Events.eventStack && Events.eventStack.length)
    {
      while (el = Events.eventStack.pop())
      Events._xbEventRemoveHandler(el[1], el[2], el[3], el[4]);
    }
    for (var i in Events) delete Events[i];
    delete window.Events;
  } catch (e) {}
  return true;
}
Events._xbEventRemoveHandler = function(oEl, sEvt, fnCB, bCap)
{
  if (oEl)
  {
    if (!bCap) bCap = false;
    if (oEl.removeEventListener)
    {
      oEl.removeEventListener(sEvt, fnCB, bCap);
      return true;
    }
    else if (oEl.detachEvent)
    {
      return oEl.detachEvent("on"+ sEvt, fnCB);
    }
  }
  return false;
}
Events._xbEventAddHandler = function(xEl, sEvt, fnCB, bCap)
{
  var oEl = (typeof (xEl) == "string") ? document.getElementById(xEl) : xEl;
  if (xEl === window && sEvt == "load")
  {
    // KHTML / WebKit
    if (/KHTML/i.test(navigator.userAgent))
    {
      window._load_timer = setInterval(function() {
        if (/loaded|complete/.test(document.readyState)) {
          clearInterval(window._load_timer);
          fnCB();
        }
      }, 10);
      return true;
    }
    // Gecko
    else if (document.addEventListener)
    {
      document.addEventListener("DOMContentLoaded", fnCB, false);
      return true;
    }
    else
    {
      // IE
      /*@cc_on @*/
      /*@if (@_win32)
      document.write("<script id='__ie_onload' defer src='javascript:void(0)'><\/script>");
      var ie_script = document.getElementById("__ie_onload");
      ie_script.onreadystatechange = function() {
        if (this.readyState == "complete") {
          fnCB();
        }
      };
      return true;
      /*@end @*/
    }
  }
  if (oEl)
  {
    if (!bCap) bCap = false;
    if (oEl.addEventListener)
    {
      oEl.addEventListener(sEvt, fnCB, bCap);
      return true;
    }
    else if (oEl.attachEvent)
    {
      return oEl.attachEvent("on"+ sEvt, fnCB);
    }
  }
  return false;
}
Events._xbEventAddHandler(window, "load", Events._setup, false);
Events._xbEventAddHandler(window, "unload", Events._cleanup, false);
/* End Events */
/** FormCollect - Compiles a string of form data. - brad@xkr.us - 2004-10-20 **
 ** Code licensed under Creative Commons Attribution-ShareAlike License      **
 ** http://creativecommons.org/licenses/by-sa/2.0/                           **/
function FormCollect(oForm)
{
  var sRetval='', sTemp='', sCTName='', sCName='', sCType='', arrElts=[],
    oCurrent=null;
  for (var i=oForm.elements.length-1; i >= 0; i--)
  {
    oCurrent = oForm.elements[i];
    /* successful elements must have a name and must not be disabled */
    if (oCurrent.name && !oCurrent.disabled) arrElts.push(oCurrent);
  }

  /* sort elements so same names will be adjacent to each other */
  arrElts.sort(function(a,b){return ((a.name<b.name)?1:(a.name==b.name)?0:-1);});

  while (oCurrent = arrElts.pop())
  {
    sCTName = oCurrent.tagName.toLowerCase();
    sCName = oCurrent.name.toLowerCase();
    sCType = ((oCurrent.type)?oCurrent.type:'').toLowerCase();

    /* handle input[type="radio|checkbox"] */
    if (sCTName == "input" && /^(?:radio|checkbox)$/.test(sCType))
    {
      do {
        if (oCurrent.checked || oCurrent.selected)
          sRetval = sRetval.append(encodeURIComponent(oCurrent.name) + '=' +
                         encodeURIComponent(oCurrent.value), '&');
      } while (arrElts[arrElts.length-1].name == oCurrent.name &&
               (oCurrent = arrElts.pop()));
    }

    /* handle select[multiple] */
    if (sCTName == "select" && oCurrent.multiple && oCurrent.options)
    {
      for (i=0,len=oCurrent.options.length,sTemp=''; i < len; i++)
        if (oCurrent.options[i].selected)
          sTemp = sTemp.append(encodeURIComponent(oCurrent.options[i].value),
                               ',');
      sRetval = sRetval.append(encodeURIComponent(oCurrent.name) + '=' +
                               sTemp, '&');
    }
    /* any other element */
    else if ((sCTName == "input" &&
             /^(?:text|password|hidden)$/.test(sCType)) ||
            /^(?:select|textarea)$/.test(sCTName))
    {
      sRetval = sRetval.append(encodeURIComponent(oCurrent.name) + '=' +
                     encodeURIComponent(oCurrent.value), '&');
    }
  }
  return sRetval;
}
String.prototype.append = function(sAdd, sSep)
{
  return this + ((this.length)?sSep:'') + sAdd;
}
/** Net - XMLHTTP Interface - bfults@gmail.com - 2006-08-29                 **
 ** Code licensed under Creative Commons Attribution-ShareAlike License     **
 ** http://creativecommons.org/licenses/by-sa/2.5/                          **/

/** Net asynchronous request library **/
Net = function()
{
  this.Request = Net._createRequestObject();
}

/** Net.get({
   url: [string] -- URL to make the request to,
   vars: [string|object] (optional) -- object or string of querystring vars,
   onsuccess: [function] -- function reference to call on success,
   onerror: [function] (optional) -- function reference to call on error
 }) -- make an HTTP GET request
 ** Returns true on successful request dispatch, false on error.
 **/
Net.get = function(oArgs)
{
  if (oArgs.url && oArgs.onsuccess)
  {
    if (typeof oArgs.vars == "object")
    {
      oArgs.vars = '?'+ Net._serializeObject(oArgs.vars);
    }
    else if (typeof oArgs.vars == "string"
             && oArgs.vars.length > 0
             && !/\?/.test(oArgs.vars))
    {
      oArgs.vars = '?' + oArgs.vars;
    }
    if (!oArgs.vars) oArgs.vars = '';
    oArgs.onerror = oArgs.onerror || Net._fnErrorDefault;
    try {
      var N = new Net();
      N.Request.open("GET", oArgs.url + oArgs.vars, true);
      N._setCallback(oArgs.onsuccess, oArgs.onerror);
      N.Request.send('');
    }
    catch (e)
    {
      oArgs.onerror("initialization");
      return false;
    }
  }
  else
  {
    return false;
  }
  return true;
}

/** Net.post({
   url: [string] -- URL to make the request to,
   vars: [string|object] (optional) -- object or string of post vars,
   onsuccess: [function] -- function reference to call on success,
   onerror: [function] (optional) -- function reference to call on error
 }) -- make an HTTP POST request
 ** Returns true on successful request dispatch, false on error.
 **/
Net.post = function(oArgs)
{
  if (oArgs.url && oArgs.onsuccess)
  {
    if (typeof oArgs.vars == "object")
    {
      oArgs.vars = Net._serializeObject(oArgs.vars);
    }
    if (!oArgs.vars) oArgs.vars = '';
    oArgs.onerror = oArgs.onerror || Net._fnErrorDefault;
    try {
      var N = new Net();
      N.Request.open("POST", oArgs.url, true);
      N._setCallback(oArgs.onsuccess, oArgs.onerror);
      N.Request.setRequestHeader("Method", "POST "+oArgs.url+" HTTP/1.1");
      N.Request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
      N.Request.send(oArgs.vars);
    }
    catch (e)
    {
      oArgs.onerror("initialization");
      return false;
    }
  }
  else
  {
    return false;
  }
  return true;
}

// a default error function showing the structure (does nothing)
Net._fnErrorDefault = function(sType, Request)
{
  switch (sType)
  {
    case "timeout":
    // it was a timeout (Request undefined)
    alert("XMLHTTP timeout.");
    break;

    case "initialization":
    // initialization error (Request undefined)
    alert("XMLHTTP initialization failure.")
    break;

    default:
    // other error (HTTP status, etc.)
    alert("XMLHTTP error: ["+ Request.status +"] "+ Request.statusText);
  }
}

// a helper function to serialize an object
Net._serializeObject = function(oFrom)
{
  var aTemp = [];
  for (var i in oFrom)
  {
    aTemp.push(encodeURIComponent(i) +"="+ encodeURIComponent(oFrom[i]));
  }
  return aTemp.join('&');
}

/** Net._setCallback(fnCallback, fnError)
 ** Attaches (and wraps) a request object with a user-defined callback.
 ** Optional fnError: call upon erroneous HTTP status code or timeout.
 **/
Net.prototype._setCallback = function(fnCallback, fnError)
{
  this.Request.onreadystatechange = (function (oNet)
  {
    return function()
    {
      if (oNet.Request.readyState == 4)
      {
        window.clearTimeout(oNet.timeout);

        try {
          if (oNet.Request.status === undefined
            || oNet.Request.status === 0
            || (oNet.Request.status >= 200 && oNet.Request.status < 300)
            || oNet.Request.status == 304)
          {
            fnCallback(oNet.Request);
          }
          else
          {
            fnError("other", oNet.Request);
          }
        } catch (e) {
          fnError("other", oNet.Request);
        }
      }
    }
  })(this);

  this.timeout = window.setTimeout((function(oNet) {
    return function() {
      oNet.Request.onreadystatechange = function() {};
      oNet.Request = null;
      fnError("timeout"); }})(this), 20000);
}

/** Net._createRequestObject()
 ** Creates and returns an XMLHTTP element or null on failure.
 **/
Net._createRequestObject = function()
{
  var xmlhttp;
  if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); }
  else if (window.ActiveXObject) {
    try { xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); }
    catch (e) { try { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }
    catch (e) { xmlhttp = null; }}
  }
  return xmlhttp;
}
/** End Net asynchronous request library */
NiceAmps = {};

NiceAmps.AMP = document.createElement('span');
NiceAmps.AMP.className = 'amp';
NiceAmps.AMP.appendChild(document.createTextNode('&'));

/* frob ampersands in text in or under the given node */
NiceAmps.frobNode = function(n) {
  if (n.nodeType == 3) { /* text nodes */
    var offset = n.data.indexOf('&');
    if (offset > -1) {
      var n2 = n.splitText(offset);
      var n3 = n2.splitText(1);
      n.parentNode.insertBefore(NiceAmps.AMP.cloneNode(true), n2);
      n.parentNode.removeChild(n2);
    }
  } else if (n.className != 'amp') { /* don't frob inside AMP */
    for (var i=0; i<n.childNodes.length; i++) {
      NiceAmps.frobNode(n.childNodes[i]);
    }
  }
}

/* frob ampersands in h[123456] */
NiceAmps.frobHeadings = function() {
  var heading_names = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
  for (var i = 0; i<heading_names.length; i++) {
    var headings = document.getElementsByTagName(heading_names[i]);
    for (var j = 0; j<headings.length; j++) {
      NiceAmps.frobNode(headings[j]);
    }
  }
}

/* Fire off ampifyHeadings at page load time. Replace with your
 * preferred JavaScript library's technique.
 */
Events.addHandler(window, 'load', NiceAmps.frobHeadings);
/* = comment.el --- XHRify the comment form
 *
 * Copyright (C) 2007 Edward O'Connor

 * Author: Edward O'Connor <ted@oconnor.cx>
 * Keywords: convenience

 * This file is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.

 * This file is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with GNU Emacs; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

/* = Commentary */

/* = Code */

Comment = {};
Comment.Throbber = {};

Comment.Throbber.start = function() {
   var elProgress = $tag('img', $('comment-status'))[0];
   elProgress.style.visibility = 'visible';
}

Comment.Throbber.stop = function() {
   var elProgress = $tag('img', $('comment-status'))[0];
   elProgress.style.visibility = 'hidden';
}

Comment.Status = {};

Comment.Status.clear = function() {
   var elOutput = $tag('span', $('comment-status'))[0];
   elOutput.className = "output";
   elOutput.innerHTML = '';

}

Comment.Status.set = function(status, iserror) {
   var elOutput = $tag('span', $('comment-status'))[0];
   if (iserror) {
      elOutput.className = "output error";
   }
   elOutput.innerHTML = status;
}

Comment.success = function(r) {
   Comment.Throbber.stop();
   Comment.Status.set("Thanks!", false);
   location.href = r.getResponseHeader('Location');
}

Comment.error = function(reason, r) {
   Comment.Throbber.stop();
   if (r.status != 500) {
      var lines = eval(r.responseText);
      Comment.Status.set(lines[0], true);
   } else {
      Comment.Status.set("Unknown error.", true);
   }
}

Comment.submit = function(evt) {
   Comment.Status.clear();
   Events.preventDefault(evt);
   Events.stopPropagation(evt);

   var elForm = $tag('form', $('comments'))[0];

   Comment.Throbber.start();

   var args = FormCollect(elForm) + '&asynch=1';
   Net.post({url: elForm.action, vars: args,
             onsuccess: Comment.success,
             onerror: Comment.error});
}

if (window.Showdown) {
  Comment.mkdn = new Showdown.converter();
}

Comment.preview = function(evt) {
  var elPreview = $('preview');
  var elText = Events.getTargetElement(evt);
  elPreview.innerHTML = Comment.mkdn.makeHtml(elText.value);
}

Events.addHandler(window, 'load', function(evt) {
  var elComments = $('comments');
  if (!elComments) { return false; }
  var elForm = $tag('form', elComments)[0];
  var elSubmit = $tag('button', elForm)[0];
  var elText = $tag('textarea', elForm)[0];
  elSubmit.type = "button";
  Events.addHandler(elForm, 'submit', Comment.submit);
  Events.addHandler(elSubmit, 'click', Comment.submit);
  Events.addHandler(elText, 'change', Comment.preview);
  Events.addHandler(elText, 'keyup', Comment.preview);
});

/* comment.js ends here */
