/* Copyright Eventful, Inc. All rights reserved except where otherwise noted. */
if (typeof window.JS == "undefined") var JS = {};
JS.defineGlobal = function(sVar, xVal)
{
    if (xVal === undefined)
    {
        xVal = {};
    }
    if (typeof window[sVar] == "undefined")
    {
        window[sVar] = xVal;
        return xVal;
    }
    return window[sVar];
}

JS.defineGlobal("Eventful");
JS.defineGlobal("DOM");
JS.defineGlobal("CSS");
Eventful.Autoclose = {};
Eventful.formHandlers = {};

/* no-op console functions for non-Firebug browsers */
var nop = function() { return true; };
Eventful._console = {error:nop, info:nop, debug:nop, warn:nop, log:nop};
Eventful.console = Eventful._console;
if (window.console && window.console.firebug)
{
  Eventful.console = window.console;
}
else if (window.console && window.console.log)
{
  Eventful.console.log = window.console.log;
}
/* end no-op console functions */

// IE detection (ugh)
Eventful.isIE = false /*@cc_on @*//*@if (@_win32) || true/*@end @*/;
Eventful.isSafari = navigator && navigator.userAgent.indexOf('Safari') > 0 ;

// JS-CSS relationship ("yes CSS, we have JS enabled")
Eventful.JSCheck = function()
{
  document.body.className += " has-js";
}

Eventful.fixKeyCode = function(keyCode)
{
  var keyEnter = 13;
  if(Eventful.isSafari && keyCode == 3) return keyEnter;
  if(Eventful.isIE && keyCode == 0) return keyEnter;
  return keyCode;
}

Eventful.helpWindow = function(elLink)
{
    return window.open(elLink.href, elLink.target,
        'width=300,height=350,resizable=1,scrollbars=1');
}

Eventful.showLargeImage = function(elLink, nWidth, nHeight)
{
    // make sure we have numbers
    nWidth = (nWidth * 1) || 300;
    nHeight = (nHeight * 1) || 300;

    // open the window and inject HTML
    var elWin = window.open('', elLink.target,
        'width='+ nWidth +',height='+ nHeight +
        ',resizable=1,scrollbars=1');
    if (elWin)
    {
        elWin.document.open("text/html");
        elWin.document.write('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML'+
        ' 4.01//EN"\n   "http://www.w3.org/TR/html4/strict.dtd">\n' +
        '<html><head>\n<title>Eventful Image Viewer</title>'+
        '<style type="text/css">\n' +
        'html,body{margin:0; padding:0; overflow:hidden; width:100%;' +
        'height:100%;}\n' +
        'img,div{margin:0;padding:0;}\n' +
        'p{margin:0;padding:0.5em;text-align:center;}\n' +
        '</style><body><div><img src="'+ elLink.href +'" alt="Full image" ' +
        'title="Click to close" onclick="self.close()" />' +
        '</div></body></html>');
        elWin.document.close();

        return true;
    }
    return false;
}

/* Begin Progress Stuff */
Eventful.showProgress = function(idEl, sMsg)
{
  var elParent = DOM.get(idEl);
  var elProgress = document.createElement("div");
  elProgress.className = "eventful-progress";

  var elImg = document.createElement("img");
  elImg.src = "/images/throbber.gif";
  elProgress.appendChild(elImg);

  var elProgressText = document.createElement("span");
  elProgress.appendChild(elProgressText);

  elParent.appendChild(elProgress);
  elProgressText.innerHTML = sMsg;

  return true;
}

Eventful.hideProgress = function(idEl)
{
  var elProgress = DOM.getByClass("div", "eventful-progress", DOM.get(idEl))[0];
  elProgress.parentNode.removeChild(elProgress);
}
/* End Progress Stuff */

/* Box Architecture */
Eventful.Boxes = {
  _store: {}
};

Eventful.Boxes.add = function(idBox)
{
  // if we've already added the box, just reload it
  if (Eventful.Boxes._store[idBox])
  {
    Eventful.Boxes.load(idBox);
  }
  // else we're adding a new box
  else
  {
    // remember that we added this box
    Eventful.Boxes._store[idBox] = true;

    // if the page has already loaded, just load the box
    if (Events.pageLoaded == true)
    {
      Eventful.Boxes.load(idBox);
    }
    // else this is before the page has loaded, so queue the load
    else
    {
      Events.addHandler(window, "load", function()
      {
        Eventful.Boxes.load(idBox);
      });
    }
  }
}

Eventful.Boxes.load = function(idBox, sMsgLoading)
{
  var elBox = DOM.get(idBox);
  var elPlaceholder = DOM.get("insert-"+ idBox);
  var idBoxCore = idBox.replace(/^box-(.*?)(-\d+)?$/, "$1");

  // if we have a place to put the box
  if (elBox || elPlaceholder)
  {
    // load the box HTML
    Net.get({
      url: "/boxes/"+ idBoxCore,
      vars: {time: new Date().getTime(), asynch: 1},
      onsuccess: Eventful.Boxes._getLoadHandler(idBox),
      onerror: Eventful.Boxes._getErrorHandler(idBox)
    });
  }
  // else we can't deal with this box
  else
  {
    Eventful.console.warn("Eventful.Boxes.load: No box with id: "+ idBox);
  }
  return true;
}

Eventful.Boxes.remove = function(idBox)
{
  // replace the box with a placeholder element
  var sHTML = '<div id="insert-'+ idBox +'" class="box-placeholder"></div>';
  Eventful.Boxes._replace(idBox, sHTML);
  return true;
}

// Private Box Methods
Eventful.Boxes._replace = function(idBox, htmlContent)
{
  var elBoxOld = DOM.get(idBox) || DOM.get("insert-"+ idBox);

  if (elBoxOld)
  {
    var elNew = document.createElement("div");
    elNew.innerHTML = htmlContent;
    elBoxOld.parentNode.replaceChild(elNew.firstChild, elBoxOld);
    elNew = null;
  }
  else
  {
    Eventful.console.error("No box to use!");
  }
  return true;
}

Eventful.Boxes._getLoadHandler = function(idBox)
{
  return function(R)
  {
    Eventful.Boxes._replace(idBox, R.responseText);
    return true;
  }
}

Eventful.Boxes._getErrorHandler = function(idBox)
{
  return function(sType, R)
  {
    var sHTML = '<div id="error-'+ idBox +'" class="box-error">'+
      'Error loading ('+ sType +'). '+
      '<span onclick="Eventful.Boxes.load(\''+ idBox +'\')"'+
        ' class="fakelink">Retry?</span>'+
      '</div>';
    var elBox = DOM.get(idBox);
    var elPlaceholder = DOM.get("insert-"+ idBox);

    // use the box if it's there, otherwise the placeholder
    var elToUse = elBox || elPlaceholder || null;
    if (elToUse)
    {
      // show the error to the user
      elToUse.innerHTML = sHTML;
    }
    else
    {
      Eventful.console.warn("Eventful.Boxes [error]: No box to put error in.");
    }
  }
}

/* End Box Architecture */

/** 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)
{
  var Events = window.Events;
  var bCap = false;
  if (xEl == window)
  {
    if (sEvt == "load")
    {
      if (!Events.pageLoaded) Events.loadStack.push(fnCB);
      else fnCB();
      return true;
    }
    else if (sEvt == "unload")
    {
      Events.unloadStack.push(fnCB);
      return true;
    }
  }
  
  // specialized keypress events for enter, escape, etc.
  // example: Events.addHandler(el, 'keypress-enter', this.listenEnter.bind(this));
  var aMatch = sEvt.match(/^keypress-(\w+)$/);
  var hKeyCodes = {enter: 13, escape: 27};
  if (aMatch && hKeyCodes[aMatch[1]])
  {
    // translate given sEvt and fnCB to 'keypress' and wrapper, respectively
    var parentCB = fnCB;
    sEvt = 'keypress';
    fnCB = function (evt)
    {
      if (evt.keyCode && Eventful.fixKeyCode(evt.keyCode) == hKeyCodes[aMatch[1]])
      {
        return parentCB(evt);
      }
    };
  }

  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 Events = window.Events;
  var oEl = (typeof (xEl) == "string") ? document.getElementById(xEl) : xEl;
  var 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 Events._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 Events = window.Events;
  var el = null;
  Events.pageLoaded = true;

  try
  {
    if (Events.loadStack && Events.loadStack.length)
    {
      for (var i = 0, len = Events.loadStack.length; i < len; i++)
      {
        if (typeof Events.loadStack[i] == "function") Events.loadStack[i]();
        delete Events.loadStack[i];
      }
    }
  } catch(e) {
    Eventful.console.error("%1.o", 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 Events = window.Events;
  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 */

/** DOM Class Manipulation - bfults@gmail.com - 2006-04-17                   **
 ** Code licensed under Creative Commons Attribution-ShareAlike License      **
 ** http://creativecommons.org/licenses/by-sa/2.5/                           **/
CSS.hasClass = function (el, sClass)
{
  if (el && el.className !== undefined)
  {
    return new RegExp("(^|\\s+)"+sClass+"(\\s+|$)").test(el.className);
  }

  return false;
}
CSS.addClass = function (el, sClass)
{
  if (el && el.className !== undefined)
  {
    CSS.remClass(el, sClass);
    el.className += ((el.className.length > 0) ? " " : '')+ sClass;
    return true;
  }

  return false;
}
CSS.remClass = function (el, sClass)
{
  if (el && el.className !== undefined)
  {
    el.className = el.className.replace(
      new RegExp("^"+sClass+"$|^"+sClass+"\\s+|\\s+"+sClass+
      "(?=\\s|$)",'g'), '');
    return true;
  }

  return false;
}
/* End DOM Class Manipulation */

/** Eventful.Autoclose **/
Eventful.Autoclose.num_elements = 0;
Eventful.Autoclose.element_id = null;

// Called on all clicks (which are inside the body) to close autoclose element
Eventful.Autoclose.body_click = function(evt)
{
  if (evt)
  {
    var elTarget = Events.getTargetElement(evt);
    if (elTarget)
    {
      // if we're clicking on an autoclose or autoclose-click element
      if (CSS.hasClass(elTarget, "autoclose") ||
        CSS.hasClass(elTarget, "autoclose-click") ||
        DOM.findAncestor(elTarget, {className: "autoclose"}))
      {
        // bail out early before closing the block
        return true;
      }
    }
  }
  Eventful.Autoclose.close_block();
  return true;
}

// Called to close the current autoclose block
Eventful.Autoclose.close_block = function()
{
  var id = Eventful.Autoclose.element_id;
  if (id && id.length)
  {
    var elAutoclose = DOM.get(id);
    if (elAutoclose)
    {
      elAutoclose.style.display = 'none';
      Eventful.Autoclose.element_id = null;
    }
  }
  return true;
}

// Called when the *click* elements are clicked to show the autoclose elements
Eventful.Autoclose.set_id = function(idEl)
{
  if (Eventful.Autoclose.element_id != idEl)
  {
    // if we're setting a new id, close an old block (if it's open)
    Eventful.Autoclose.close_block();
    Eventful.Autoclose.element_id = idEl;
  }
  return true;
}

Events.addHandler(window, "load", function() {
    if (window.Eventful && Eventful.Autoclose && Eventful.Autoclose.body_click)
    {
        Events.addHandler(document.body, "click",
            Eventful.Autoclose.body_click);
    }
});
/** End Eventful.Autoclose **/

/** 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 = bMatch && (CSS.hasClass(el, hAtts[att]));
            break;

            case 'tagName':
            bMatch = bMatch &&
              (el[att].toLowerCase() == hAtts[att].toLowerCase());
            break;

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

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


/** DOM.get(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.
 **/
DOM.get = function()
{
  var els = [], el = null;
  for (var i = 0; i < arguments.length; i++)
  {
    el = arguments[i];
    if (typeof el == "string")
    {
      el = document.getElementById(el);
    }
    else if (el instanceof Array)
    {
      if (el.length == 1)
      {
        el = [DOM.get.apply(window, el)];
      }
      else
      {
        el = DOM.get.apply(window, el);
      }
    }

    if (arguments.length == 1) return el;
    els[els.length] = el;
  }
  return els;
}
$ = DOM.get;

/** DOM.getByClass(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.
 **/
DOM.getByClass = function(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 = DOM.getByClass;

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

  return Array.arrayize(elRoot.getElementsByTagName(sTagName));
}
$tag = DOM.getByTag;

/** DOM.swapChildren(elOne, elTwo)
 ** Replaces all children of elOne with those of elTwo and vice versa.
 ** Returns true on success, false on failure
 ** Requires: moz.array, xkr.array
 **/
DOM.swapChildren = function(elOne, elTwo)
{
  if (!elOne || !elTwo)
    return false;

  var elChildrenOne = Array.arrayize(elOne.childNodes);
  var elChildrenTwo = Array.arrayize(elTwo.childNodes);

  elChildrenOne.forEach(function(child) {
    elTwo.appendChild(child);
  });

  elChildrenTwo.forEach(function(child) {
    elOne.appendChild(child);
  });

  return true;
}

/** Array.arrayize(el1[, el2, ...])
 ** Returns a single array containing all elements within arguments or the
 ** argument itself if it is not iterable.
 ** Note: it only "collapses" one level of arrays.
 **/
Array.arrayize = function()
{
    if (arguments.length > 0)
    {
        var aRet = [], cur, j=0;
        for (var i=0; i < arguments.length; i++)
        {
            if (arguments[i] !== undefined)
            {
                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 [];
}
$A = Array.arrayize;


Eventful.addCommas = function(nStr)
{
  nStr += '';
  x = nStr.split('.');
  x1 = x[0];
  x2 = x.length > 1 ? '.' + x[1] : '';
  var rgx = /(\d+)(\d{3})/;
  while (rgx.test(x1)) {
    x1 = x1.replace(rgx, '$1' + ',' + '$2');
  }
  return x1 + x2;
}


/** This code originated with the Prototype JS library.
  *  Prototype is freely distributable under the terms of an MIT-style license.
  *  For details, see the Prototype web site: http://prototype.conio.net/
  */
Function.prototype.bind = function() {
  var __method = this, args = Array.arrayize(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat(Array.arrayize(arguments)));
  }
}
/** End Prototype JS **/


/** set - bfults@gmail.com - 2007-04-30                                      **
 ** Code licensed under Creative Commons Attribution-ShareAlike License      **
 ** http://creativecommons.org/licenses/by-sa/2.5/                           **/
function set()
{
  var o = {};
  for (var i = 0, l = arguments.length; i < l; i++) o[arguments[i]] = true;
  return o;
}
/** End set **/


Eventful.Cats = {};
/* Eventful.Cats.addCount
 *
 * Handles setting category count for a single category.
 */
Eventful.Cats.addCount = function(elem)
{
  if (elem.href)
  {
    // append a count_only=1 to the url
    var url = elem.href + ((/\?/.test(elem.href)) ? '&' : '?') + "count_only=1";
    Net.get({url: url, vars: {asynch: 1}, onsuccess: Eventful.Cats.getInsertCount(elem)});
  }
}

/* Eventful.Cats.getInsertCount
 *
 * Returns a callback handler for the URL request.  Stuffs the returned
 * count into the DOM after the <a> we started with, formatted like this:
 *   " (23)".
 */
Eventful.Cats.getInsertCount = function(elem)
{
  return function(R)
  {
    if (R)
    {
      // we need a number
      var text  = R.responseText;
      if (/^\d+$/.test(text))
      {
        // elem.parentNode.appendChild(document.createTextNode(" (" + text + ")"));
        var objNewNode = document.createElement("span");
        var objNewText = document.createTextNode(" (" + Eventful.addCommas(text) + ")");
        objNewNode.appendChild(objNewText);
        elem.parentNode.appendChild(objNewNode);
      }
    }
  }
}

/* Asynchronous form submission */
Eventful.formHandlers = {};

Eventful.formHandlers._get = function(elForm, fnCallback)
{
  return function(evt)
  {
    fnCallback(elForm);

    var sVars = FormCollect(elForm);
    sVars += ((sVars) ? '&' : '') + "asynch=true";
    Net[elForm.method.toLowerCase()]({
      url: elForm.action,
      vars: sVars,
      onsuccess: nop
    });

    Events.preventDefault(evt);
    return false;
  }
}

Events.addHandler(window, "load", function()
{
  var elsForms = DOM.getByTag("form");
  var i=0, el = null, sHandler = '';
  while (el = elsForms[i++])
  {
    sHandler = el.getAttribute("client-handler");
    if (sHandler)
    {
      Events.addHandler(el, "submit",
        Eventful.formHandlers._get(el, Eventful.formHandlers[sHandler]));
    }
  }
});


/** FormCollect - Compiles string from form. - bfults@gmail.com - 2004-10-20 **
 ** Code licensed under Creative Commons Attribution-ShareAlike License      **
 ** http://creativecommons.org/licenses/by-sa/2.0/                           **/
function FormCollect(oArg)
{
  var sRetval = '', sTemp = '', sCTName = '', sCName = '', sCType = '';
  var oCurrent = null, aInput = [], arrElts = [];

  if (oArg.constructor == Array)
  {
    aInput = oArg;
  }
  else
  {
    aInput = oArg.elements;
  }

  for (var i = aInput.length - 1; i >= 0; i--)
  {
    oCurrent = aInput[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;
}
/** End FormCollect **/

/* add_favorite
 *
 * Add an item to a user's list of favorites.
 */
function add_favorite(user, type, item_id, label, disable_star) {
  var label = label || false;
  var disable_star = disable_star || false;

  if (label) {
    if (DOM.get("f_"+item_id)){
      var imFav = DOM.get("f_"+item_id);
      imFav.src = "/images/star-small-yellow.gif";
    }
    replaceContentsWithText('fav_label', "<a href=\"javascript:remove_favorite('" + user + "','" + type + "','" + item_id + "',true)\">Remove from favorites list</a>");
  }
  else {
    var elFav = DOM.get("f_"+item_id);
    elFav.style.backgroundImage = "url('/images/star-small-yellow.gif')";
  }

  // Remove the favorite.
  var url = "/users/" + user + "/favorites/add?item_id=" + item_id +
    "&type=" + type + "&time="+(new Date().valueOf()) + "&asynch=true";
  Net.post({url:url, vars: {asynch: 1}, onsuccess: refresh_favorite_callback(type, item_id, false, label, disable_star)});
}


/* remove_favorite
 *
 * Remove an item from a user's list of favorites.
 */
function remove_favorite(user, type, item_id, label, disable_star) {
  var label = label || false;
  var disable_star = disable_star || false;

  if (label) {
    if (DOM.get("f_"+item_id)){
      var imFav = DOM.get("f_"+item_id);
      imFav.src = "/images/star-small-grey.gif";
    }
    replaceContentsWithText('fav_label', "<a href=\"javascript:add_favorite('" + user + "','" + type + "','" + item_id + "',true)\">Add to favorites list</a>");
  }
  else {
    var elFav = DOM.get("f_"+item_id);
    elFav.style.backgroundImage = "url('/images/star-small-grey.gif')";
  }

  // Remove the favorite.
  var url = "/users/" + user + "/favorites/remove?item_id=" + item_id +
    "&type=" + type + "&time="+(new Date().valueOf()) + "&asynch=true";
  Net.post({url:url, vars: {asynch: 1}, onsuccess: refresh_favorite_callback(type, item_id, false, label, disable_star)});
}


function refresh_favorite_callback(type, item_id, refresh_page, label, disable_star) {
  label = label || false;
  disable_star = disable_star || false;
  return function() {
    var url = "/parts/favorite_contents.php?item_id=" + item_id + "&type=" + type + "&time="+(new Date().valueOf()) + "&asynch=true";
    if (label) {
      url += "&label=true";
    }
    if (disable_star) {
      url += "&disable_star=true";
    }
    replaceContentsWithURL("af_"+item_id, url);
    if(refresh_page) {
      window.location.reload();
    }
  }
}

// The following code is Copyright (C) Simon Willison 2004.
//
// document.getElementsBySelector(selector)
// - returns an array of element objects from the current document
//   matching the CSS selector. Selectors can contain element names,
//   class names and ids and can be nested. For example:
//     elements = document.getElementsBySelect('div#main p a.external')
// Version 0.4 - Simon Willison, March 25th 2003

function getAllChildren(e) {
  // Returns all children of element. Workaround required for IE5/Windows. Ugh.
  return e.all ? e.all : e.getElementsByTagName('*');
}

document.getElementsBySelector = function(selector) {
  Eventful.console.warn("Use of deprecated document.getElementsBySelector() function.");
  // Attempt to fail gracefully in lesser browsers
  if (!this.getElementsByTagName) {
    return new Array();
  }
  // Split selector in to tokens
  var tokens = selector.split(' ');
  var currentContext = new Array(this);
  for (var i = 0; i < tokens.length; i++) {
    token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
    if (token.indexOf('#') > -1) {
      // Token is an ID selector
      var bits = token.split('#');
      var tagName = bits[0];
      var id = bits[1];
      var element = this.getElementById(id);
      if (tagName && element.nodeName.toLowerCase() != tagName) {
        // tag with that ID not found, return false
        return new Array();
      }
      // Set currentContext to contain just this element
      currentContext = new Array(element);
      continue; // Skip to next token
    }
    if (token.indexOf('.') > -1) {
      // Token contains a class selector
      var bits = token.split('.');
      var tagName = bits[0];
      var className = bits[1];
      if (!tagName) {
        tagName = '*';
      }
      // Get elements matching tag, filter them for class selector
      var found = new Array;
      var foundCount = 0;
      for (var h = 0; h < currentContext.length; h++) {
        var elements;
        if (tagName == '*') {
            elements = getAllChildren(currentContext[h]);
        } else {
            elements = currentContext[h].getElementsByTagName(tagName);
        }
        for (var j = 0; j < elements.length; j++) {
          found[foundCount++] = elements[j];
        }
      }
      currentContext = new Array;
      var currentContextIndex = 0;
      for (var k = 0; k < found.length; k++) {
        if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
          currentContext[currentContextIndex++] = found[k];
        }
      }
      continue; // Skip to next token
    }
    // Code to deal with attribute selectors
    if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) { // "
      var tagName = RegExp.$1;
      var attrName = RegExp.$2;
      var attrOperator = RegExp.$3;
      var attrValue = RegExp.$4;
      if (!tagName) {
        tagName = '*';
      }
      // Grab all of the tagName elements within current context
      var found = new Array;
      var foundCount = 0;
      for (var h = 0; h < currentContext.length; h++) {
        var elements;
        if (tagName == '*') {
            elements = getAllChildren(currentContext[h]);
        } else {
            elements = currentContext[h].getElementsByTagName(tagName);
        }
        for (var j = 0; j < elements.length; j++) {
          found[foundCount++] = elements[j];
        }
      }
      currentContext = new Array;
      var currentContextIndex = 0;
      var checkFunction; // This function will be used to filter the elements
      switch (attrOperator) {
        case '=': // Equality
          checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
          break;
        case '~': // Match one of space seperated words
          checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
          break;
        case '|': // Match start with value followed by optional hyphen
          checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
          break;
        case '^': // Match starts with value
          checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
          break;
        case '$': // Match ends with value - fails with "Warning" in Opera 7
          checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
          break;
        case '*': // Match ends with value
          checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
          break;
        default :
          // Just test for existence of attribute
          checkFunction = function(e) { return e.getAttribute(attrName); };
      }
      currentContext = new Array;
      var currentContextIndex = 0;
      for (var k = 0; k < found.length; k++) {
        if (checkFunction(found[k])) {
          currentContext[currentContextIndex++] = found[k];
        }
      }
      // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
      continue; // Skip to next token
    }

    if (!currentContext[0]){
      return;
    }

    // If we get here, token is JUST an element (not a class or ID selector)
    tagName = token;
    var found = new Array;
    var foundCount = 0;
    for (var h = 0; h < currentContext.length; h++) {
      var elements = currentContext[h].getElementsByTagName(tagName);
      for (var j = 0; j < elements.length; j++) {
        found[foundCount++] = elements[j];
      }
    }
    currentContext = found;
  }
  return currentContext;
}

/* That revolting regular expression explained
/^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
  \---/  \---/\-------------/    \-------/
    |      |         |               |
    |      |         |           The value
    |      |    ~,|,^,$,* or =
    |   Attribute
   Tag
"*/

// END Simon Willison code

// function setInitialFocus() {
//   try {
//     var e = DOM.get(focusElement);
//     if (e && e.focus) {
//       e.focus();
//     }
//   }
//   catch (e) {
//     return false;
//   }
// }
// 
// 
// Events.addHandler(window, 'load', setInitialFocus);


/* Show
 *
 * Given an item ID, makes the item visible.  If an
 * optional select_item ID is given, after making item visible,
 * the input focus is sent to select_item.
 */
function Show(item,select_item)
{
  var theItem = DOM.get(item);
  theItem.style.display = "block";
  if(select_item)
  {
    var selectItem = DOM.get(select_item);
    selectItem.focus();
    selectItem.select();
  }
}


/* Hide
 *
 * Given an item ID, makes the item invisible.  If an
 * optional select_item ID is given, after making item invisible,
 * the input focus is sent to select_item. (hint: don't make the select_item
 * something within the now-hidden item, as it'll be invisible now)
 */
function Hide(item,select_item)
{
  var theItem = DOM.get(item);
  theItem.style.display = "none";
  if(select_item)
  {
    var selectItem = DOM.get(select_item);
    selectItem.focus();
    selectItem.select();
  }
}
