// A simple script to turn a table into a slide-menu
// See README for documentation
// License: Public Domain, use how you want
var SlideMenu = Class.create();
Object.extend(SlideMenu.prototype, {
	initialize: function(element, options) {
		this.element = $(element);
		(new Selector('*')).findElements(element).each(function(e) {
			Element.cleanWhitespace(e);
		});
		var openCloseSwap = {
			up:   'down',
			open: 'closed',
			on:   'off'
		};
		this.options = $H(Object.extend({
			autoClose: false,
			scrollContents: true,
			showContentsEffect: window['Effect'] == null
				? Element.show : Effect.SlideDown,
			hideContentsEffect: window['Effect'] == null
				? Element.hide : Effect.SlideUp
		}, options));
		this.options.openCloseSwap = $H(Object.extend(openCloseSwap,
			this.options.openCloseSwap));

		if( Element.hasClassName(this.element, 'auto-close') )
			this.options.autoClose = true;

		var visible = document.getNodePath('tbody/tr/td', this.element).
			findAll(function(e) { return Element.visible(e); });
		visible.invoke('hide');
		this.content_size = Element.getDimensions(this.element).height;
		var parent = this.element.parentNode;
		while(Element.getDimensions(parent).height == this.content_size) {
			parent = parent.parentNode;
		}
		this.content_size =
			Element.getDimensionsWithoutBorders(parent).height;
		this.headers().each((function(e) {
			this.content_size -= Element.getDimensions(e).height;
		}).bind(this));
		visible.invoke('show');

		document.getNodePath('tbody/tr/td', this.element).each(
			(function(e) {
			wrapper = document.createElement('div');
			while(e.childNodes.length != 0 ) {
				wrapper.appendChild(e.firstChild);
			}
			e.appendChild(wrapper);
			if( !Element.visible(e) ) {
				Element.hide(wrapper);
				Element.show(e);
			}
			if(Element.getDimensions(wrapper).height>this.content_size ) {
				Element.setStyle(wrapper, {
					overflow: 'auto',
					height: this.content_size + 'px'
				});
				wrapper.scrollable = true;
			}
		}).bind(this));

		this.headers().each((function(header) {
			header.style.cursor = 'pointer';
			Event.observe(header, 'click',
				this.headerClick.bindAsEventListener(this));
		}).bind(this));
	},

	headerClick: function(event) {
		var header = this.headers().detect(function(header) {
			return (header == Event.element(event)) ||
				Element.childOf(Event.element(event), header);
		});
		this.toggleHeader(header);
	},

	toggleHeader: function(header) {
		var content = this.contentFor(header);
		Element.visible(content)
			? this.closeHeader(header) : this.openHeader(header);
	},

	closeHeader: function(header) {
		var content = this.contentFor(header);
		if (!Element.visible(content)) return;
		this.options.hideContentsEffect(content, {
			beforeStart: function(e) {
				if( e.element.scrollable )
					e.element.style.overflow = 'hidden';
			}
		});
		this.handleSwap(header, this.options.openCloseSwap);
	},

	openHeader: function(header) {
		var content = this.contentFor(header);
		if (Element.visible(content)) return;
		this.options.showContentsEffect(content, {
			beforeStart: function(e) {
				if( e.element.scrollable )
					e.element.style.overflow = 'hidden';
			},
			afterFinish: function(e) {
				if( e.element.scrollable )
					e.element.style.overflow = 'auto';
			}
		});
		this.handleSwap(header, this.options.openCloseSwap.invert());
		if( this.options.autoClose ) {
			this.headers().without(header).each((function(h) {
				this.closeHeader(h);
			}).bind(this));
		}
	},

	handleSwap: function(header, swapText) {
		document.getElementsByClassName('handle', header).each(
			function(handle) {
			swapText.each(function(pair) {
				var re = new RegExp('([^\/]+)'+pair[0]+'([^\/]+)$');
				handle.setAttribute('src',
					handle.getAttribute('src').gsub(
					re, '#{1}'+pair[1]+'#{2}'));
			});
		});
	},

	contentPanes: function() {
		return document.getNodePath('tbody/tr/td/div', this.element);
	},

	headers: function() {
		return document.getNodePath('tbody/tr/th', this.element);
	},

	contentFor: function(header) {
		return header.parentNode.nextSibling.firstChild.firstChild;
	}
});

/* We really should use something like XPath or CSS selectors but prototype's
CSS selectors doesn't support element relationship specifications and
including a XPath library would be overkill since we want to keep it simple.
If you give this function something like "tr/td/a" it will return all <a>
elements that are direct children of <td> elements that are direct children of
<tr> elements which are direct children of the context node.

Anything more complicated and we should just add support to Prototype's
selectors or use a XPath library. Otherwise we are just reinventing the
wheel badly */
document.getNodePath = function(path, context) {
	path = path.split(/\W+/);
	return $A($(context).childNodes).reject(function(e) {
		return !e.tagName || (e.tagName.toLowerCase() != path[0]);
	}).collect(function(e) {
		return path.length > 1 ?
			document.getNodePath(path.slice(1).join('/'), e) : e;
	}).flatten();
};

Object.extend(Hash, {
	invert: function() {
		var ret = $H();
		this.each(function(pair) {
			ret[pair[1]] = pair[0];
		});
		return ret;
	}
});

Object.extend(Element, {
	getDimensionsWithoutBorders: function(ele) {
		var dimensions = Element.getDimensions(ele);
		var borders = $H({
			'border-top-width': 0,
			'border-right-width': 0,
			'border-bottom-width': 0,
			'border-left-width': 0
		});
		borders.each(function(bdr) {
			b = Element.getStyle(ele, bdr[0]);

			// Hack for IE becuase it default to medium. Is there
			// anyway to query a browser to find out what
			// thin, medium and thick is?
			if( b == 'medium' ) b = '2px';


			b = parseInt(b);
			if( b ) borders[bdr[0]] = parseInt(b);
		});

		dimensions.height = dimensions.height -
			borders['border-top-width'] -
			borders['border-bottom-width'];
		dimensions.width = dimensions.width -
			borders['border-left-width'] -
			borders['border-right-width'];
		return dimensions;
	}
});

var sliders = new Array();
(function(){
	var func = function() {
		sliders = $$('table.slide-menu').collect(function(element) {
			return new SlideMenu(element);
		});
	}
	var oldonload = window.onload;
	window.onload = typeof window.onload != 'function' ?
		func : function() {oldonload();func();};
})();
