// JavaScript Document

function MessagesScroller()
{
	this.scroll_speed = 200;  // The time interval for each movement of layer
	this.scroll_step = 4;     // The pixels to move when the layer scrolls
	this.scroll_delay = 3000; // The time to pause when the layer become visible
	this.scroll_default_layer_gap = 20; // Pixels between two consective layers
	this.scroll_disabled = false;
	this.src_url = "";
	this.src_url_args  = "";
	this.container_id = "";
	this.layer_classname = "";
	this.layer_ids = [];
	this.layer_gaps = [];
	this.interval_id = 0;
	this.layer_enumerator = new LayerCyclicEnumerator(this.layer_ids);
}

MessagesScroller.prototype.setContainerId = function (id)
{
	this.container_id  = id;
}

MessagesScroller.prototype.setLayerClassname = function (name)
{
	this.layer_classname  = name;
}

MessagesScroller.prototype.loadMessages = function (url, url_args)
{
	var that = this;
	url_args = (typeof(url_args) == "undefined" || url_args == null ? "" : url_args);
	this.src_url = url;
	this.src_url_args = url_args;
	do_xhr("GET", this.src_url, this.src_url_args, function(r) {
		that.XMLRequestCallback(r);
	});
}

MessagesScroller.prototype.XMLRequestCallback = function (result)
{
	if (result == null || result.responseXML == null || result.responseXML.documentElement == null)
		return;
	
	if (this.container_id == "")
		return;

	var container = document.getElementById(this.container_id);
	if (container == null)
		return;

	var tree = getSingleElementByTagName(result.responseXML.documentElement, "scrollingMessages");
	if (tree == null)
		return;

	// Load the "message" nodes from the XML file into the html DOM
	var layers_total_height = 0;
	var msgs = tree.getElementsByTagName("message");
	for (var i=0; i < msgs.length; i++) {
		var msg = msgs[i];
		var enabled = stringToBoolean(getNodeValue(msg, "enabled"));
		if (enabled) {
			var content = getSingleElementByTagName(msg, "content");
			if (content) {
				var clonedContent = document._importNode(content, true);
				var div_id = this.container_id+"-msg"+(i+1);
				var div = document.createElement("DIV");
				div.setAttribute("id", div_id);
				if (this.layer_name != "") {
					div.setAttribute("class", this.layer_classname);
					div.setAttribute("className", this.layer_classname);
				}
				for (var k=0; k < clonedContent.childNodes.length; k++) {
					div.appendChild(clonedContent.childNodes[k].cloneNode(true));
				}
				this.layer_ids.push(div_id);
				this.layer_gaps.push(this.scroll_default_layer_gap);
				div.style.visibility = "hidden";
				container.appendChild(div);
				layers_total_height += div.offsetHeight;
			}
		}
	}

/*
	// Extend the tail layer gap if those layers cannot fit into the container's window
	for (var h=0; i < this.layer_ids.length; h++) {
		var c_height = 0;
		var layer = document.getElementById(this.layer_ids[h]);
		if (layer == null)
			continue;

		for (var k=0; i < this.layer_ids.length; k++) {
			if (k == h)
				continue;
			var layer_b = document.getElementById(this.layer_ids[k]);
			if (layer_b) {
				c_height += layer_b.offsetHeight + this.layer_gaps[k];
			}
		}

		// Adjust the previous layer gap if current set of layers cannot fit
		if (c_height < container.offsetHeight) {
			var prev_layer_idx = (k  - 1) % this.layer_ids.length;
			this.layer_gaps[k] = container.offsetHeight - c_height;
		}
		
	}
*/

	// Set head layer
	this.layer_enumerator.setStartIndex(0);
	var layer = this.layer_enumerator.getCurrentLayer();
	if (layer) {
		layer.style.visibility = "visible";
	}
}

MessagesScroller.prototype.scrollupLayers = function ()
{
	if (this.scroll_disabled)
		return;

	var container = document.getElementById(this.container_id);
	var head_layer = this.layer_enumerator.moveStart();
	var head_layer_index = this.layer_enumerator.getCurrentIndex();
	if (container == null || head_layer == null)
		return;

	var tail_layer_edge = head_layer.offsetTop - this.scroll_step;
	var container_edge = container.offsetHeight;
	var container_padding_top = getElementStyle(container, "padding-top", true);

	// Scroll the layers within the container's window
	for (var layer = head_layer; layer != null; layer = this.layer_enumerator.moveNext()) {
		layer.style.visibility = "visible";
		layer.style.top = tail_layer_edge + "px";
		tail_layer_edge += layer.offsetHeight + this.layer_gaps[this.layer_enumerator.getCurrentIndex()];
		
		if (tail_layer_edge >= container_edge)
			break;
	}

	// Hide the head layer when it scrolled out of the container's window
	if (head_layer.offsetTop <= -(head_layer.offsetHeight + this.layer_gaps[head_layer_index] - container_padding_top)) {
		head_layer.style.visibility = "hidden";
		this.layer_enumerator.moveStart();
		this.layer_enumerator.moveNextCyclic();
		this.layer_enumerator.setStartIndex(this.layer_enumerator.getCurrentIndex());

		// Let the new head layer be displayed for scroll_delay milliseconds
		var that = this;
		this.scroll_disabled = true;
		setTimeout(function() {
			that.enableScroll();
		}, this.scroll_delay);
	}

	return;
}

MessagesScroller.prototype.enableScroll = function ()
{
	this.scroll_disabled = false;
}

MessagesScroller.prototype.startScrolling = function ()
{
	var that = this;
	this.interval_id = setInterval(function() {
		that.scrollupLayers();
	}, this.scroll_speed);

	this.scroll_disabled = true;
	setTimeout(function() {
		that.enableScroll();
	}, this.scroll_delay);
}


//-------------------------------------------------------------------------

function LayerCyclicEnumerator(layer_ids)
{
	this.start_index = 0;
	this.end_index = 0;
	this.current_index = 0;
	this.layer_ids = layer_ids;
}

LayerCyclicEnumerator.prototype.setStartIndex = function(idx)
{
	this.start_index = idx;
	this.end_index = (idx - 1) % this.layer_ids.length;
	this.end_index += (this.end_index < 0 ? this.layer_ids.length : 0);
	this.current_index = idx;

	if (this.getCurrentLayer() == null) {
		while (this.current_index != this.end_index && this.moveNext() == null) {};
		this.start_index = this.current_index;
		this.current_index = this.end_index;
	}

	if (this.getCurrentLayer() == null) {
		while (this.current_index != this.start_index && this.movePrevious() == null) {};
		this.end_index = this.current_index;
		this.current_index = this.start_index;
	}

	return this.start_index;
}

LayerCyclicEnumerator.prototype.moveBy = function(start, step, cyclic)
{
	var idx = start;
	var layer = null;
	var count = 0;

	if (!cyclic && step < 0 && this.current_index == this.start_index)
		return null;

	if (!cyclic && step > 0 && this.current_index == this.end_index)
		return null;

	do {
		idx = (idx + step)  % this.layer_ids.length;
		idx += (idx < 0 ? this.layer_ids.length : 0);
		layer = document.getElementById(this.layer_ids[idx]);
		count += step;
	} while (layer == null && count < this.layer_ids.length && step != 0);

	this.current_index = idx;
	return layer;
}

LayerCyclicEnumerator.prototype.moveStart = function() {	return this.moveBy(this.start_index, 0, false); }
LayerCyclicEnumerator.prototype.moveEnd = function()  { return this.moveBy(this.end_index, 0, false); }
LayerCyclicEnumerator.prototype.moveNext = function() { return this.moveBy(this.current_index, 1, false); }
LayerCyclicEnumerator.prototype.movePrevious = function() {	return this.moveBy(this.current_index, -1, false); }
LayerCyclicEnumerator.prototype.moveNextCyclic = function() { return this.moveBy(this.current_index, 1, true); }
LayerCyclicEnumerator.prototype.movePreviousCyclic = function() {	return this.moveBy(this.current_index, -1, true); }
LayerCyclicEnumerator.prototype.getLayerByIndex = function(idx) {	return document.getElementById(this.layer_ids[idx]); }
LayerCyclicEnumerator.prototype.getCurrentIndex = function() { return this.current_index; }
LayerCyclicEnumerator.prototype.getCurrentLayer = function() { return this.getLayerByIndex(this.current_index); }

//-------------------------------------------------------------------------

function getSingleElementByTagName(parent, name)
{
	var elements = parent.getElementsByTagName(name);
	return (elements.length > 0 ? elements[0] : null);
}

function getNodeValue(parent, name)
{
	var element = getSingleElementByTagName(parent, name);
	if (element) {
		return element.firstChild.nodeValue;
	}
	return "";
}

function stringToBoolean(string)
{
	switch (string.toLowerCase()) {
		case "true": case "yes": case "1": return true;
		case "false": case "no": case "0": case null: return false;
		default: return Boolean(string);
	}
}

function getElementStyle(oElm, strCssRule, pixelValue)
{
	var strValue = "";

	if (document.defaultView && document.defaultView.getComputedStyle) {
		strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
	} else if (oElm.currentStyle) {
		strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1) {
			return p1.toUpperCase();
		});

		strValue = oElm.currentStyle[strCssRule];

		// Convert none "px" result to pixel value (from jQuery)
		// From the awesome hack by Dean Edwards
		// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
		// If we're not dealing with a regular pixel number
		// but a number that has a weird ending, we need to convert it to pixels
		if ( !/^\d+(px)?$/i.test( strValue ) && /^\d/.test( strValue ) ) {
			// Remember the original values
			var left = oElm.style.left, rsLeft = oElm.runtimeStyle.left;

			// Put in the new values to get a computed value out
			oElm.runtimeStyle.left = oElm.currentStyle.left;
			oElm.style.left = strValue || 0;
			strValue = oElm.style.pixelLeft + "px";

			// Revert the changed values
			oElm.style.left = left;
			oElm.runtimeStyle.left = rsLeft;
		}
	}

	if (typeof(pixelValue) == "undefined" || pixelValue == null)
		pixelValue = false;
	
	if (pixelValue) {
		if (strValue.indexOf("px") != -1) { // stripping away 'px'
			strValue = parseInt( strValue.slice(0, strValue.indexOf("px")) );
		}
	}

	return strValue;
}

// IE HACK: Define _importNode for IE since it doesnt support importNode
document._importNode = function (oNode, bImportChildren)
{
	var oNew;

	if (oNode.nodeType == 1)
	{
		oNew = document.createElement(oNode.nodeName);

		for (var i = 0; i < oNode.attributes.length; i++)
		{
			if (oNode.attributes[i].nodeValue != null && oNode.attributes[i].nodeValue != '')
			{
				var attrName = oNode.attributes[i].name;

				oNew.setAttribute(attrName, oNode.attributes[i].value);

				if (attrName == "class") {
					oNew.setAttribute("className", oNode.attributes[i].value);
				}

				if (attrName == "style") {
					if (oNew.style != null && oNew.style.cssText != null) {
						oNew.style.cssText = oNode.attributes[i].value;
					}
				}
			}
		}
	}
	else if (oNode.nodeType == 3)
	{
		oNew = document.createTextNode(oNode.nodeValue);
	}

	if (bImportChildren && oNode.hasChildNodes())
	{
		for (var oChild = oNode.firstChild; oChild; oChild = oChild.nextSibling)
		{
			oNewChild = document._importNode(oChild, true);
			oNew.appendChild(oNewChild);
		}
	}

	return oNew;
}
