jQuery.fn.getCDATA = function() {
/*
	if(jQuery.browser.msie)
		return this[0].childNodes[0].nodeValue;
	// Other browsers do this
	alert(this[0].childNodes[0].nodeType);
	return this[0].childNodes[1].nodeValue;
*/
	if (this[0].childNodes[0].nodeType == 4) {
		return this[0].childNodes[0].nodeValue;
	} else {
		return this[0].childNodes[1].nodeValue;
	}
};

jQuery.fn.getText = function() {
	if (this[0]) {
		if (this[0].text) {
			return this[0].text;
		}
		if (this[0].textContent) {
			return this[0].textContent;
		}
	}
};

var WIDGETCALLBACK = {
	i: 0
	, list: []
	, register: function(pUrl, pCallbackObject) {

			// add to list
				WIDGETCALLBACK.i++;
				
				WIDGETCALLBACK.list[WIDGETCALLBACK.i] = pCallbackObject;
			
			// build script tag
				var scriptObj = document.createElement("script");
				
				scriptObj.setAttribute("type", "text/javascript");
				scriptObj.setAttribute("charset", "utf-8");
				scriptObj.setAttribute("src", pUrl + "?a=WIDGETCALLBACK.fire&b=" + WIDGETCALLBACK.i + "&rnd=" + (new Date()).getTime());
				//scriptObj.setAttribute("id", this.scriptId);
				
			// attach script tag
				document.getElementsByTagName("head").item(0).appendChild(scriptObj);

		}
	, fire: function(jsonData) {
			var cb = WIDGETCALLBACK.list[jsonData.id];

			try { //Internet Explorer
				xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
				xmlDoc.async="false";
				xmlDoc.loadXML(jsonData.data);
			}
			catch(e) {
				try { //Firefox, Mozilla, Opera, etc.
					parser=new DOMParser();
					xmlDoc=parser.parseFromString(jsonData.data,"text/xml");
				}
				catch(e) {
					alert(e.message)
				}
			}
			
			cb.renderCallback(xmlDoc);
		}
};

function WIDGET(pConfig) {

	var w = this;
	
	w.locked = false;
	
	w.rootUrl = "http://www.contentwidgets.com/";
	
	w.configId = pConfig;
	
	w.data = null;
	w.target = null;
	w.stage = null;
	w.action = null;
	w.status = null;
	w.statusText = null;
	w.powered = null;

	w.height = 0;
	w.width = 0;
	
	w.slide = -1;
	w.slides = [];
	
	w.slideDefaults = {
		status: ''
		, content: 'No Content Specified for this slide'
		, canMoveNext: function() {
				return true;
			}
		, shouldMoveNext: function() {
				return true;
			}
		, beforeChange: function() {
				return;
			}
		, beforeShow: function() {
				return;
			}
	};
	
	w.SLIDE = function(options) {
		this.options = jQuery.extend({}, w.slideDefaults, options);
	}
	
	// score keeping
	w.totalQuestions = 0;
	w.correctQuestions = 0;
	
	w.totalPoints = 0;
	w.correctPoints = 0;

}

WIDGET.prototype.getCurrentSlide = function() {
	
	var w = this;
	
	if (w.slide == -1)
		return null;
	
	return w.slides[w.slide];
	
}

WIDGET.prototype.moveNextSlide = function() {

	var w = this;
	
	w.slide++;
	
	if (w.slide < w.slides.length)
		return w.slides[w.slide];


	return null;
	
}

WIDGET.prototype.render = function(pTarget) {

	var w = this;

	$('head').append('<link rel="stylesheet" href="http://www.contentwidgets.com/style/widget.css" type="text/css" />');

	// relative container
	w.target = jQuery('#'+pTarget);
	
	w.target.addClass('widget');
	
	w.height = w.target.height();
	w.width = w.target.width();

	// relative container
		w.target.append("<div style='position:static;height:" + w.height + "px;width:" + w.width + "px;'></div>");
		w.target = jQuery('#'+pTarget + ' > DIV:last');

	// loading slide
		w.target.append("<div id='loading' style='position:absolute;width:" + w.width + "px;text-align:center;'><img src='" + w.rootUrl + "images/widgets/ajax-loader.gif'/></div>");
		var load = jQuery('#loading', w.target);
		load.css('top', (w.height - load.height()) / 2 + 'px');
		
	// go get data
	/*
		jQuery.ajax({
			url:		w.rootUrl + "test/" + w.configId + "/"
			, data:		{}
			, dataType:	"xml"
			, success:	function(pData, pStatus) {
					w.renderCallback(pData);
				}
			, error: function(pRequest, pStatus, pError) {
					alert("Failed to load test: " + pStatus);
				}
		});
	*/
	WIDGETCALLBACK.register(w.rootUrl + "test/" + w.configId + "/", w);

};

WIDGET.prototype.renderCallback = function(pData) {
	
	var w = this;
	
	w.data = pData;
	
	// remove loading image
		jQuery('#loading').replaceWith('');

	// background
		var bg = jQuery('exam > layout > background', w.data).getText() || w.rootUrl + "images/widgets/bb/widget-back.gif";
		w.target.parent().css('background-image', 'url(\'' + bg + '\')');
		w.target.parent().css('overflow', 'hidden');

	// stage inside container
		var fg = jQuery('exam > layout > foreground', w.data).getText() || w.rootUrl + "images/widgets/bb/widget.gif";
		w.target.append("<div style='position:absolute;top:0px;left:0px;height:" + w.height + "px;width:" + w.width + "px;'></div>");
		w.stage = jQuery('DIV:last', w.target);
		w.stage.css('background-image', 'url(\'' + fg + '\')');

	// clipping shape inside container
		w.target.append("<div style='position:absolute;top:0px;left:0px;height:" + w.height + "px;width:" + w.width + "px;'></div>");
		jQuery('DIV:last', w.target).css('background-image', 'url(\'' + w.rootUrl + 'images/widgets/bb/widget.gif\')');
		
	// action layer inside container
		var btn = jQuery('exam > layout > action', w.data).getText() || w.rootUrl + "images/widgets/bb/widget-next.gif";
		w.target.append("<div style='position:absolute;bottom:-50px;left:300px;'><a href='#next' onclick='return false;'><img src='" + btn + "' border='0'/></a></div>");
		w.action = jQuery('DIV:last', w.target);
		//w.action.css('cursor', 'hand');
		w.action.click(function() {
			w.showNext();
		});
		
	// status layer inside container
		var btn = jQuery('exam > layout > status', w.data).getText() || w.rootUrl + "images/widgets/bb/widget-status.gif";
		w.target.append("<div style='position:absolute;bottom:-50px;left:61px;'><img src='" + btn + "' border='0'/></div>");
		w.status = jQuery('DIV:last', w.target);
		w.target.append("<div style='LEFT:61px;BOTTOM:-2px;POSITION:absolute;font-family:arial;font-size:14px;font-weight:bold;color:#fff;margin-bottom:7px;text-align:center;width:43px;'></div>");
		w.statusText = jQuery('DIV:last', w.target);
		
	// powered by layer inside container
		var btn = jQuery('exam > layout > plug', w.data).getText() || w.rootUrl + "images/widgets/bb/widget-plug.gif";
		w.target.append("<div style='position:absolute;bottom:-50px;left:61px;display:none;'><a href='http://www.contentwidgets.com/' target='_blank'><img src='" + btn + "' border='0'/></a></div>");
		w.powered = jQuery('DIV:last', w.target);

	
	// add slides
		w.slides[w.slides.length] =  new w.SLIDE({
			content: jQuery.trim(jQuery('exam > intro', w.data).getCDATA())
		});
		
		var questions = jQuery('exam > questions > question', w.data);
		
		questions.each(function() {
		
			var question = this;

			var props = {
				value: parseInt(jQuery('value', question)[0].text || jQuery('value', question)[0].textContent)
				, text: jQuery('text', question).getCDATA()
				, right: jQuery('right', question).getCDATA()
				, wrong: jQuery('wrong', question).getCDATA()
			};
			
			w.totalQuestions++;
			w.totalPoints += props.value
			
			var frm = "<form id='question' onsubmit='return false;'><div style='height:" + w.height + "px;width:" + w.width + "px;text-align:left;font-size:15px;'><div style='margin:20px;'>";

			frm += "<strong>" + props.text + "</strong><br>";
			
			var answers = jQuery('answers > answer', question);
			
			var j = answers.length;
			
			if (j > 1) {
				frm += "<em><span style='margin:0px;font-size:11px;font-weight:normal;'>(select all that apply)</span></em><br>";
			}
			
			frm += "<table cellspacing='0' cellpadding='5' border='0' style='font-size:12px;'>";
			
			var i = 0;
			
			var choices = jQuery('choices > choice', question);
			
			var alpha = ['a', 'b', 'c', 'd'];
			
			choices.each(function(p) {
				var choiceText = (this.text || this.textContent);
				if (choiceText) {
					var lbl = '<b>' + alpha[p] + '.</b> ' + choiceText;
					frm += "<tr><td valign='top'><input type='" + (j > 1 ? "checkbox" : "radio") + "' name='opt' id='opt" + (++i) + "'/></td><td><label for='opt" + (i) + "'>" + lbl + "</label></td></tr>";
				}
			});
			
			frm += "</table>";
			frm += "</div></div></form>";
	
			var thisSlideIndex = w.totalQuestions;
			
			w.slides[w.slides.length] =  new w.SLIDE({
				status: thisSlideIndex + "/" + questions.length
				, content: frm
				, canMoveNext: function() {
						var sel = false;
						jQuery('#question input').each(function() {
							if(this.checked) {
								sel = true;
							}
						});
						return sel;
					}
				, beforeChange: function() {
						// prepare the array of options, indicating that they aren't supposed to be checked
						var choice = [];
						choices.each(function(k) {
							var choiceText = (this.text || this.textContent);
							if (choiceText) {
								choice[k] = false;
							}
						});
						
						// iterate the answer key, turning on the choices that are supposed to be checked
						answers.each(function() {
							var k = parseInt(this.text || this.textContent) - 1;
							choice[k] = true;
						});
						
						// the state of these flags must match exactly for the question to be right
						var correct = true;
						
						for (k = 0; k < choice.length; k++) {
							var sel = jQuery('#question input:eq(' + k + ')')[0].checked;
							if (choice[k] != sel) {
								correct = false;
							}
						}
						
						responseContent = "<div style='height:" + w.height + "px;width:" + w.width + "px;text-align:left;font-size:15px;'><div style='margin:20px;'>" + (correct ? props.right : props.wrong) + "</div></div>";
						
						w.slides.splice(w.slide+1, 0, new w.SLIDE({
								status: thisSlideIndex + "/" + questions.length
								, content: responseContent
							})
						);
						
						if (correct) {
							w.correctQuestions++;
							w.correctPoints += props.value;
						}

						return;
					}
			});
	
		});
	
		w.slides[w.slides.length] =  new w.SLIDE({
			content: 'test results'
			, beforeShow: function() {
					// find the response block based on accumulated points
					var s = this;
					jQuery('exam > scores > score', w.data).each(function() {
						var score = jQuery(this);
						var min = parseInt(score.attr('min'));
						var max = parseInt(score.attr('max'));
						if (min <= w.correctPoints && w.correctPoints <= max) {
							s.content = score.getCDATA();
						}
					});
					s.content = s.content.replace(/%q/, w.correctQuestions);
					s.content = s.content.replace(/%Q/, w.totalQuestions);
					s.content = s.content.replace(/%p/, w.correctPoints);
					s.content = s.content.replace(/%P/, w.totalPoints);
					s.content = "<div style='height:" + w.height + "px;width:" + w.width + "px;text-align:left;font-size:15px;'><div style='margin:20px;'>" + s.content + "</div></div>";
				}
		});
	
		w.slides[w.slides.length] =  new w.SLIDE({
			content: jQuery.trim(jQuery('exam > outro', w.data).getCDATA())
			, canMoveNext: function() {
					return false;
				}
			, shouldMoveNext: function() {
					return false;
				}
		});

	
	w.showNext();
		
};

WIDGET.prototype.showNext = function() {

	var w = this;
	
	if (w.locked) {
		return;
	}
	
	w.locked = true;
	
	var s = w.getCurrentSlide();

	var slideIn = function() {
	
		s = w.moveNextSlide();
		
		s.options.beforeShow();
		
		w.stage.html("<div id='panel' style='position:absolute;top:0px;left:0px;width:" + w.width + "px;text-align:center;'><div style='position:relative;'>" + s.options.content + "</div></div>");
		
		var p = jQuery('#panel', w.stage);
		
		p.css('top', (w.height - Math.min(w.height, p.height())) / 2 + 'px');
		p.css('left', w.width);
		//p.css('opacity', 0.0);
		
		p.animate({
				left: '0px'
				//, opacity: 1.0
			}
			, 500
			, 'easeInExpo' // easeInCirc
			, function() {
				if (s.options.status != '') {
					w.powered.animate({bottom: '-50px'}, function() {
						w.status.animate({bottom: '0px'}, function() {
							w.statusText.html(s.options.status);
						});
					});
				} else {
					w.status.animate({bottom: '-50px'}, function() {
						w.powered.animate({bottom: '5px'});
					});
					w.statusText.html("");
				}
				if (s.options.shouldMoveNext()) {
					w.action.animate({bottom: '0px'});
				} else {
					w.action.animate({bottom: '-50px'});
				}
				// promote to the target level after the transition so that it isn't covered by the shape clipping layer
				p.appendTo(w.target);
				w.action.appendTo(w.target);
			}
		);

		w.locked = false;
		
	};
	
	// check to see if they can move
	if (s != null) {
		if(!s.options.canMoveNext()) {
			w.locked = false;
			return;
		}
		
		s.options.beforeChange();
		
		// get rid of current slide
		var p = jQuery('#panel');

		// demote to the stage level for the transition so that it is clipped
		p.appendTo(w.stage);

		p.animate({
				left: (-1 * w.width) + 'px'
				//, opacity: 0.0
			}
			, 500
			, 'easeInExpo' // easeOutCirc
			, function() {
					p.replaceWith("");
					slideIn();
				}
		);

	} else {
		slideIn();
	}
	
};