/*
  collapse v1.0
  7 August 2008
  Nicolas Sierro

  Instructions:
    - Download this file
    - Add <script src="collapse.js"></script> to your HTML.
    - Add an id and class="collapsable" to any element you might like to collapse.
    - Click on the element to collapse or expend the elements it contains.

  This is code was based on:
    - Stuart Langridge's SortTable (http://kryogenix.org/code/browser/sorttable)

  Licensed under the BSD license.
 */

 
var stIsIE = /*@cc_on!@*/false;

collapse = {
  init: function() {
    // quit if this function has already been called
    if (arguments.callee.done) return;
    // flag this function so we don't do the same thing twice
    arguments.callee.done = true;
    // kill the timer
    if (_timer) clearInterval(_timer);
    
    if (!document.createElement || !document.getElementsByTagName) return;
    
    forEach(getElementsByClassName("collapsable"), function(element) {
        collapse.makeCollapsable(element);
    });
    
  },
  
  makeCollapsable: function(element) {
    element.collapse_status="false";
    indicator = document.createElement('span');
    indicator.innerHTML = stIsIE ? '<font face="webdings">6</font>&nbsp;' : '&#x25BC;&nbsp;';
    element.insertBefore(indicator,element.childNodes[0]);
    for (var i=1; i<element.childNodes.length;i++) {
      if (element.childNodes[i].nodeName && element.childNodes[i].nodeName.search("#") == -1) {
        element.childNodes[i].default_display=element.childNodes[i].style.display;
        dean_addEvent(element.childNodes[i],"click", function(e) {
          e.preventDefault();
          e.stopPropagation();
        });
      }
    }
    if (readCookie("collapse_"+element.parentNode.id+"_"+element.id))
      collapse.toggle(element);
    dean_addEvent(element,"click", function(e) {
      collapse.toggle(element);
    });
  },
  
  toggle: function(element) {
    element.collapse_status=element.collapse_status=="false"?"true":"false";
    collapse.status=!collapse.status;
    for (var i=1; i<element.childNodes.length;i++) {
      if (element.childNodes[i].nodeName && element.childNodes[i].nodeName.search("#") == -1) {
        if (element.collapse_status=="true")
          element.childNodes[i].style.display='none';
        else
          element.childNodes[i].style.display=element.childNodes[i].default_display;
      }
    }
    if (element.collapse_status=="true") {
      indicator = document.createElement('span');
      indicator.innerHTML = stIsIE ? '<font face="webdings">4</font>&nbsp;' : '&#x25B6;&nbsp;';
      element.replaceChild(indicator,element.childNodes[0]);
      createCookie("collapse_"+element.parentNode.id+"_"+element.id,"true",7);
    } else {
      indicator = document.createElement('span');
      indicator.innerHTML = stIsIE ? '<font face="webdings">6</font>&nbsp;' : '&#x25BC;&nbsp;';
      element.replaceChild(indicator,element.childNodes[0]);
      eraseCookie("collapse_"+element.parentNode.id+"_"+element.id);
    }
  }
}


/* ******************************************************************
   Supporting functions: bundled here to avoid depending on a library
   ****************************************************************** */

// Dean Edwards/Matthias Miller/John Resig
// has a hook for collapse.init already been added? (see below)
var collapseListenOnLoad = false;

/* for Mozilla/Opera9 */
if (document.addEventListener) {
    collapseListenOnLoad = true;
    document.addEventListener("DOMContentLoaded", collapse.init, false);
}

/* for Internet Explorer */
/*@cc_on @*/
/*@if (@_win32)
    collapseListenOnLoad = true;
    document.write("<script id=__collapse_onload defer src=javascript:void(0)><\/script>");
    var script = document.getElementById("__collapse_onload");
    script.onreadystatechange = function() {
        if (this.readyState == "complete") {
            collapse.init(); // call the onload handler
        }
    };
/*@end @*/

/* for Safari */
if (/WebKit/i.test(navigator.userAgent)) { // sniff
    collapseListenOnLoad = true;
    var _timer = setInterval(function() {
        if (/loaded|complete/.test(document.readyState)) {
            collapse.init(); // call the onload handler
        }
    }, 10);
}

/* for other browsers */
if (!collapseListenOnLoad) {
    window.onload = collapse.init;
}
// written by Dean Edwards, 2005
// with input from Tino Zijdel, Matthias Miller, Diego Perini

// http://dean.edwards.name/weblog/2005/10/add-event/

function dean_addEvent(element, type, handler) {
	if (element.addEventListener) {
		element.addEventListener(type, handler, false);
	} else {
		// assign each event handler a unique ID
		if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++;
		// create a hash table of event types for the element
		if (!element.events) element.events = {};
		// create a hash table of event handlers for each element/event pair
		var handlers = element.events[type];
		if (!handlers) {
			handlers = element.events[type] = {};
			// store the existing event handler (if there is one)
			if (element["on" + type]) {
				handlers[0] = element["on" + type];
			}
		}
		// store the event handler in the hash table
		handlers[handler.$$guid] = handler;
		// assign a global event handler to do all the work
		element["on" + type] = handleEvent;
	}
};
// a counter used to create unique IDs
dean_addEvent.guid = 1;

function removeEvent(element, type, handler) {
	if (element.removeEventListener) {
		element.removeEventListener(type, handler, false);
	} else {
		// delete the event handler from the hash table
		if (element.events && element.events[type]) {
			delete element.events[type][handler.$$guid];
		}
	}
};

function handleEvent(event) {
	var returnValue = true;
	// grab the event object (IE uses a global event object)
	event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
	// get a reference to the hash table of event handlers
	var handlers = this.events[event.type];
	// execute each event handler
	for (var i in handlers) {
		this.$$handleEvent = handlers[i];
		if (this.$$handleEvent(event) === false) {
			returnValue = false;
		}
	}
	return returnValue;
};

function fixEvent(event) {
	// add W3C standard event methods
	event.preventDefault = fixEvent.preventDefault;
	event.stopPropagation = fixEvent.stopPropagation;
	return event;
};
fixEvent.preventDefault = function() {
	this.returnValue = false;
};
fixEvent.stopPropagation = function() {
  this.cancelBubble = true;
}

// Dean's forEach: http://dean.edwards.name/base/forEach.js
/*
	forEach, version 1.0
	Copyright 2006, Dean Edwards
	License: http://www.opensource.org/licenses/mit-license.php
*/

// array-like enumeration
if (!Array.forEach) { // mozilla already supports this
	Array.forEach = function(array, block, context) {
		for (var i = 0; i < array.length; i++) {
			block.call(context, array[i], i, array);
		}
	};
}

// generic enumeration
Function.prototype.forEach = function(object, block, context) {
	for (var key in object) {
		if (typeof this.prototype[key] == "undefined") {
			block.call(context, object[key], key, object);
		}
	}
};

// character enumeration
String.forEach = function(string, block, context) {
	Array.forEach(string.split(""), function(chr, index) {
		block.call(context, chr, index, string);
	});
};

// globally resolve forEach enumeration
var forEach = function(object, block, context) {
	if (object) {
		var resolve = Object; // default
		if (object instanceof Function) {
			// functions have a "length" property
			resolve = Function;
		} else if (object.forEach instanceof Function) {
			// the object implements a custom forEach method so use that
			object.forEach(block, context);
			return;
		} else if (typeof object == "string") {
			// the object is a string
			resolve = String;
		} else if (typeof object.length == "number") {
			// the object is array-like
			resolve = Array;
		}
		resolve.forEach(object, block, context);
	}
};

var getElementsByClassName = function(className, parentElement) {
    // native
    if (document.getElementsByClassName) {
        return function(className, parentElement) {
            var s = (parentElement || document).getElementsByClassName(className);
            return [].slice.call(s, 0);
        }
    }
    // xpath
    if (document.evaluate) {
        return  function(className, parentElement) {
            if (!parentElement) parentElement = document;
            var results = [], s, i = 0, element;
            s = document.evaluate(
                ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]",
                parentElement,
                null,
                XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
                null
            );
            while ((element = s.snapshotItem(i++)))
                results.push(element);
            return results;
        }
    }
    // generic
    return function(className, parentElement) {
        if (!parentElement) parentElement = document
        var results = [], s, i = 0, element;
        var re = new RegExp('(^|\\s)' + className + '(\\s|$)'), elementClassName;
        s = parentElement.getElementsByTagName('*');
        while ((element = s[i++])) {
            if ((elementClassName = element.className) && (elementClassName == className || re.test(elementClassName)))
                results.push(element);
        }
        return results;
    }
}();
