/*

This file contains only functions necessary for the article features
The full library code and enhanced versions of the functions present
here can be found at http://v2studio.com/k/code/lib/


ARRAY EXTENSIONS

push(item [,...,item])
    Mimics standard push for IE5, which doesn't implement it.


find(value [, start])
    searches array for value starting at start (if start is not provided,
    searches from the beginning). returns value index if found, otherwise
    returns -1;


has(value)
    returns true if value is found in array, otherwise false;


FUNCTIONAL

map(list, func)
    traverses list, applying func to list, returning an array of values returned
    by func

    if func is not provided, the array item is returned itself. this is an easy
    way to transform fake arrays (e.g. the arguments object of a function or
    nodeList objects) into real javascript arrays.

    map also provides a safe way for traversing only an array's indexed items,
    ignoring its other properties. (as opposed to how for-in works)

    this is a simplified version of python's map. parameter order is different,
    only a single list (array) is accepted, and the parameters passed to func
    are different:
    func takes the current item, then, optionally, the current index and a
    reference to the list (so that func can modify list)


filter(list, func)
    returns an array of values in list for which func is true

    if func is not specified the values are evaluated themselves, that is,
    filter will return an array of the values in list which evaluate to true

    this is a similar to python's filter, but parameter order is inverted


DOM

getElem(elem)
    returns an element in document. elem can be the id of such element or the
    element itself (in which case the function does nothing, merely returning
    it)

    this function is useful to enable other functions to take either an    element
    directly or an element id as parameter.

    if elem is string and there's no element with such id, it throws an error.
    if elem is an object but not an Element, it's returned anyway


hasClass(elem, className)
    Checks the class list of element elem or element of id elem for className,
    if found, returns true, otherwise false.

    The tested element can have multiple space-separated classes. className must
    be a single class (i.e. can't be a list).


getElementsByClass(className [, tagName [, parentNode]])
    Returns elements having class className, optionally being a tag tagName
    (otherwise any tag), optionally being a descendant of parentNode (otherwise
    the whole document is searched)


DOM EVENTS

listen(event,elem,func)
    x-browser function to add event listeners

    listens for event on elem with func
    event is string denoting the event name without the on- prefix. e.g. 'click'
    elem is either the element object or the element's id
    func is the function to call when the event is triggered

    in IE, func is wrapped and this wrapper passes in a W3CDOM_Event (a faux
    simplified Event object)


mlisten(event, elem_list, func)
    same as listen but takes an element list (a NodeList, Array, etc) instead of
    an element.


W3CDOM_Event(currentTarget)
    is a faux Event constructor. it should be passed in IE when a function
    expects a real Event object. For now it only implements the currentTarget
    property and the preventDefault method.

    The currentTarget value must be passed as a paremeter at the moment    of
    construction.


MISC CLEANING-AFTER-MICROSOFT STUFF

isUndefined(v)
    returns true if [v] is not defined, false otherwise

    IE 5.0 does not support the undefined keyword, so we cannot do a direct
    comparison such as v===undefined.
*/

// ARRAY EXTENSIONS

if (!Array.prototype.push) Array.prototype.push = function() {
    for (var i=0; i<arguments.length; i++) this[this.length] = arguments[i];
    return this.length;
}

Array.prototype.find = function(value, start) {
    start = start || 0;
    for (var i=start; i<this.length; i++)
        if (this[i]==value)
            return i;
    return -1;
}

Array.prototype.has = function(value) {
    return this.find(value)!==-1;
}

// FUNCTIONAL

function map(list, func) {
    var result = [];
    func = func || function(v) {return v};
    for (var i=0; i < list.length; i++) result.push(func(list[i], i, list));
    return result;
}

function filter(list, func) {
    var result = [];
    func = func || function(v) {return v};
    map(list, function(v) { if (func(v)) result.push(v) } );
    return result;
}


// DOM

function getElem(elem) {
    if (document.getElementById) {
        if (typeof elem == "string") {
            elem = document.getElementById(elem);
            if (elem===null) throw 'cannot get element: element does not exist';
        } else if (typeof elem != "object") {
            throw 'cannot get element: invalid datatype';
        }
    } else throw 'cannot get element: unsupported DOM';
    return elem;
}

function hasClass(elem, className) {
    return getElem(elem).className.split(' ').has(className);
}

function getElementsByClass(className, tagName, parentNode) {
    parentNode = !isUndefined(parentNode)? getElem(parentNode) : document;
    if (isUndefined(tagName)) tagName = '*';
    return filter(parentNode.getElementsByTagName(tagName),
        function(elem) { return hasClass(elem, className) });
}


// DOM EVENTS

function listen(event, elem, func) {
    elem = getElem(elem);
    if (elem.addEventListener)  // W3C DOM
        elem.addEventListener(event,func,false);
    else if (elem.attachEvent)  // IE DOM
        elem.attachEvent('on'+event, function(){ func(new W3CDOM_Event(elem)) } );
        // for IE we use a wrapper function that passes in a simplified faux Event object.
    else throw 'cannot add event listener';
}

function mlisten(event, elem_list, func) {
    map(elem_list, function(elem) { listen(event, elem, func) } );
}

function W3CDOM_Event(currentTarget) {
    this.currentTarget  = currentTarget;
    this.preventDefault = function() { window.event.returnValue = false }
    return this;
}


// MISC CLEANING-AFTER-MICROSOFT STUFF

function isUndefined(v) {
    var undef;
    return v===undef;
}
// the functions in this file require the supplementary library lib.js

// These defaults should be changed the way it best fits your site
var _POPUP_FEATURES = '';

function raw_popup(url, target, features) {
    // pops up a window containing url optionally named target, optionally having features
    if (isUndefined(features)) features = _POPUP_FEATURES;
    if (isUndefined(target  )) target   = '_blank';
    var theWindow = window.open(url, target, features);
    theWindow.focus();
    return theWindow;
}

function link_popup(src, features) {
    // to be used in an html event handler as in: <a href="..." onclick="link_popup(this,...)" ...
    // pops up a window grabbing the url from the event source's href
    return raw_popup(src.getAttribute('href'), src.getAttribute('target') || '_blank', features);
}

function event_popup(e) {
    // to be passed as an event listener
    // pops up a window grabbing the url from the event source's href
    link_popup(e.currentTarget);
    e.preventDefault();
}

function event_popup_features(features) {
    // generates an event listener similar to event_popup, but allowing window features
    return function(e) { link_popup(e.currentTarget, features); e.preventDefault() }
}



/************************************************************************************/
/*			Zebra Tables (from A List Apart - http://www.alistapart.com)			*/
/************************************************************************************/

// this function is needed to work around 
// a bug in IE related to element attributes
function hasClass(obj) {
	var result = false;
	if (obj.className != null) {
		result = obj.className.value;
	}
	return result;
}   

function stripe(id) {
	
	// the flag we'll use to keep track of 
	// whether the current row is odd or even
	var even = false;
	
	// if arguments are provided to specify the colours
	// of the even & odd rows, then use the them;
	// otherwise use the following defaults:
	var evenColor = arguments[1] ? arguments[1] : "#fff";
	var oddColor = arguments[2] ? arguments[2] : "#e5e5e6";
	
	// obtain a reference to the desired table
	// if no such table exists, abort
	var table = document.getElementById(id);
	if (! table) { return; }
		
	// by definition, tables can have more than one tbody
	// element, so we'll have to get the list of child
	// &lt;tbody&gt;s 
	var tbodies = table.getElementsByTagName("tbody");
	
	// and iterate through them...
	for (var h = 0; h < tbodies.length; h++) {
		
		// find all the &lt;tr&gt; elements... 
		var trs = tbodies[h].getElementsByTagName("tr");
		
		// ... and iterate through them
		for (var i = 0; i < trs.length; i++) {
			
			// avoid rows that have a class attribute
			// or backgroundColor style
			if (! hasClass(trs[i]) &&
				! trs[i].style.backgroundColor) {
				
				// get all the cells in this row...
				var tds = trs[i].getElementsByTagName("td");
				
				// and iterate through them...
				for (var j = 0; j < tds.length; j++) {
					
					var mytd = tds[j];
					
					// avoid cells that have a class attribute
					// or backgroundColor style
					if (! hasClass(mytd) &&	! mytd.style.backgroundColor) {
					
						mytd.style.backgroundColor =
						even ? evenColor : oddColor;
					
					}
				}
				
				//Added 24/3/2004 Chris McLeod: Get any th's in a row:
				var ths = trs[i].getElementsByTagName("th");
				// and iterate through them...
				for (var j = 0; j < ths.length; j++) {
					
					var myth = ths[j];
					
					// avoid cells that have a class attribute
					// or backgroundColor style
					if (! hasClass(myth) &&	! myth.style.backgroundColor) {
					
						myth.style.backgroundColor =
						even ? evenColor : oddColor;
					
					}
				}
				
			}
			// flip from odd to even, or vice-versa
			even =  ! even;
		}
	}
}

function stripeTables(){
	var tables = document.getElementsByTagName('table');
	if (tables) {
		for (var ti=0;ti<tables.length;ti++) {
			var tbl = tables[ti];
			if (tbl.id) {
				stripe(tbl.id);
			}
		}
	}
}

// original code by Stuart Langridge 2003-11
// with additions to the code by other good people
// http://www.kryogenix.org/code/browser/nicetitle/
// thank you, sir

// modified by Peter Janes 2003-03-25
// http://peterjanes.ca/blog/archives/2003/03/25/nicetitles-for-ins-and-del
// added in ins and del tags

// modified by Dunstan Orchard 2003-11-18
// http://www.1976design.com/blog/
// added in accesskey information
// tried ever-so-hard, but couldn't work out how to do what Ethan did

// final genius touch by by Ethan Marcotte 2003-11-18
// http://www.sidesh0w.com/
// worked out how to delay showing the popups to make them more like the browser's own



var XHTMLNS = "http://www.w3.org/1999/xhtml";
var CURRENT_NICE_TITLE;
var browser = new Browser();

function makeNiceTitles() {
    if (!document.createElement || !document.getElementsByTagName) return;
    // add namespace methods to HTML DOM; this makes the script work in both
    // HTML and XML contexts.
    if(!document.createElementNS)
    {
        document.createElementNS = function(ns,elt) {
            return document.createElement(elt);
        }
    }
	var sptags = document.getElementsByTagName('span');
    if (sptags) {
		for (var ti=0;ti<sptags.length;ti++) {
			var sptag = sptags[ti];
			if (sptag.title) {
				sptag.setAttribute("nicetitle",sptag.title);
				sptag.removeAttribute("title");
				addEvent(sptag,"mouseover",showNiceTitle);
				addEvent(sptag,"mouseout",hideNiceTitle);
				addEvent(sptag,"focus",showNiceTitle);
				addEvent(sptag,"blur",hideNiceTitle);
			}
		}
    }
	
}

function findPosition( oLink ) {
  if( oLink.offsetParent ) {
    for( var posX = 0, posY = 0; oLink.offsetParent; oLink = oLink.offsetParent ) {
      posX += oLink.offsetLeft;
      posY += oLink.offsetTop;
    }
    return [ posX, posY ];
  } else {
    return [ oLink.x, oLink.y ];
  }
}

function showNiceTitle(e) {
    if (CURRENT_NICE_TITLE) hideNiceTitle(CURRENT_NICE_TITLE);
    if (!document.getElementsByTagName) return;
    if (window.event && window.event.srcElement) {
        lnk = window.event.srcElement
    } else if (e && e.target) {
        lnk = e.target
    }
    if (!lnk) return;
    if (lnk.nodeType == 3) {
        // lnk is a textnode -- ascend parents until we hit a link
        lnk = getParent(lnk,"A");
    }
    if (!lnk) return;
    nicetitle = lnk.getAttribute("nicetitle");
    
    var d = document.createElementNS(XHTMLNS,"div");
    d.className = "nicetitle";
    tnt = document.createTextNode(nicetitle);
    pat = document.createElementNS(XHTMLNS,"p");
    pat.className = "titletext";
    pat.appendChild(tnt);
    d.appendChild(pat);
    if (lnk.href) {
        tnd = document.createTextNode(lnk.href);
        pad = document.createElementNS(XHTMLNS,"p");
        pad.className = "destination";
        pad.appendChild(tnd);
        d.appendChild(pad);
    }
    
    STD_WIDTH = 320;
	w = STD_WIDTH;
	if (browser.isIE) {
		mpos = getMousePosition(lnk);
	}
	if (browser.isNS) {
		mpos = findPosition(lnk);
	}
    
    mx = mpos[0];
    my = mpos[1];
    
    d.style.left = (mx+15) + 'px';
    d.style.top = (my+20) + 'px';
    if (window.innerWidth && ((mx+w) > window.innerWidth)) {
        d.style.left = (window.innerWidth - w - 25) + "px";
    }
    if (document.body.scrollWidth && ((mx+w) > document.body.scrollWidth)) {
        d.style.left = (document.body.scrollWidth - w - 25) + "px";
    }
    
    document.getElementsByTagName("body")[0].appendChild(d);
    
    CURRENT_NICE_TITLE = d;
}

function hideNiceTitle(e) {
    if (!document.getElementsByTagName) return;
    if (CURRENT_NICE_TITLE) {
        document.getElementsByTagName("body")[0].removeChild(CURRENT_NICE_TITLE);
        CURRENT_NICE_TITLE = null;
    }
}

// Add an eventListener to browsers that can do it somehow.
// Originally by the amazing Scott Andrew.
function addEvent(obj, evType, fn){
  if (obj.addEventListener){
    obj.addEventListener(evType, fn, true);
    return true;
  } else if (obj.attachEvent){
	var r = obj.attachEvent("on"+evType, fn);
    return r;
  } else {
	return false;
  }
}

function getParent(el, pTagName) {
	if (el == null) return null;
	else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase())	// Gecko bug, supposed to be uppercase
		return el;
	else
		return getParent(el.parentNode, pTagName);
}

function getMousePosition(event) {
  if (browser.isIE) {
    x = window.event.clientX + document.documentElement.scrollLeft
      + document.body.scrollLeft;
    y = window.event.clientY + document.documentElement.scrollTop
      + document.body.scrollTop;
  }
  if (browser.isNS) {
    x = event.clientX + window.scrollX;
    y = event.clientY + window.scrollY;
  }
  return [x,y];
}

// Determine browser and version.

function Browser() {
// blah, browser detect, but mouse-position stuff doesn't work any other way
  var ua, s, i;

  this.isIE    = false;
  this.isNS    = false;
  this.version = null;

  ua = navigator.userAgent;

  s = "MSIE";
  if ((i = ua.indexOf(s)) >= 0) {
    this.isIE = true;
    this.version = parseFloat(ua.substr(i + s.length));
    return;
  }

  s = "Netscape6/";
  if ((i = ua.indexOf(s)) >= 0) {
    this.isNS = true;
    this.version = parseFloat(ua.substr(i + s.length));
    return;
  }

  // Treat any other "Gecko" browser as NS 6.1.

  s = "Gecko";
  if ((i = ua.indexOf(s)) >= 0) {
    this.isNS = true;
    this.version = 6.1;
    return;
  }
}



/************************************************************************************/
/*			Style Switcher (from A List Apart - http://www.alistapart.com)			*/
/************************************************************************************/

function setActiveStyleSheet(title) {
  var i, a, main;
  for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
    if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")) {
      a.disabled = true;
      if(a.getAttribute("title") == title) a.disabled = false;
    }
  }
}

function getActiveStyleSheet() {
  var i, a;
  for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
    if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title") && !a.disabled) return a.getAttribute("title");
  }
  return null;
}

function getPreferredStyleSheet() {
  var i, a;
  for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
    if(a.getAttribute("rel").indexOf("style") != -1
       && a.getAttribute("rel").indexOf("alt") == -1
       && a.getAttribute("title")
       ) return a.getAttribute("title");
  }
  return null;
}

function createCookie(name,value,days) {
  if (days) {
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
  }
  else expires = "";
  document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
  var nameEQ = name + "=";
  var ca = document.cookie.split(';');
  for(var i=0;i < ca.length;i++) {
    var c = ca[i];
    while (c.charAt(0)==' ') c = c.substring(1,c.length);
    if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
  }
  return null;
}

window.onload = function(e) {
  var cookie = readCookie("style");
  var title = cookie ? cookie : getPreferredStyleSheet();
  setActiveStyleSheet(title);
}

window.onunload = function(e) {
  var title = getActiveStyleSheet();
  createCookie("style", title, 365);
}

var cookie = readCookie("style");
var title = cookie ? cookie : getPreferredStyleSheet();
setActiveStyleSheet(title);


/****************************************************************************/

// start everything off on window load
addEvent(window, "load", stripeTables);
addEvent(window, "load", makeNiceTitles);