
var ACCORDION_RENDER_DELAY = 750;

// Accordion constructor.
function acc(id, url) {
	var me = this;
	this.id = id;
	this.links = e_enum(id);
	this.panels = Array();
	this.url = url;
	this.i = -1; // Currently open panel.
	this.loaded = false;
	this.ajax = window.XMLHttpRequest ? new XMLHttpRequest : new ActiveXObject("Microsoft.XMLHTTP");
	this.cssLoaded = Array();
	this.jsLoaded = Array();
	var i;
	var makef = function(j) { return function() { me.linkClick(j); return false; } }
	for(i in this.links) {
		me.links[i].href = null;
		me.links[i].onclick = makef(i);
		me.panels[i] = document.createElement("div");
		me.panels[i].className = "acc-panel posHide";
		var nexte = me.links[i].nextSibling;
		if(nexte) nexte.parentNode.insertBefore(me.panels[i], nexte);
		else me.panels[i].parentNode.appendChild(me.panels[i]);
	}
}

// Panel header click callback.
acc.prototype.linkClick = function(i) {
	if(!this.loaded) {
		this.load(this.url);
	}
	this.links[i].blur();
	this.toggle(i);
}

// External code should use this to set and load an url.
acc.prototype.extLoad = function(url, open) {
	this.load(url);
	if(open) if(this.i == -1) this.toggle(0);
}

// Set all the panels to 'loading...', get the url.
acc.prototype.load = function(url) {
	this.loaded = true;
	if(this.pendingTime) clearTimeout(this.pendingTime);
	var me = this;
	var j;
	var s = this.ajax.readyState;
	if((s != 0) && (s != 4)) this.ajax.abort();
	else for(j in this.panels) {
		var h = this.panels[j].offsetHeight;
		rem(this.panels[j], "acc-error");
		add(this.panels[j], "acc-loading");
		this.panels[j].innerHTML = '<div class="acc-message">loading...</div>';
		if(h > 198) this.panels[j].style.height = h + "px";
	}
	this.ajax.open("get", url);
	this.ajax.onreadystatechange = function() {
		if(me.ajax.readyState == 4) {
			if(me.ajax.status == 200) {
				var dom = me.ajax.responseXML;
				if(dom && dom.firstChild) { me.parseDOM(dom); }
				else if(me.ajax.responseText) me.handleError(me.ajax.responseText);
				else me.handleError("Empty response.");
			}
			else me.handleError(me.ajax.statusText);
		}
	}
	this.ajax.send('');
}

// Open panel i, or close it if it already is.
acc.prototype.toggle = function(i) {
	if(this.i == i) { this.close(i); this.i = -1; }
	else if(this.i != -1) { this.close(this.i); this.open(i); this.i = i; }
	else { this.open(i); this.i = i; }
}

// Style toggling functions.
acc.prototype.open = function(i) {
	rem(this.panels[i], "posHide");
	invalidate(this.panels[i]);
	add(this.links[i], "open"); 
}
acc.prototype.close = function(i) {
	add(this.panels[i], "posHide");
	rem(this.links[i], "open"); 
}

// Parse a successfully retrieved dom.
acc.prototype.parseDOM = function(dom) {
	var me = this;
	var node = dom.firstChild.firstChild;
	this.pending = Array();
	this.pendingScript = null;
	do {
		if(node.nodeName == "panel") this.pending[this.pending.length] = node.firstChild.nodeValue;
		else if(node.nodeName == "script") {
			this.pendingScript = node.firstChild.nodeValue;
		}
		else if(node.nodeName == "style") {
			var src = node.getAttribute('src');
			if(!this.cssLoaded[src]) {
				this.cssLoaded[src] = true;
				var style = document.createElement("link");
				style.rel = "stylesheet"; style.media = "all"; style.type = "text/css";
				style.href = node.getAttribute('src');
				document.documentElement.childNodes[0].appendChild(style);
			}
		}
	}
	while(node = node.nextSibling);		
	var render = function() { me.renderPending(); }
	this.pendingTime = setTimeout(render, ACCORDION_RENDER_DELAY);
	this.onParsedDOM(dom);
}

// Display an error.
acc.prototype.handleError = function(error) {
	for(j in this.panels) {
		rem(this.panels[j], "acc-loading");
		add(this.panels[j], "acc-error");
		this.panels[j].innerHTML = '<div class="acc-message">'+ error +'</div>';
	}
}

// Render pending stylesheets after a delay to allow stylesheets to load.
acc.prototype.renderPending = function() {
	var j;
	for(j in this.panels) {
		this.panels[j].style.height = "";
		rem(this.panels[j], "acc-loading");
		this.panels[j].innerHTML = this.pending[j];
	}
	if(this.pendingScript) eval(this.pendingScript);
	this.onDoneRendering();
}

// Redefinable.
acc.prototype.onParsedDOM = function(){};
acc.prototype.onDoneRendering = function(){};