// +----------------------------------------------------+
// | ALPHASPARK - COMMON                                |
// | Holds functions common to both CONTROL and CONTENT |
// +----------------------------------------------------+
//
// v 0019

// =============================================================================================================================================
//		 # COMMON
// =============================================================================================================================================


// Sets the text in the title bar of the current window
// Parameters: str:string;
// Returns:    void;
function setWindowTitle(str) {
	$(document).attr("title", str);
}


// Returns the text in the title bar of the current window
// Parameters: void;
// Returns:    str:string;
function getWindowTitle() {
	return $(document).attr("title");
}


// Finds whether not the Adobe AIR framework is available
// Parameters: void;
// Returns:    boolean;
function usingAIR() {
	if (window.runtime && window.runtime != null) return true;
	else if (top != self && parent.runtime && parent.runtime != null) return true;
	else return false;
}


// Returns the index of the last instance of a character within a string
// Parameters: str:string, find:character;
// Returns:    integer;
function lastIndexOf(str, find) {
	var currentIndex = -1;

	for (var i = 0; i < str.length; i++) {
		if (str.charAt(i) == find) currentIndex = i;
	}
	
	return currentIndex;
}


// Converts a string to an integer, returning zero if NaN
// Parameters: str:string;
// Returns:    integer;
function parse(str) {
	var n = 0;
	
	str = trim("" + str);
	
	if (str == "") n = 0;	
	else n = parseInt(str);
	
	if (isNaN(n)) n = 0;
	
	return n;
}


// Returns the first character from a string
// Parameters: str:string;
// Returns:    character;
function firstChar(str) {
	return str.charAt(0);
}


// Returns the last character from a string
// Parameters: str:string;
// Returns:    character;
function lastChar(str) {
	return str.charAt(str.length - 1);
}


// Returns the URL currently being used in this window
// Parameters: void;
// Returns:    string;
function getWindowLocation() {
	var windowLocation = "" + window.location;
	windowLocation = windowLocation.substring(0, lastIndexOf(windowLocation, "/"));
	return windowLocation;
}


// Extracts a variable from the URL query string and returns its value
// Parameters: variable:string;
// Returns:    string;
function getQueryVariable(variable) {
	var query = window.location.search.substring(1); // Get the URL query string	
	var vars = query.split("&"); // Split into variable pairs - e.g. name=bob & age=12
	
	for (var i = 0; i < vars.length; i++) { // Now loop through each of the variable pairings
		var pair = vars[i].split("="); // Split the pair into variable and content, e.g. name = bob
		if (pair[0] == variable) return pair[1]; // If this is the variable we are looking for then return its content
	}
	
	return "";
}


// Locates all hyperlinks pointing to either "#" or "" or blocks their default behaviour
// Parameters: void;
// Returns:    void;
function turnOffEmptyLinks() {
	$("a").filter(function() {
		return ($(this).attr("href") == "#" || $(this).attr("href") == "");
	}).click(function(){
		return false;
	});
}


// Opens a new window with minimal surroundings (if requested)
// Parameters: url:string, id:string, width:integer, height:integer, minimal:boolean, centered:boolean, fullscreen:boolean;
// Returns:    window;
function openNewWindow(url, id, width, height, minimal, centered, fullscreen) {
	var configuration = "";
	var win;
	
	if (usingHE()) id = "_hepopup_" + id;
	
	if (minimal) configuration = "toolbar=no,  location=no,  directories=no,  status=no,  menubar=no,  scrollbars=yes, copyhistory=no, resizable=yes";
	else		 configuration = "toolbar=yes, location=yes, directories=yes, status=yes, menubar=yes, scrollbars=yes, copyhistory=no, resizable=yes";
	
	if (fullscreen) {
		width	= screen.width;
		height  = screen.height;
		top		= 0;
		left	= 0;
	}
	else if (centered) {
		var left = (screen.width  - width)  / 2;
		var top	 = (screen.height - height) / 2;
				
		if (left < 0) left = 0;		
		if (top	 < 0) top= 0;
	}
	
	if (fullscreen || centered) configuration += ", left = " + left + ", top = " + top;

	//if (usingHE()) {
	if (false) {
		window.external.ShowPopup(id, url, width, height, top, left, 0);
		win = null;
	} else {
		win = window.open(url, id, "width=" + width + ", height=" + height + ", " + configuration);
	}

	return win;
}


// Centres the current window on screen
// Parameters: void;
// Returns:    void;
function centreWindow() {
	window.moveTo((screen.width	- $(window).width()) / 2, (screen.height - $(window).height()) / 2);
}


// Converts a string to a boolean value
// Parameters: val:string;
// Returns:    boolean;
function parseBoolean(val) {
	val = val.toLowerCase();
	
	switch (val) {
		case "true":
			return true;

		case "false":
			return false;
			
		case "yes":
			return true;
			
		case "no":
			return false;
			
		case "1":
			return true;
			
		case "0":
			return false;
			
		case "":
			return false;
			
		case "t":
			return true;
			
		case "f":
			return false;
			
		case "y":
			return true;
			
		case "n":
			return false;
	}
	
	return false;
}


// Finds all instances of a substring within a string and replaces them
// Parameters: str:string, original:string, replacement:string;
// Returns:    string;
function replaceStrings(str, original, replacement) {	
	if (str.indexOf(original) > -1) return str.replace(RegExp(original, "gi"), replacement);
	else return str;
}


// Evaluates whether or not a string is void (ie nothing in it)
// Parameters: val:string;
// Returns:    boolean;
function isStringVoid(val) {
	return (val == "" || val == "undefined" || val == null);
}


// Finds the highest value within an array
// Parameters: a:array;
// Returns:    integer/float;
function findHighest(a) {
	var highest = 0;
	
	for (var x = 0; x < a.length; x++) {
		if (a[x] > highest) highest = a[x];
	}
	
	return highest;
}


// Finds the lowest value within an array
// Parameters: array;
// Returns:    integer/float;
function findLowest(a) {
	var lowest = 0;
	
	for (var x = 0; x < a.length; x++) {
		if (a[x] < lowest) lowest = a[x];
	}
	
	return lowest;
}


// Trims all excess spaces, tabs and line returns from a string
// Parameters: str: string;
// Returns:    string;
function trim(str) {
	if (str == null) return "";
	else return str.replace(/^\s+|\s+$/g, '');
}


// Automatically adds classes to table rows so we can alternate the colours
// Parameters: void;
// Returns:    void;
function zebraStripeTables() {
	if (exists(".zebra")) {
		$(".zebra tr:even").addClass("even");
		$(".zebra tr:odd" ).addClass("odd");
	}
}


// Blocks all forms from using default submit behaviour
// Parameters: void;
// Returns:    void;
function blockFormSubmission() {
	$("form").submit(function(){  return false;  });
}


// Catches the user pressing the return key on the specified object (ie a form) and calls the given function
// Parameters: obj:String, func:Function;
// Returns:    void;
function onReturn(obj, func) {
	$(obj).keydown(function(event){
		if (event.keyCode == 13) func();
	});
}


// Shuffles the items within an array into a random order
// Parameters: array;
// Returns:    array;
function shuffle(o) {
	for (var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
	return o;
};


// Determines whether a string contains any alphanumeric characters
// Parameters: string;
// Returns:    boolean;
function containsOnlyAlphaNumerics(str) {	
	for(var x = 0; x < str.length; x++) {
		var v = str.charAt(x).charCodeAt(0);
		if (!((v > 47 && v < 58) || (v > 64 && v < 91) || (v > 96 && v < 123))) return false;
	}
	
	return true;
}


// Sets focus to the first input field on the page
// Parameters: false;
// Returns:    false;
function focusFirstField() {
	try {
		$("input")[0].focus();
	} catch(e){};
}


// Turns on fancy Jquery-created tooltips (since AIR doesn't show them)
// Parameters: false;
// Returns:    false;
function activateTooltips() {
	$("a, img").tooltip({
		delay:   700, 
		showURL: false,
		fade:    100
	});
}


// Determines whether an HTML object exists on the page or not
// Parameters: id:string;
// Returns:    boolean;
function exists(id) {
	return ($(id).length > 0);
}




// =============================================================================================================================================
//		 # AJAX
// =============================================================================================================================================


// Creates a runs an AJAX request
// Parameters: url:string, callbackFunction:function;
// Returns:    void;
function ajax(url, callbackFunction) {
	var xmlHttp;

	try {
		xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
	} catch (e) {
		try {
			xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
		} catch (e) {
			try {
				// Firefox, Opera 8.0+, Safari
				xmlHttp = new XMLHttpRequest();
			} catch (e) {
				callbackFunction("ERROR: AJAX not supported");
				return false;
			}
		}
	}	

	xmlHttp.onreadystatechange = function() {
		if (xmlHttp.readyState == 4) {
			callbackFunction(xmlHttp.responseText);
		}
	};

	try {
		xmlHttp.open("GET", url, true);
		xmlHttp.send(null);
	} catch (e) {
		callbackFunction("ERROR: Cannot access URL");
	}
}


// Loads convert with AJAX and converst to XML before passing on
// Parameters: url:string, callbackFunction:function;
// Returns:    void;
function ajaxXml(url, callbackFunction) {
	ajax(url, function(response) {  callbackFunction($.textToXML(response));  });
}




// =============================================================================================================================================
//		 # COOKIES
// =============================================================================================================================================


// Writes a value to a local cookie, automatically adding extra information to the name
// Parameters: name:string, value:string;
// Returns:    void;
function writeCookie(name, value) {
	var cookieName = "bpplm_as_" + name;
	
	if (usingHE) setGlobalVariable(cookieName, value);
	else         $.cookie(cookieName, value);
}


// Reads a value to a local cookie, automatically adding extra information to the name
// Parameters: name:string;
// Returns:    string;
function readCookie(name) {
	var cookieName = "bpplm_as_" + name;
	var result     = "";

	if (usingHE) result = getGlobalVariable(cookieName);
	else         result = $.cookie(cookieName);	

	if (result == null) result = "";

	return result;
}


// Checks whether a cookie exists and has content set
// Parameters: name:string;
// Returns:    boolean;
function cookieExists(name) {
	if (readCookie(name) == "") return false;
	else                        return true;
}


// Returns a random number
// Parameters: max:integer;
// Returns:    integer;
function rand(max) {
	return Math.floor(Math.random() * max);
}


// Returns a random number from within a set range
// Parameters: min:integer, max:integer;
// Returns:    integer;
function randRange(min, max) {
	return parseInt(Math.floor((max - min) * Math.random()) + min);
}




// =============================================================================================================================================
//		 # ENCRYPTED LOCAL STORE (Air only)
// =============================================================================================================================================


// Fetches the value stored in a specified AIR Encrypted Local Store
// Parameters: storeName:string;
// Returns:    string;
function getLocalStore(storeName) {
	if (usingAIR()) {
		var val = air.EncryptedLocalStore.getItem(storeName);
		if (val == null) val = "";
		else             val = val.readUTFBytes(val.bytesAvailable);

		return val;
	} else return "";
}


// Sets the value stored in a specified AIR Encrypted Local Store
// Parameters: storeName:string, value:string;
// Returns:    void;
function setLocalStore(storeName, value) {
	if (usingAIR()) {
		var data = new air.ByteArray();
		data.writeUTFBytes(value);
		air.EncryptedLocalStore.setItem(storeName, data);
	}
}


// Clears the value stored in a specified AIR Encrypted Local Store
// Parameters: storeName:string;
// Returns:    void;
function deleteLocalStore(storeName) {
	if (usingAIR()) air.EncryptedLocalStore.removeItem(storeName);
}




// =============================================================================================================================================
//		 # HTML EXECUTABLE FUNCTIONS
// =============================================================================================================================================


// Finds whether not the HTML Executable framework is available
// Parameters: void;
// Returns:    boolean;
function usingHE() {
	var location = "" + window.location;
	return (location.indexOf("heserver") > -1);
}


// Retrieves the value of a Global Variable stored using HE
// Parameters: variable:string;
// Returns:    string;
function getGlobalVariable(variable) {
	if (usingHE()) return window.external.GetGlobalVariable(variable, "");
	else return "";
}


// Sets the value of a Global Variable stored using HE
// Parameters: variable:string, value:string;
// Returns:    void;
function setGlobalVariable(variable, value) {
	if (usingHE()) window.external.SetGlobalVariable(variable, value, true);
}




// =============================================================================================================================================
//		 # INTERACTIONS
// =============================================================================================================================================


var InteractionTypes = new Array("true_false", "multiple_choice", "fill_in", "long_fill_in", "matching", "performance", "sequencing", "likert", "numeric", "other");
var TRUE_FALSE        = 0;
var MULTIPLE_CHOICE   = 1;
var FILL_IN           = 2;
var LONG_FILL_IN      = 3;
var MATCHING          = 4;
var PERFORMANCE       = 5;
var SEQUENCING        = 6;
var LIKERT            = 7;
var NUMERIC           = 8;
var OTHER             = 9;

var InteractionResult = new Array("correct", "incorrect", "unanticipated", "neutral", "real");
var CORRECT           = 0;
var INCORRECT         = 1;
var UNANTICIPATED     = 2;
var NEUTRAL           = 3;
var REAL              = 4;


// InteractionObject class
function InteractionObject(id) {
	this._id               = id;
	this._xml_id           = "";
	this._type             = OTHER;
	this._timestamp        = "";
	this._latency          = "";
	this._score            = 0;
	this._weighting        = 0;
	this._learner_response = "";
	this._result           = NEUTRAL;
	this._description      = "";
	this._attempts         = "";
}


InteractionObject.prototype._startTime;

InteractionObject.prototype._id;
InteractionObject.prototype._xml_id;
InteractionObject.prototype._type;
InteractionObject.prototype._timestamp;
InteractionObject.prototype._latency;
InteractionObject.prototype._score;
InteractionObject.prototype._weighting;
InteractionObject.prototype._learner_response;
InteractionObject.prototype._correct_response;
InteractionObject.prototype._result;
InteractionObject.prototype._description;
InteractionObject.prototype._attempts;


InteractionObject.prototype.start = function() {
	this.setTimestamp();
};


InteractionObject.prototype.finish = function() {
	this.setLatency(convertTime(this._startTime, getTimeStamp()));
	this._attempts++;
};


InteractionObject.prototype.setCorrect = function(score) {
	this._score	 = score;
	this._result = CORRECT;
	this.finish();
};


InteractionObject.prototype.setIncorrect = function() {
	this._score	 = 0;
	this._result = INCORRECT;
	this.finish();
};


InteractionObject.prototype.setDescription = function(desc) {
	if (desc.length > 250) desc = desc.substring(250);
	this._description = desc;
};


InteractionObject.prototype.setTimestamp = function() {
	var currrentDateTime = new Date();
	var currrentHour     = currrentDateTime.getHours();	
	
	this._timestamp =
		currrentDateTime.getFullYear()      + "-" +
		(currrentDateTime.getDate()    + 1) + "-" +
		(currrentDateTime.getMonth()   + 1) + "T" + 
		(currrentDateTime.getHours()   + 1) + ":" + 
		(currrentDateTime.getMinutes() + 1) + ":" + 
		(currrentDateTime.getSeconds() + 1) + ".00Z";
};


InteractionObject.prototype.setLatency = function(time) {
	var timeArray  = time.split(":");	
	this._latency = "PT" + parse(timeArray[1]) + "M" + parse(timeArray[2]) + "S";
};




// =============================================================================================================================================
//		 # FILE (Air only)
// =============================================================================================================================================


function saveFile(filename, content) {
	var docsDir = air.File.desktopDirectory.resolvePath(filename);
	docsDir.browseForSave("Save as");
	docsDir.addEventListener(air.Event.SELECT, function(event){
		var file = event.target;
		var fileStream = new air.FileStream();
		fileStream.open(file, air.FileMode.WRITE);
		fileStream.writeMultiByte(content, air.File.systemCharset);
		fileStream.close();
		alert("File saved!");
	});
}




// =============================================================================================================================================
//		 # GROUP OBJECT
// =============================================================================================================================================


// Group class
function Group(title) {
	this._title = title;
	this._modules = new Array();
}

Group.prototype._title;
Group.prototype._modules;




// =============================================================================================================================================
//		 # LOADING ANIMATION
// =============================================================================================================================================


function showLoadingAnimation() {
	if (exists(".loadingAnimation")) {
		$(".loadingAnimation").show();
	} else {
		var animationString = "";
		animationString += "<div class='loadingAnimation' style='text-align: center; position: fixed; top: 250px; left: 0px; width: 100%;'>";
		animationString += "<image src='./images/furniture/loading/loadinganimation.gif'>";
		animationString += "</div>";
		
		$("body").append(animationString);
	}
}


function hideLoadingAnimation() {
	$(".loadingAnimation").hide();
}




// =============================================================================================================================================
//		 # MODULE OBJECT
// =============================================================================================================================================


// Module class
function Module(id, url, title, desc, assessment) {
	this._id         = id;
	this._url        = url;
	this._title      = title;
	this._desc       = desc;
	this._assessment = assessment;
}

Module.prototype._id;
Module.prototype._url;
Module.prototype._title;
Module.prototype._desc;
Module.prototype._assessment;




// =============================================================================================================================================
//		 # PAGE OBJECT
// =============================================================================================================================================


// Page class
function Page(url, title, type, desc) {
	this._url       = url;
	this._title     = title;
	
	if (desc == undefined) desc = "&nbsp;";
	this._desc      = desc;
	
	if (type != "") this._type = type;
	else            this._type = url.substring(url.lastIndexOf(".") + 1);
	
	this._newWindow	= false;
}

Page.prototype._url;
Page.prototype._title;
Page.prototype._desc;
Page.prototype._type;
Page.prototype._newWindow;
Page.prototype._width;
Page.prototype._height;
Page.prototype._fullscreen;




// =============================================================================================================================================
//		 # PRINT
// =============================================================================================================================================


// Prints the current page - either by calling the browsers print function or by opening the default browser
// Parameters: void;
// Returns:    void;
function printPage() {
	if (usingAIR()) { // Printing using AIR
		//window.runtime.flash.net.navigateToURL(new window.runtime.flash.net.URLRequest(pageUrlsArray[currentPage]));
		multiplePagePrint();
		//airPrint();
	} else { // Printing via the browser
		frames["contentFrame"].focus();
		frames["contentFrame"].print();
	}
}


// Uses the AIR framework to print the current page - currently unused
// Doesn't work very well, takes a poor-quality screenshot of the window rather than the content
// Parameters: void;
// Returns:    void;
function airPrint() {
	var printJob = new window.runtime.flash.printing.PrintJob;
	var mySprite = window.htmlLoader;
	if (printJob.start()) {
		var printOptions = new window.runtime.flash.printing.PrintJobOptions;
		printOptions.printAsBitmap = false;
		
		try {
			//mySprite.rotation = 90;
			//mySprite.width = printJob.pageHeight;
			//mySprite.height = printJob.pageWidth;
						
			printJob.addPage(mySprite, null, printOptions);
			printJob.send();
		} catch (e) {
			logError("Error: An error occured during printing.");
		}
	}
}

 // You might find useful to pass some value, number of rows for instance
function multiplePagePrint() {
	// Create the Print Job
	var pjob = new window.runtime.flash.printing.PrintJob;
	var psprite = window.htmlLoader; // Define the Sprite
	if ( pjob.start() ) // Start the Print Job
		{
			// Define the Print Job Options
			var poptions = new window.runtime.flash.printing.PrintJobOptions;
			poptions.printAsBitmap = true; // Must print it as Bitmap

			//after you've start the print job
			// you can get any print specifics (page size etc.)
			var pageHeight = pjob.pageHeight;
			var pageWidth = pjob.pageWidth;
			var paperHeight = pjob.paperHeight;
			var paperWidth = pjob.paperWidth;

			// Calculate the number of pages
			//var pagesToPrint = /** Your logic here **/;

			// Add your pages:
			for(var i = 0; i < pagesToPrint; i++) {
	//** Your logic here for calculating RECT_HEIGHT **/
				var rect = new air.Rectangle(0, RECT_HEIGHT, pageWidth, pageHeight);

				try{
					pjob.addPage(psprite, rect, poptions);
				} catch(e) {
					air.trace(e);
				}
			}

			try{
				pjob.send();
				return true;
			}
			catch (err){
				air.trace(err);
				return false;
			}
		}
		else {
			air.trace("PrintJob couldn't start");
			return false;
		}
}




// =============================================================================================================================================
//		 # STYLESHEETS
// =============================================================================================================================================


// Locates any instances of the specified stylesheet and toggles whether they are active or not
// Parameters: url:string;
// Returns:    void;
function toggleStylesheet(url) {
	$("link[@rel*=style]").each(function() {
		if (this.getAttribute('href') == url) {
			this.disabled = !this.disabled;
			return this.disabled;
		}
	});
}


// Locates any instances of the specified stylesheet and activates it
// Parameters: url:string;
// Returns:    void;
function activateStylesheet(url) {
	$("link[@rel*=style]").each(function() {
		if ($(this).attr('href').indexOf(url) > -1) {
			if (lastChar($(this).attr('href')) == "x") {
				$(this).attr('href', $(this).attr('href').substring(0, $(this).attr('href').length-1));
			}
		}
	});
}


// Locates any instances of the specified stylesheet and deactivates it
// Parameters: url:string;
// Returns:    void;
function deactivateStylesheet(url) {
	$("link[@rel*=style]").each(function() {
		if ($(this).attr('href').indexOf(url) > -1) {
			if (lastChar($(this).attr('href')) != "x") $(this).attr('href', $(this).attr('href') + "x");
		}
	});
}




// =============================================================================================================================================
//		 # TIME
// =============================================================================================================================================


// Returns the current time in the format HH:MM:SS
// Parameters: void;
// Returns:    string;
function getCurrentTime() {
	var currrentDateTime = new Date();
	var currrentHour     = currrentDateTime.getHours();
	var currrentMin      = currrentDateTime.getMinutes();
	var currrentSec      = currrentDateTime.getSeconds();
	
	return currrentHour + ":" + currrentMin + ":" + currrentSec;
}


// Returns the current date
// Parameters: void;
// Returns:    string;
function getCurrentDate() {
	var dayFullNames  = new Array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday");
	var dayShortNames = new Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");

	var currentDateTime = new Date();
	var currentYear     = currentDateTime.getFullYear();
	var currentMonth    = currentDateTime.getMonth() + 1;
	var currentDate     = currentDateTime.getDate();
	var currentDay      = dayFullNames[currentDateTime.getDay()];
	
	return currentDay + " " + currentDate + "/" + currentMonth + "/" + currentYear;
}


// Returns a unique representing the number of milliseconds since 1970
// Parameters: void;
// Returns:    integer;
function getTimeStamp() {
	var theDate = new Date();
	return theDate.getTime();
}


// Concatenates three integers: hours, minutes and seconds into a string of the format HH:MM:SS
// Parameters: hours:integer, minutes:integer, seconds:integer;
// Returns:    integer;
function makeTimeString(hours, minutes, seconds) {
	hours   = parse(Math.round(hours));
	minutes = parse(Math.round(minutes));
	seconds = parse(Math.round(seconds));
	
	var h = "" + hours;
	var m = "" + minutes;
	var s = "" + seconds;
	
	if (hours   < 10) h = "0" + hours;
	if (minutes < 10) m = "0" + minutes;
	if (seconds < 10) s = "0" + seconds;
	
	var timeString = h + ":" + m + ":" + s;
	
	return timeString;
}


// Calculates the difference between two timestamps and return as string in the format HH:MM:SS
// Parameters: startTimeStamp:integer, endTimeStamp:integer;
// Returns:    string;
function convertTime(startTimeStamp, endTimeStamp) {
	var seconds = Math.round(Math.ceil(endTimeStamp - startTimeStamp) / 1000);
	var minutes = 0;
	var hours   = 0;
	
	while (seconds > 59) {
		seconds = seconds - 60;
		minutes = minutes + 1;
		if (minutes > 59) {
			minutes = 0;
			hours = hours + 1;
		}
	}

	return makeTimeString(hours, minutes, seconds);
}


// Adds together two times, both passed as strings in the format HH:MM:SS
// Parameters: firstTime:string, secondTime:string;
// Returns:    string;
function addTimes(firstTime, secondTime) {
	if (!firstTime)	 firstTime  = "00:00:00";
	if (!secondTime) secondTime = "00:00:00";
	
	firstTime  = firstTime.split(":");
	secondTime = secondTime.split(":");
	
	var first_hours    = parse(firstTime[0]);
	var first_minutes  = parse(firstTime[1]);
	var first_seconds  = parse(firstTime[2]);
	
	var second_hours   = parse(secondTime[0]);
	var second_minutes = parse(secondTime[1]);
	var second_seconds = parse(secondTime[2]);
	
	var new_hours      = first_hours   + second_hours;
	var new_minutes    = first_minutes + second_minutes;
	var new_seconds    = first_seconds + second_seconds;
	
	if (new_seconds > 60) {
		new_minutes ++;
		new_seconds -= 60;
	}
	
	if (new_minutes > 60) {
		new_hours ++;
		new_minutes -= 60;
	}

	return makeTimeString(new_hours, new_minutes, new_seconds);
}


// Converts a string in the format HH:MM:SS to the number of minutes
// Parameters: time:string;
// Returns:    float;
function convertTimeToMinutes(time) {
	time = time.split(":");
	
	var hours   = parse(time[0]);
	var minutes	= parse(time[1]);
	var seconds	= parse(time[2]);
	
	var total   = minutes;
	
	total += hours * 60;
	total += seconds / 60;
	
	return total;
}


// Converts a string in the format HH:MM:SS to the number of seconds
// Parameters: time:string;
// Returns:    float;
function convertTimeToSeconds(time) {
	time = time.split(":");
	
	var hours   = parse(time[0]);
	var minutes	= parse(time[1]);
	var seconds	= parse(time[2]);
	
	var total = seconds;
	
	total += hours * 60 * 60;
	total += minutes * 60;
	
	return total;
}


// Converts a number of seconds into a string in the format HH:MM:SS
// Parameters: seconds:integer;
// Returns:    string;
function convertSecondsToTime(seconds) {
	var hours   = 0;
	var minutes = 0;
	
	seconds = Math.round(seconds);

	if (seconds > 1892160000) seconds = 0; // To avoid long loops the cut off is a year
	
	while (seconds > 3600) {
		seconds -= 3600;
		hours++;
	}
	
	while (seconds > 60) {
		seconds -= 60;
		minutes++;
	}
	
	return makeTimeString(hours, minutes, seconds);
}




// =============================================================================================================================================
//		 # TIMER
// =============================================================================================================================================


var timeCounter = 0;


// Starts the counter (used for debugging and profiling)
// Parameters: void;
// Returns:    void;
function timerStart() {
	var theDate = new Date();
	timeCounter = theDate.getTime();
}


// Stops the timer and displays the result
// Parameters: void;
// Returns:    time;
function timerEnd() {
	var theDate = new Date();
	alert("TIMER: " + (theDate.getTime() - timeCounter));
}




// =============================================================================================================================================
//		 # TRACKING
// =============================================================================================================================================


// Status values
var UNATTEMPTED = 0;
var INCOMPLETE  = 1;
var COMPLETE    = 2;


// TrackingObject class
function TrackingObject(name) {
	this._name         = name;
	this._sessionTime  = "00:00:00";
	this._overallTime  = "00:00:00";
	this._status       = 0;
	this._actualScore  = 0;
	this._maxScore     = 0;
	this._interactions = new Array();
}


TrackingObject.prototype._startTime;

TrackingObject.prototype._name;
TrackingObject.prototype._sessionTime;
TrackingObject.prototype._overallTime;
TrackingObject.prototype._status;
TrackingObject.prototype._actualScore;
TrackingObject.prototype._maxScore;
TrackingObject.prototype._interactions = new Array();


TrackingObject.prototype.startTiming = function() {
		this._startTime = getTimeStamp();
};


TrackingObject.prototype.stopTiming = function() {
		this.setSessionTime(convertTime(this._startTime, getTimeStamp()));
};


TrackingObject.prototype.setSessionTime = function(sessionTime) {
		this._sessionTime = addTimes(this._sessionTime, sessionTime);
};


TrackingObject.prototype.getCurrentoverallTime = function() {
		return addTimes(this._sessionTime, this._overallTime);
};


TrackingObject.prototype.setIncomplete = function() {
		this._status = INCOMPLETE;
};


TrackingObject.prototype.setComplete = function() {
		this._status = COMPLETE;
};


TrackingObject.prototype.isComplete = function() {
		return (this._status == COMPLETE);
};


TrackingObject.prototype.isIncomplete = function() {
		return (this._status == INCOMPLETE);
};


TrackingObject.prototype.isUnattempted = function() {
		return (this._status == UNATTEMPTED);
};


TrackingObject.prototype.clear = function() {
	this.startTiming();
	this._name         = "";
	this._sessionTime  = "00:00:00";
	this._overallTime  = "00:00:00";
	this._status       = 0;
	this._actualScore  = 0;
	this._maxScore     = 0;
	this._interactions = new Array();
};


function convertStatusShort(n) {
	var values = new Array("U", "I", "C");
	
	return values[n];
}


function convertStatusLong(n) {
	var values = new Array("Unattempted", "Incomplete", "Completed");
	
	return values[n];
}


TrackingObject.prototype.addInteraction = function(id) {
	this._interactions.push(new InteractionObject(id));
	
	return this.findInteraction(id);
};


TrackingObject.prototype.findInteraction = function(id) {
	for (var x = 0; x < this._interactions.length; x++) {
		if (this._interactions[x]._id == id) return this._interactions[x];
	}
	
	return this.addInteraction(id);
};


TrackingObject.prototype.getMaxScore = function() {
	this._maxScore = 0;
	var maxScore   = 0;

	for (var x = 0; x < this._interactions.length; x++) maxScore += this._interactions[x]._weighting;
	
	if (maxScore > this._maxScore) this._maxScore = maxScore;
};


TrackingObject.prototype.getActualScore = function() {
	this._actualScore = 0;
	
	for (var x = 0; x < this._interactions.length; x++) this._actualScore += this._interactions[x]._score;
};




// =============================================================================================================================================
//		 # USER DATA
// =============================================================================================================================================


// UserDataField class
function UserDataField(name, title, min, max, required, unique, confirm, login, password) {
	this._name     = "bpp_" + name;
	this._title    = title;
	this._min      = parse(min);
	this._max      = parse(max);
	this._required = parseBoolean(required);
	this._unique   = parseBoolean(unique);
	this._confirm  = parseBoolean(confirm);
	this._login    = parseBoolean(login);
	this._password = parseBoolean(password);
	this._filter   = "";
}

UserDataField.prototype._name;
UserDataField.prototype._title;
UserDataField.prototype._min;
UserDataField.prototype._max;
UserDataField.prototype._required;
UserDataField.prototype._unique;
UserDataField.prototype._confirm;
UserDataField.prototype._login;
UserDataField.prototype._password;
UserDataField.prototype._filter;




// =============================================================================================================================================
//		 # VEIL
// =============================================================================================================================================


// Adds an overlay to the screen while things happen in the foreground
// Parameters: void;
// Returns:    void;
function showVeil() {
	if (!exists(".veil")) {
		$("body").append("<div class='veil'></div>");
		$(".veil").css({
			"position":         "fixed",
			"display":          "block",
			"top":              "0px",
			"left":             "0px",
			"width":            "100%",
			"height":           "50000px",
			"z-index":          "100",
			"overflow":         "hidden",
			"background-image":	"url(./images/furniture/loading/veil.png)"
		});
	}
}


// Removes the overlay from the screen again
// Parameters: void;
// Returns:    void;
function hideVeil() {
	if (exists(".veil")) {
		$(".veil").remove();		
	}
}




// =============================================================================================================================================
//		 # WINDOW OBJECT
// =============================================================================================================================================


// WindowObject class
function WindowObject(id, url, width, height) {
	this._id     = id;
	this._url    = url;
	this._active = (url != "");
	this._width	 = parse(width);
	this._height = parse(height);	
	this._newWin = (this._width != 0 && this._height != 0);
	this._win    = null;
}


WindowObject.prototype._id;
WindowObject.prototype._url;
WindowObject.prototype._active;
WindowObject.prototype._width;
WindowObject.prototype._height;
WindowObject.prototype._newWin;
WindowObject.prototype._win;


WindowObject.prototype.openWindow = function() {
	if (this._newWin) this._win = openNewWindow(this._url, this._id, this._width, this._height, true, true, false);
	else              loadPage(this._url, false);
};


WindowObject.prototype.closeWindow = function() {
	if (this._win != null) this._win.close();
};



// =============================================================================================================================================
//		 # XML
// =============================================================================================================================================


// Loads an XML file using AJAX at first and if that fails, GET
// Parameters: xmlFileName:string, callBackFunction:function;
// Returns:    void;
function loadXML(xmlFileName, callBackFunction) {
	loadXMLAjax(xmlFileName, callBackFunction);
}


// Uses JQuery's AJAX GET command to load an XML file
// Parameters: xmlFileName:string, callBackFunction:function;
// Returns:    void;
function loadXMLAjax(xmlFileName, callBackFunction) {
	var success = true;

	$.ajax({
		type:     "GET",
		url:      xmlFileName,
		dataType: "xml",
		error: function(XMLHttpRequest, textStatus, errorThrown) {	// Try again using a different method
			success = false;
			if (window.ActiveXObject) loadXMLActiveX(xmlFileName, callBackFunction); // IE method
			else                      loadXMLGet(xmlFileName, callBackFunction);     // Non-IE method
		},
		complete: function(xml) {
			if (success) callBackFunction(xml.responseXML);
		}
	});
}


// Uses JQuery's GET command to load an XML file
// Parameters: xmlFileName:string, callBackFunction:function;
// Returns:    void;
function loadXMLGet(xmlFileName, callBackFunction) {
	$.get(xmlFileName, function(xml) {
		callBackFunction($.textToXML(xml));
	});
}


// Uses the Microsoft.XMLDOM ActiveX object load an XML file (IE only!)
// Parameters: xmlFileName:string, callBackFunction:function;
// Returns:    void;
function loadXMLActiveX(xmlFileName, callBackFunction) {
	var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
	 xmlDoc.async = "false";
	 xmlDoc.onreadystatechange = function() {
		if (xmlDoc.readyState != 4) { return false; }
	 };
	 xmlDoc.load(xmlFileName);
	 callBackFunction($.textToXML(xmlDoc.documentElement.xml));
}




// =============================================================================================================================================
//		 # AIR Aliases
// =============================================================================================================================================

//* AIRAliases.js - Revision: 0.16 */

// Copyright (c) 2007. Adobe Systems Incorporated.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//	 * Redistributions of source code must retain the above copyright notice,
//		 this list of conditions and the following disclaimer.
//	 * Redistributions in binary form must reproduce the above copyright notice,
//		 this list of conditions and the following disclaimer in the documentation
//		 and/or other materials provided with the distribution.
//	 * Neither the name of Adobe Systems Incorporated nor the names of its
//		 contributors may be used to endorse or promote products derived from this
//		 software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.


var air;
if (window.runtime) {
		if (!air) air = {};
		// functions
		air.trace = window.runtime.trace;
		air.navigateToURL = window.runtime.flash.net.navigateToURL;
		air.sendToURL = window.runtime.flash.net.sendToURL;


		// file
		air.File = window.runtime.flash.filesystem.File;
		air.FileStream = window.runtime.flash.filesystem.FileStream;
		air.FileMode = window.runtime.flash.filesystem.FileMode;

		// events
		air.AsyncErrorEvent = window.runtime.flash.events.AsyncErrorEvent;
		air.DataEvent = window.runtime.flash.events.DataEvent;
		air.DRMAuthenticateEvent = window.runtime.flash.events.DRMAuthenticateEvent;
		air.DRMStatusEvent = window.runtime.flash.events.DRMStatusEvent;
		air.Event = window.runtime.flash.events.Event;
		air.EventDispatcher = window.runtime.flash.events.EventDispatcher;
		air.FileListEvent = window.runtime.flash.events.FileListEvent;
		air.HTTPStatusEvent = window.runtime.flash.events.HTTPStatusEvent;
		air.IOErrorEvent = window.runtime.flash.events.IOErrorEvent;
		air.InvokeEvent = window.runtime.flash.events.InvokeEvent;
		air.NetStatusEvent = window.runtime.flash.events.NetStatusEvent;
		air.OutputProgressEvent = window.runtime.flash.events.OutputProgressEvent;
		air.ProgressEvent = window.runtime.flash.events.ProgressEvent;
		air.SecurityErrorEvent = window.runtime.flash.events.SecurityErrorEvent;
		air.StatusEvent = window.runtime.flash.events.StatusEvent;
		air.TimerEvent = window.runtime.flash.events.TimerEvent;
		air.NativeDragEvent = window.runtime.flash.events.NativeDragEvent;
		air.ActivityEvent = window.runtime.flash.events.ActivityEvent;
		air.KeyboardEvent = window.runtime.flash.events.KeyboardEvent;
		air.MouseEvent = window.runtime.flash.events.MouseEvent;
		
		// native window
		air.NativeWindow = window.runtime.flash.display.NativeWindow;
		air.NativeWindowDisplayState = window.runtime.flash.display.NativeWindowDisplayState;
		air.NativeWindowInitOptions = window.runtime.flash.display.NativeWindowInitOptions;
		air.NativeWindowSystemChrome = window.runtime.flash.display.NativeWindowSystemChrome;
		air.NativeWindowResize = window.runtime.flash.display.NativeWindowResize;
		air.NativeWindowType = window.runtime.flash.display.NativeWindowType;

		air.NativeWindowBoundsEvent = window.runtime.flash.events.NativeWindowBoundsEvent;
		air.NativeWindowDisplayStateEvent = window.runtime.flash.events.NativeWindowDisplayStateEvent;

		// geom
		air.Point = window.runtime.flash.geom.Point;
		air.Rectangle = window.runtime.flash.geom.Rectangle;
		air.Matrix = window.runtime.flash.geom.Matrix;

		// net
		air.FileFilter = window.runtime.flash.net.FileFilter;
		
		air.LocalConnection = window.runtime.flash.net.LocalConnection;
		air.NetConnection = window.runtime.flash.net.NetConnection;

		air.URLLoader = window.runtime.flash.net.URLLoader;
		air.URLLoaderDataFormat = window.runtime.flash.net.URLLoaderDataFormat;
		air.URLRequest = window.runtime.flash.net.URLRequest;
		air.URLRequestDefaults = window.runtime.flash.net.URLRequestDefaults;
		air.URLRequestHeader = window.runtime.flash.net.URLRequestHeader;
		air.URLRequestMethod = window.runtime.flash.net.URLRequestMethod;
		air.URLStream = window.runtime.flash.net.URLStream;
		air.URLVariables = window.runtime.flash.net.URLVariables;
		air.Socket = window.runtime.flash.net.Socket;
		air.XMLSocket = window.runtime.flash.net.XMLSocket;

		air.Responder = window.runtime.flash.net.Responder;
		air.ObjectEncoding = window.runtime.flash.net.ObjectEncoding;

		air.NetStream = window.runtime.flash.net.NetStream;
		air.SharedObject = window.runtime.flash.net.SharedObject;
		air.SharedObjectFlushStatus = window.runtime.flash.net.SharedObjectFlushStatus;

		// system
		air.Capabilities = window.runtime.flash.system.Capabilities;
		air.System = window.runtime.flash.system.System;
		air.Security = window.runtime.flash.system.Security;
		air.Updater = window.runtime.flash.desktop.Updater;

		// desktop
		air.Clipboard = window.runtime.flash.desktop.Clipboard;
		air.ClipboardFormats = window.runtime.flash.desktop.ClipboardFormats;
		air.ClipboardTransferMode = window.runtime.flash.desktop.ClipboardTransferMode;

		air.NativeDragManager = window.runtime.flash.desktop.NativeDragManager;
		air.NativeDragOptions = window.runtime.flash.desktop.NativeDragOptions;
		air.NativeDragActions = window.runtime.flash.desktop.NativeDragActions;

		air.Icon = window.runtime.flash.desktop.Icon;
		air.DockIcon = window.runtime.flash.desktop.DockIcon;
		air.InteractiveIcon = window.runtime.flash.desktop.InteractiveIcon;
		air.NotificationType = window.runtime.flash.desktop.NotificationType;
		air.SystemTrayIcon = window.runtime.flash.desktop.SystemTrayIcon;

		air.NativeApplication = window.runtime.flash.desktop.NativeApplication;

		// display
		air.NativeMenu = window.runtime.flash.display.NativeMenu;
		air.NativeMenuItem = window.runtime.flash.display.NativeMenuItem;
		air.Screen = window.runtime.flash.display.Screen;
		
		air.Loader	= window.runtime.flash.display.Loader;
		air.Bitmap = window.runtime.flash.display.Bitmap;
		air.BitmapData = window.runtime.flash.display.BitmapData;

		// ui
		air.Keyboard = window.runtime.flash.ui.Keyboard;
		air.KeyLocation = window.runtime.flash.ui.KeyLocation;
		air.Mouse = window.runtime.flash.ui.Mouse;


		// utils
		air.ByteArray = window.runtime.flash.utils.ByteArray;
		air.CompressionAlgorithm = window.runtime.flash.utils.CompressionAlgorithm;
		air.Endian = window.runtime.flash.utils.Endian;
		air.Timer = window.runtime.flash.utils.Timer;
		air.XMLSignatureValidator = window.runtime.flash.security.XMLSignatureValidator;

		air.HTMLLoader = window.runtime.flash.html.HTMLLoader;

		// media
		air.ID3Info = window.runtime.flash.media.ID3Info;
		air.Sound = window.runtime.flash.media.Sound;
		air.SoundChannel = window.runtime.flash.media.SoundChannel;
		air.SoundLoaderContext = window.runtime.flash.media.SoundLoaderContext;
		air.SoundMixer = window.runtime.flash.media.SoundMixer;
		air.SoundTransform = window.runtime.flash.media.SoundTransform;
		air.Microphone = window.runtime.flash.media.Microphone;
		air.Video = window.runtime.flash.media.Video;
		air.Camera = window.runtime.flash.media.Camera;

		// data
		air.EncryptedLocalStore = window.runtime.flash.data.EncryptedLocalStore;
		air.SQLCollationType = window.runtime.flash.data.SQLCollationType;
		air.SQLColumnNameStyle = window.runtime.flash.data.SQLColumnNameStyle;
		air.SQLColumnSchema = window.runtime.flash.data.SQLColumnSchema;
		air.SQLConnection = window.runtime.flash.data.SQLConnection;
		air.SQLError = window.runtime.flash.errors.SQLError;
		air.SQLErrorEvent = window.runtime.flash.events.SQLErrorEvent;
		air.SQLErrorOperation = window.runtime.flash.errors.SQLErrorOperation;
		air.SQLEvent = window.runtime.flash.events.SQLEvent;
		air.SQLIndexSchema = window.runtime.flash.data.SQLIndexSchema;
		air.SQLMode = window.runtime.flash.data.SQLMode;
		air.SQLResult = window.runtime.flash.data.SQLResult;
		air.SQLSchema = window.runtime.flash.data.SQLSchema;
		air.SQLSchemaResult = window.runtime.flash.data.SQLSchemaResult;
		air.SQLStatement = window.runtime.flash.data.SQLStatement;
		air.SQLTableSchema = window.runtime.flash.data.SQLTableSchema;
		air.SQLTransactionLockType = window.runtime.flash.data.SQLTransactionLockType;
		air.SQLTriggerSchema = window.runtime.flash.data.SQLTriggerSchema;
		air.SQLUpdateEvent = window.runtime.flash.events.SQLUpdateEvent;
		air.SQLViewSchema = window.runtime.flash.data.SQLViewSchema;

		// service monitoring framework
		air.__defineGetter__("ServiceMonitor", function() { return window.runtime.air.net.ServiceMonitor; });
		air.__defineGetter__("SocketMonitor", function() { return window.runtime.air.net.SocketMonitor; });
		air.__defineGetter__("URLMonitor", function() { return window.runtime.air.net.URLMonitor; });
}




// =============================================================================================================================================
//		 # FLASH
// =============================================================================================================================================

//v1.7
// Flash Player Version Detection
// Detect Client Browser type
// Copyright 2005-2007 Adobe Systems Incorporated.	All rights reserved.
var isIE	= (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;

function ControlVersion() {
	var version;
	var axo;
	var e;

	// NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry

	try {
		// version will be set for 7.X or greater players
		axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
		version = axo.GetVariable("$version");
	} catch (e) {}

	if (!version) {
		try {
			// version will be set for 6.X players only
			axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
			
			// installed player is some revision of 6.0
			// GetVariable("$version") crashes for versions 6.0.22 through 6.0.29,
			// so we have to be careful. 
			
			// default to the first public version
			version = "WIN 6,0,21,0";

			// throws if AllowScripAccess does not exist (introduced in 6.0r47)		
			axo.AllowScriptAccess = "always";

			// safe to call for 6.0r47 or greater
			version = axo.GetVariable("$version");

		} catch (e) {}
	}

	if (!version) {
		try {
			// version will be set for 4.X or 5.X player
			axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
			version = axo.GetVariable("$version");
		} catch (e) {}
	}

	if (!version) {
		try {
			// version will be set for 3.X player
			axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
			version = "WIN 3,0,18,0";
		} catch (e) {}
	}

	if (!version) {
		try {
			// version will be set for 2.X player
			axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
			version = "WIN 2,0,0,11";
		} catch (e) {
			version = -1;
		}
	}
	
	return version;
}

// JavaScript helper required to detect Flash Player PlugIn version information
function GetSwfVer(){
	// NS/Opera version >= 3 check for Flash plugin in plugin array
	var flashVer = -1;
	
	if (navigator.plugins != null && navigator.plugins.length > 0) {
		if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
			var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
			var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
			var descArray = flashDescription.split(" ");
			var tempArrayMajor = descArray[2].split(".");
			var versionMajor = tempArrayMajor[0];
			var versionMinor = tempArrayMajor[1];
			var versionRevision = descArray[3];
			if (versionRevision == "") {
				versionRevision = descArray[4];
			}
			if (versionRevision[0] == "d") {
				versionRevision = versionRevision.substring(1);
			} else if (versionRevision[0] == "r") {
				versionRevision = versionRevision.substring(1);
				if (versionRevision.indexOf("d") > 0) {
					versionRevision = versionRevision.substring(0, versionRevision.indexOf("d"));
				}
			}
			var flashVer = versionMajor + "." + versionMinor + "." + versionRevision;
		}
	}
	// MSN/WebTV 2.6 supports Flash 4
	else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4;
	// WebTV 2.5 supports Flash 3
	else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3;
	// older WebTV supports Flash 2
	else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2;
	else if ( isIE && isWin && !isOpera ) {
		flashVer = ControlVersion();
	}
	return flashVer;
}

// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision) {
	versionStr = GetSwfVer();
	if (versionStr == -1 ) {
		return false;
	} else if (versionStr != 0) {
		if(isIE && isWin && !isOpera) {
			// Given "WIN 2,0,0,11"
			tempArray				 = versionStr.split(" "); 	// ["WIN", "2,0,0,11"]
			tempString				= tempArray[1];			// "2,0,0,11"
			versionArray			= tempString.split(",");	// ['2', '0', '0', '11']
		} else {
			versionArray			= versionStr.split(".");
		}
		var versionMajor			= versionArray[0];
		var versionMinor			= versionArray[1];
		var versionRevision	 = versionArray[2];

					// is the major.revision >= requested major.revision AND the minor version >= requested minor
		if (versionMajor > parseFloat(reqMajorVer)) {
			return true;
		} else if (versionMajor == parseFloat(reqMajorVer)) {
			if (versionMinor > parseFloat(reqMinorVer)) return true;
			else if (versionMinor == parseFloat(reqMinorVer)) {
				if (versionRevision >= parseFloat(reqRevision)) return true;
			}
		}
		return false;
	}
}

function AC_AddExtension(src, ext) {
	if (src.indexOf('?') != -1) return src.replace(/\?/, ext+'?'); 
	else return src + ext;
}

function AC_Generateobj(objAttrs, params, embedAttrs) { 
	var str = '';
	if (isIE && isWin && !isOpera) {
		str += '<object ';
		for (var i in objAttrs) {
			str += i + '="' + objAttrs[i] + '" ';
		}
		str += '>';
		for (var i in params) {
			str += '<param name="' + i + '" value="' + params[i] + '" /> ';
		}
		str += '</object>';
	} else {
		str += '<embed ';
		for (var i in embedAttrs) {
			str += i + '="' + embedAttrs[i] + '" ';
		}
		str += '> </embed>';
	}

	document.write(str);
}

function AC_FL_RunContent() {
	var ret = 
		AC_GetArgs
		(	arguments, ".swf", "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
		 , "application/x-shockwave-flash"
		);
	AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
}

function AC_SW_RunContent() {
	var ret = 
		AC_GetArgs
		(	arguments, ".dcr", "src", "clsid:166B1BCA-3F9C-11CF-8075-444553540000"
		 , null
		);
	AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
}

function AC_GetArgs(args, ext, srcParamName, classid, mimeType) {
	var ret = new Object();
	ret.embedAttrs = new Object();
	ret.params = new Object();
	ret.objAttrs = new Object();
	for (var i=0; i < args.length; i=i+2){
		var currArg = args[i].toLowerCase();		

		switch (currArg){	
			case "classid":
				break;
			case "pluginspage":
				ret.embedAttrs[args[i]] = args[i+1];
				break;
			case "src":
			case "movie":	
				args[i+1] = AC_AddExtension(args[i+1], ext);
				ret.embedAttrs["src"] = args[i+1];
				ret.params[srcParamName] = args[i+1];
				break;
			case "onafterupdate":
			case "onbeforeupdate":
			case "onblur":
			case "oncellchange":
			case "onclick":
			case "ondblclick":
			case "ondrag":
			case "ondragend":
			case "ondragenter":
			case "ondragleave":
			case "ondragover":
			case "ondrop":
			case "onfinish":
			case "onfocus":
			case "onhelp":
			case "onmousedown":
			case "onmouseup":
			case "onmouseover":
			case "onmousemove":
			case "onmouseout":
			case "onkeypress":
			case "onkeydown":
			case "onkeyup":
			case "onload":
			case "onlosecapture":
			case "onpropertychange":
			case "onreadystatechange":
			case "onrowsdelete":
			case "onrowenter":
			case "onrowexit":
			case "onrowsinserted":
			case "onstart":
			case "onscroll":
			case "onbeforeeditfocus":
			case "onactivate":
			case "onbeforedeactivate":
			case "ondeactivate":
			case "type":
			case "codebase":
			case "id":
				ret.objAttrs[args[i]] = args[i+1];
				break;
			case "width":
			case "height":
			case "align":
			case "vspace": 
			case "hspace":
			case "class":
			case "title":
			case "accesskey":
			case "name":
			case "tabindex":
				ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
				break;
			default:
				ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
		}
	}
	ret.objAttrs["classid"] = classid;
	if (mimeType) ret.embedAttrs["type"] = mimeType;
	return ret;
}

function MM_CheckFlashVersion(reqVerStr,msg) {
	with(navigator){
		var isIE	= (appVersion.indexOf("MSIE") != -1 && userAgent.indexOf("Opera") == -1);
		var isWin = (appVersion.toLowerCase().indexOf("win") != -1);
		if (!isIE || !isWin){	
			var flashVer = -1;
			if (plugins && plugins.length > 0){
				var desc = plugins["Shockwave Flash"] ? plugins["Shockwave Flash"].description : "";
				desc = plugins["Shockwave Flash 2.0"] ? plugins["Shockwave Flash 2.0"].description : desc;
				if (desc == "") flashVer = -1;
				else{
					var descArr = desc.split(" ");
					var tempArrMajor = descArr[2].split(".");
					var verMajor = tempArrMajor[0];
					var tempArrMinor = (descArr[3] != "") ? descArr[3].split("r") : descArr[4].split("r");
					var verMinor = (tempArrMinor[1] > 0) ? tempArrMinor[1] : 0;
					flashVer =	parseFloat(verMajor + "." + verMinor);
				}
			}
			// WebTV has Flash Player 4 or lower -- too low for video
			else if (userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 4.0;

			var verArr = reqVerStr.split(",");
			var reqVer = parseFloat(verArr[0] + "." + verArr[2]);
	
			if (flashVer < reqVer){
				if (confirm(msg))
					window.location = "http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash";
			}
		}
	} 
}