/*******************************************************************************
 * This notice must be untouched at all times.
 * 
 * This javascript library is a javascript collapsable list library.
 * It requires domHelpers.js, a cross browser DHTML library.
 * 
 * This program 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 of the License, or (at your option) any later
 * version. This program 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 at
 * http://www.gnu.org/copyleft/gpl.html for more details. 
 * 
 * This library is fully documented at http://www.dulac.ca/, along
 * with a whole suite of education, cross browser javascript libraries.
 ******************************************************************************/


/*
 * The CollapsableLists variable is a singleton object.
 * In the words of Connor McLeod of the Clan McLeod in
 * _The Highlander_, "There Can Be Only One" (and they
 * should have stopped at one Highlander film, but that
 * is another discussion).   
 */
var CollapsableLists = new function () {
	
	/*
	 * All the objects I declare have the "me" variable.  This
	 * is because this has a different meaning when being called
	 * by events that are in the object.
	 */
	var me = this;
	
	
	
	/*
	 * Public methods and variables are declared
	 * using the me.functionName syntax.  This method is public
	 * because it is called outside the object at the end of this
	 * .js file.
	 */
	me.item = new Array();
	
	me.init = function (e) {
		/*
		 * This method is called outside this object with the 
		 * EventHelpers.addPageLoadEvent() method.  This special event
		 * is not a standard event, and could potentially be called
		 * twice by Internet Explorer (once when the HTML of the page
		 * is fully loaded, and once when all the object on the page
		 * are loaded (i.e. a standard load event).  To prevent this
		 * from happening, this single-line if condition is included.
		 */
		if (EventHelpers.hasPageLoadHappened(arguments)) return;
		
		// Find all the tags of class collapsableList
		var HTMLItems = CSSHelpers.getElementsByClassName(document, "collapsableList");
		
		// Iterate through those tags, and if they are un/sorted list
		// tags, push a new CollapsableList Object associated with that
		// tag inside the item array.
		for (var i=0; i<HTMLItems.length; i++) {
			var HTMLItem = HTMLItems[i];
			var tagName = HTMLItem.tagName;
			
			/*
			 * the tagName property is always in uppercase.  I hate
			 * that, since it is the opposite of what the XHTML
			 * standard is, but that's the way it goes. 
			 */
			if (tagName == 'UL' || tagName == 'OL') {
				me.item.push(new CollapsableList(HTMLItem));
			}
		} 		
		
		
	}
}

/*
 * Note the difference between a singleton object
 * and a regular object.  
 */
function CollapsableList(node) {
	var me = this;
	me.timer = new Timer (me);
	
	var msToOpen, intervals, isAnimated;
	
	/*
	 *  public variables
	 */
	me.node = node;
	
	/*
	 * Private variables are declared with the var keyword.  They
	 * cannot be seen outside the object.  
	 */
	
	
	var HTMLLiElements;
	var HTMLLinks;
	
	
	
	var linkClassName = 'collapsableList-link'

	var exceptionPathPattern;
	
	/*
	 * The init() method of this object is private since it is not 
	 * called outside of the object, but at the end of the object
	 * definition.
	 */
	function init () {
		
		// Get all the <li> elements inside the collapsableList	
		HTMLLiElements = me.node.getElementsByTagName('li');
		
		msToOpen = config.getIntegerValue('collapsableList.values.msToOpen');
		intervals = config.getIntegerValue('collapsableList.values.intervals');
		isAnimated = eval(config.getValue('collapsableList.values.isAnimated'));
		
		var re = config.getValue('collapsableList.values.exceptionPathPattern')
		if (re) {
			exceptionPathPattern = new RegExp(re);
		} else {
			exceptionPathPattern = null;
		}
		// Put links inside the <li> tags that contain either
		// <ol> or <ul> tags.
		setLinks();
		
		// Set the events on those links
		setEvents();
		jslog.debug('####')

		openPageBranch();
		jslog.debug('####')
	}
	
	function setLinks() {
		/*
		 * This regular expression matches all printable characters 
		 * up to first <ol> tag or the first <ul> tag.  The ? at the 
		 * end of the first bracketed pattern indicated that this
		 * pattern is a non-greedy pattern.
		 */
		var branchClickRe = new RegExp("([a-zA-Z0-9.\/ \(\)<>\r\t\n]+?)<ul>", "i");
		var replaceString = '<a class="' + linkClassName + '" href="#">$1</a><ul>';
		
		var tmp;
		
		// for each of the <li> tags, put a link inside of the tag
		// if it contains a <ol> or <ul>.   I use a tmp variable
		// to store the HTML first just in case I need to debug,
		// since setting it to the innerHTML property of listItem
		// directly has some interesting side effects if the resultant
		// HTML is malformed.
		for (var i=0; i<HTMLLiElements.length; i++) {
			var listItem = HTMLLiElements[i];

			tmp = listItem.innerHTML;
			
			tmp = tmp.replace(branchClickRe, replaceString);
			listItem.innerHTML = tmp;
			

		}
		
		// We now set the object's private variable HTMLLinks to be all the
		// new links created by the above for loop (they all have the class
		// as set by the linkClassName variable
		HTMLLinks = CSSHelpers.getElementsByClassName(me.node, linkClassName);
		
		
	}
	

	function setEvents() {
		
		// for each of the new <a> tags created, set a click event
		// on it so that it toggles the visibility of it's <li>'s
		// "subList".
		for (var i=0; i<HTMLLinks.length; i++) {
			EventHelpers.addEvent(HTMLLinks[i], 'click', toggleCollapseEvent);
			
		}
	}
	
	/*
	function getTarget(evt) {
			var target = evt.originalTarget;
			if (target) {
				return target; 
			} else {
				return EventHelpers.getEventTarget(evt);
			}
	}
	*/
	
	function toggleCollapseEvent(evt) {
		var target = EventHelpers.getEventTarget(evt)
		
		var li = DOMHelpers.getAncestorByTagName(target, 'li');
		
		var subList = getSublist(li);
		
		if (isVisible(subList)) {
			me.removeVisibility(li, subList);
		} else {
			me.addVisibility(li, subList);
		}
		
		EventHelpers.preventDefault(evt);
	}
	
	
	function getSublist(li) {
		var subLists = li.getElementsByTagName('ul');
		var subList;
		
		if (subLists.length > 0) {
			subList = subLists[0];	
		} else {
			subList = null;
		}
		
		return subList;
	}
	
	function isVisible(subList) {
		if (CSSHelpers.isMemberOfClass(subList, 'collapsableList-visibleSublist')) {
			return true;
		} else {
			return false;
		}
	}
	
	me.addVisibility = function (li, subList) {
			if (isAnimated) {
				CSSHelpers.addClass(subList, 'collapsableList-animatedSublist');
				
				me.animatedOpen(li, subList, 0, 1, me.addVisibilityHelper);		
			} else {
				me.addVisibilityHelper(li, subList);
			}
	}
	
	me.addVisibilityHelper = function (li, subList) {
		CSSHelpers.addClass(subList, 'collapsableList-visibleSublist');
		CSSHelpers.addClass(li, 'collapsableList-expanded');
	}
	
	me.animatedOpen = function(li, subList, interval, step, onFinishEvent){
		if ((step >0 && interval >= intervals) || (step <0 && interval < 0)) {
			
			onFinishEvent(li, subList);
			return;
		} else {
			var fullHeight = subList.scrollHeight;
			var intervalHeight = fullHeight / intervals;
			subList.style.height = (intervalHeight * interval) + "px";
			me.timer.setTimeout('animatedOpen', msToOpen/intervals, li, subList, interval + step, step, onFinishEvent)
		}
	}
	
	
	
	me.removeVisibility = function (li, subList) {
			if (isAnimated) {
				CSSHelpers.addClass(subList, 'collapsableList-animatedSublist');
				me.animatedOpen(li, subList, intervals - 1, -1, me.removeVisibilityHelper);
				
			} else {
				me.removeVisibilityHelper(li, subList)
			}
			
	}
	
	me.removeVisibilityHelper = function (li, subList) {
		CSSHelpers.removeClass(subList, 'collapsableList-animatedSublist');
		CSSHelpers.removeClass(subList, 'collapsableList-visibleSublist');
		CSSHelpers.removeClass(li, 'collapsableList-expanded');
	}
	
	function getRelavantLinks(pathNamePrefix) {
		var links = cssQuery('a[href]');
		var r = new Array();
		
		for (var i=0; i<links.length; i++) {
			var link = links[i];
			var pathname = link.pathname 
			if (pathname.substring(0, 1) != '/') {
				pathname = "/" + pathname;
			}
			
			//if (link.pathname.indexOf(pathNamePrefix) == 0) {
			if (!link.href.match(exceptionPathPattern) && pathname == pathNamePrefix ) {
				
				r.push(link);
			}
		}
		return r;
	}
	
	function openPageBranch() {
		
		var links = getRelavantLinks(location.pathname); 
		
		
		for (var i=0; i<links.length; i++) {
			var link = links[i];
			
			if ( me.node.contains(link)) {
				
				var ulNode = DOMHelpers.getAncestorByTagName(link, 'UL');
				var liNode = DOMHelpers.getAncestorByTagName(link, 'LI');
				
				liNode = ulNode;
				do {
					
					
					liNode = DOMHelpers.getAncestorByTagName(liNode, 'LI');
					
				}
				while (liNode && liNode.nodeName == 'LI' && CSSHelpers.isMemberOfClass(liNode, 'childless'));
				
				if (liNode) {
					me.addVisibility(liNode, ulNode)
				}
				
				
			}	
			
		
			jslog.debug(!link.href.match(/#$/))
			// hilite appropriate section
			if (!link.href.match(/#$/)) {
				
				var children = DOMHelpers.getAllChildElements(me.node);
				
				for (var j = 0; j <= children.length; j++) {
					var child = children[j];
					
					if (child && child.contains(link)) {
						// hilite the node
						CSSHelpers.addClass(child, 'collapsableList-hilite');
						
					}
				}
			}
		}
		
	}
	
	init();
}

EventHelpers.addPageLoadEvent("CollapsableLists.init");


