/* BEGIN: GLOBAL */

// Global/framework.js
//
// Base framework for all Web Applications
//
// © 2010 REAL Software Inc. -- All Rights Reserved
// This code contains patent-pending technology.

// Array Support
if (!Array.indexOf) {
	Array.prototype.indexOf = function (obj) {
		var i;
		for (i = 0; i < this.length; i++) {
			if (this[i] == obj) {
				return i;
			}
		}
		return -1;
	};
}

// RGBA Support
function testRGBASupport ()
{
	var element = document.createElement("a");
	try {
		element.style.backgroundColor = 'rgba(0,0,0,0)';
		return true;
	} catch (err) {
		return false;
	}
}

// Chrome Frame Support
var gcfCheck = function ()
{
};

// REAL Studio Namespace
var RS = {
	version: 4,
	mode: 0,
	begin: function () {},
	init: function () {},
	getElementID: function () {},
	postLoadObjects: [],
	wheelTargets: [],
	refreshObjects: [],
	imageCache: [],
	controls: [],
	loaders: [],
	pageWindows: [],
	styleSheetLookups: [],
	changedFields: [],
	holdInterval: null,
	holdOffset: null,
	dragTarget: null,
	dragCallback: null,
	activeLoader: null,
	pageID: null,
	pageFinishedLoading: false,
	appURL: '',
	sessionURL: '',
	sessionID: '',
	refreshLocks: 0,
	serializer: null,
	rgbaSupported: testRGBASupport(),
	opacitySupported: true,
	stopInit: false,
	platform: -1,
	engine: -1,
	browser: -1,
	interactionAllowed: true,
	view: {},
	comm: {},
	menus: {},
	input: {
		begin: function () {},
		move: function () {},
		end: function () {},
		enter: function () {},
		exit: function () {},
		install: function () {},
		isTouchUI: function () {},
		getCoordinates: function () {},
		refireMove: function () {},
		forwardEvent: function () {},
		mouseEventData: function () {},
		doubleClick: function() {},
		keyDown: function () {},
		keyUp: function () {},
		keyPress: function () {},
		keyQueue: [],
		setFocusControl: function () {},
		mIsTouchUI: false,
		target: null,
		holdInterval: 0,
		lastCoordinates: [],
		lastEvent: null,
		setLastEvent: function () {},
		installedElements: [],
		pointerTarget: null,
		focusControl: null,
		touchHandler: function() {}
	},
	console: {
		error: function () {},
		log: function () {},
		present: function () {},
		dismiss: function () {},
		isVisible: function () {},
		submit: function () {},
		mIsVisible: false
	},
	events: {
		addListener: function () {},
		removeListener: function () {},
		preventDefault: function () {},
		stopPropagation: function () {},
		fireEvent: function() {}
	},
	DOM: {
		addClass: function () {},
		removeClass: function () {},
		getAppliedStyle: function () {},
		getArrayOfClassNames: function () {},
		hasClass: function () {},
		get: function() {},
		getElementsByClassName: function() {}
	},
	utils: {
		toCamelCase: function() {},
		toHyphens: function() {},
		tabCapture: function() {}
	},
	ua: { }
};

// Communication functions
RS.comm = {
	begin: function () {
		try {
			if(RS.mode == 1) {
				RS.comm.websockets.didConnect = false;
				var host = 'ws://' + window.location.host + '/' + RS.sessionID + "/comm/push";
				var sock = new WebSocket(host);
				sock.onopen = function (event) {
					clearTimeout(RS.comm.websockets.timeout);
					RS.comm.websockets.didConnect = true;
					RS.comm.sendOpen();
				};
				sock.onerror = function (event) {
					clearTimeout(RS.comm.websockets.timeout);
					RS.comm.ajax.begin();
				};
				sock.onclose = function () {
					if(RS.comm.websockets.didConnect) {
						//if it connected before, try again.
						RS.comm.begin(); 				
					} else {	
						clearTimeout(RS.comm.websockets.timeout);
						RS.comm.ajax.begin();
					}
				};
				sock.onmessage = function (event) {
					clearTimeout(RS.comm.websockets.timeout);
					RS.comm.processResponse(event.data);
				};
				
				RS.comm.websockets.socket = sock;
				//timeout for browsers using new websocket protocol w/ no fallback
				RS.comm.websockets.socket.timeout = setTimeout(RS.comm.websockets.socket.onerror,3000); 
			} else {
				RS.comm.ajax.begin();
			}
		} catch (wsErr) {
			RS.comm.ajax.begin();
		}
	},
	sendOpen: function () {
		if (RS.comm.inited === false) {
			RS.view.setLoaderState(2);
			var currentDate = new Date();
			var offset;
			if (currentDate.getTimeZoneOffset) {
				offset = currentDate.getTimeZoneOffset();
			} else {
				offset = currentDate.getTimezoneOffset();
				offset = offset / 60;
			}
			offset = offset * -1;
			
			var dimensions = RS.view.sizing.dimensions();
			
			var userdata = [];
			userdata.push(dimensions.width);
			userdata.push(dimensions.height);
			userdata.push(offset);
			userdata.push(window.location.hash);
			
			RS.comm.triggerEvent('Event','Open',userdata);
			RS.comm.inited = true;
			RS.view.monitorHashTag(window.location.hash);
		}
	},
	triggerEvent: function (control,event,userdata,ev) {
		var url = RS.sessionURL + '/comm/event/';
		if ((control !== null) && (control !== '')) {
			url = url + control + '.' + event;
		}
		
		if ((!userdata) || (userdata.constructor.toString().indexOf('Array') === -1)) {
			userdata = [];
		}
		
		var queries = prepareFormData(RS.changedFields);
		queries.push('params=' + encodeURIComponent(userdata.join('&')));
		RS.changedFields = [];
		
		if (RS.comm.websockets.connected() === true) {
			queries.push('url=' + url);
			queries.push('WSCookies=' + document.cookie);
			RS.comm.websockets.socket.send(queries.join('&'));
		} else {
			var xhr;
			if (window.XMLHttpRequest) {
				xhr = new XMLHttpRequest();
			} else {
				RS.console.error("This browser does not support AJAX.");
				return;
			}
			
			xhr.open('POST',url,true);
			xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
			xhr.setRequestHeader('Cache-Control','no-cache');
			xhr.onreadystatechange = function () {
				if (this.readyState === 4) {
					if (this.status === 200) {
						RS.comm.processResponse(this.responseText);
					} else if (this.status === 404) {
						RS.console.log("Requested content was not found.");
					} else {
						RS.console.log("HTTP Error: " + this.statusText);
					}
				}
			};
			xhr.send(queries.join('&'));
		}
	},
	processResponse: function (response) {
		var obj;
		try {
			obj = eval("(" + response + ")");
		} catch (jsonErr) {
			RS.console.error("Trouble evaluating response: " + jsonErr.message + "\nSource: " + response);
			return null;
		}
		
		// update the html
		var c = obj.html.length;
		var i = 0;
		var parent, target, o;
		for (i = 0; i < c; i++) {
			o = obj.html[i];
			
			parent = document.getElementById(o.parent);
			target = document.createElement('div');
			target.innerHTML = o.source;
			target = target.children[0];
			
			if (parent) {
				switch (o.mode) {
				case '1': // source should replace parent content
					parent.innerHTML = o.source;
					break;
				case '2': // source should be appended to parent content
					parent.appendChild(target);
					break;
				case '3': // source should be prepended to parent content
					parent.insertBefore(target,parent.firstChild);
					break;
				case '4': // parent should be removed
					if (parent.parentNode) {
						parent.parentNode.removeChild(parent);
					}
					break;
				case '5': // source should replace parent
					if (parent.parentNode) {
						var destination = parent.parentNode;
						destination.removeChild(parent);
						destination.appendChild(target);
					}
					break;
				}
			}
		}
		
		// insert css
		var css = obj.cssSource;
		var cssID = obj.cssID || "";
		createStyleSheet(css,cssID);
		
		// execute the returned javascript
		if (obj.jsSource !== '' ) {
			RS.view.refresh.lock();
			try {
				eval(obj.jsSource);
			} catch (jsErr) {
				RS.console.error("Could not execute returned javascript: " + jsErr.message + "\nSource: " + obj.jsSource);
			}
			RS.view.refresh.unlock();
		}
		
		// done
		didFinishLoading();
		
		// execute post-load
		var object;
		for (i = 0; i < RS.postLoadObjects.length; i++) {
			object = RS.postLoadObjects[i];
			object.postLoad();
		}
		
		return obj;
	},
	ajax: {
		begin: function () {
			RS.comm.sendOpen();
			
			if ((RS.comm.websockets.connected() === true) || (RS.comm.ajax.xhr !== null)) {
				return;
			}
			
			var xhr;
			if (window.XMLHttpRequest) {
				xhr = new XMLHttpRequest();
			} else {
				return;
			}
			
			xhr.open('POST',RS.sessionURL + '/comm/push',true);
			xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
			xhr.setRequestHeader('Cache-Control','no-cache');
			xhr.onreadystatechange = function () {
				if (this.readyState === 4) {
					RS.comm.ajax.xhr = null;
					if (this.status === 200) {
						RS.comm.processResponse(this.responseText);
					} else {
						setTimeout(RS.comm.ajax.ping,3000);
					}
				}
			};
			xhr.send();
			
			RS.comm.ajax.xhr = xhr;
		},
		end: function (gracefully) {
			if (gracefully === null) {
				gracefully = false;
			}
			if (RS.comm.ajax.xhr) {
				RS.comm.ajax.xhr.abort();
				RS.comm.ajax.xhr = null;
				if (gracefully === false) {
					preventPageInteraction();
				}
			}
		},
		ping: function () {
			var xhr;
			if (window.XMLHttpRequest) {
				xhr = new XMLHttpRequest();
			} else {
				return;
			}
			
			xhr.open('POST',RS.sessionURL + '/comm/ping',true);
			xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
			xhr.setRequestHeader('Cache-Control','no-cache');
			xhr.onreadystatechange = function () {
				if (this.readyState === 4) {
					if (this.status === 200) {
						RS.comm.processResponse(this.responseText);
					} else {
						RS.view.preventInteraction();
					}
				}
			};
			xhr.send();
		},
		xhr: null
	},
	websockets: {
		connected: function () {
			if ((RS.comm.websockets.socket !== null) && (RS.comm.websockets.socket.readyState === 1)) {
				return true;
			} else {
				return false;
			}
		},
		socket: null
	},
	terminate: function () {
		if (RS.comm.websockets.connected() === true) {
			RS.comm.websockets.socket.onclose = function () {};
			RS.comm.websockets.socket.close();
		}
		if (RS.comm.ajax.xhr !== null) {
			RS.comm.ajax.xhr.abort();
			RS.comm.ajax.xhr = null;
		}
		RS.view.preventInteraction();
	},
	inited: false
};

// View Control
RS.view = {
	showOverlay: function () {
		if (RS.view.overlayCount == 0) {
			var overlay = document.getElementById('REALOverlay');
			if (overlay) {
				overlay.style.display = 'block';
			}
			
			RS.view.preventScrolling();
		}
		RS.view.overlayCount = RS.view.overlayCount + 1;
	},
	hideOverlay: function () {
		if (RS.view.overlayCount <= 0) {
			return;
		}
		
		RS.view.overlayCount = RS.view.overlayCount - 1;
		
		if (RS.view.overlayCount == 0) {
			var overlay = document.getElementById('REALOverlay');
			if (overlay) {
				overlay.style.display = 'none';
			}
			
			RS.view.allowScrolling();
		}
	},
	overlayCount: 1,
	preventScrolling: function () {
		RS.menus.dismissAllMenus();
		if (RS.view.scrollPreventionCount === 0) {
			var container = document.getElementById('REALContainer');
			if (container) {
				container.style.minWidth = '100%';
				container.style.minHeight = '100%';
			}
		}
		RS.view.scrollPreventionCount = RS.view.scrollPreventionCount + 1;
	},
	allowScrolling: function () {
		RS.menus.dismissAllMenus();
		if (RS.view.scrollPreventionCount <= 0) {
			return;
		}
		
		RS.view.scrollPreventionCount = RS.view.scrollPreventionCount - 1;
		
		if (RS.view.scrollPreventionCount === 0) {
			var container = document.getElementById('REALContainer');
			var page = document.getElementById(RS.view.currentPage);
			if ((container) && (page)) {
				container.style.minWidth = page.style.minWidth;
				container.style.minHeight = page.style.minHeight;
			}
		}
	},
	scrollPreventionCount: 1,
	dismissLoader: function () {
		try { clearTimeout(loaderTimeout); } catch(err) { }
		RS.view.setLoaderState(3);
		document.getElementById('REALLoader').style.top = '100%';
		setTimeout("document.getElementById('REALLoader').style.display = 'none'",100);
		setTimeout(RS.view.hideOverlay,101);
	},
	setLoaderState: function (state) {
		var bar = document.getElementById('REALLoaderBar');
		bar.style.backgroundPosition = '0px -' + (state * 21) + 'px';
	},
	showPage: function (pageID) {
		if (RS.view.currentPage !== '') {
			var el = document.getElementById(RS.view.currentPage);
			if (el) {
				el.style.display = 'none';
			}
			RS.comm.triggerEvent(RS.view.currentPage,'Hidden');
		}
		
		var page = document.getElementById(pageID);
		var container = document.getElementById('REALPages');
		container.style.minWidth = page.style.minWidth;
		container.style.minHeight = page.style.minHeight;
		
		var container = document.getElementById('REALContainer');
		container.style.minWidth = page.style.minWidth;
		container.style.minHeight = page.style.minHeight;
		
		RS.view.currentPage = pageID;
		page.style.left = '0';
		page.style.display = '';
		
		RS.comm.triggerEvent(pageID,'Shown');
	},
	setStatus: function (message) {
		try {
			window.status = message;
		} catch (statErr) {
		}
	},
	setHashTag: function (hash) {
		RS.view.lastHashTag = hash;
		window.location.hash = hash;
	},
	monitorHashTag: function (startingTag) {
		RS.view.lastHashTag = startingTag;
		
		var fn = function () {
			if (window.location.hash != RS.view.lastHashTag) {
				RS.view.lastHashTag = window.location.hash;
				RS.comm.triggerEvent('Event','HashTagChanged',[window.location.hash]);
			}
		};
		if("onhashchange" in window) {
			RS.events.addListener(window,"hashchange",fn,false);
		} else {
			setInterval(fn,250);
		}
	},
	preventInteraction: function () {
		RS.view.showOverlay();
		var blocker = document.getElementById('REALDisconnect');
		if ((blocker) && (blocker.style.display !== 'block')) {
			blocker.style.display = 'block';
			setTimeout("document.getElementById('REALDisconnect').style.top = '0%'",10);
			
			//Stop any controls that continue to cause events to fire
			for(i=0;i<RS.controls.length;i++) {
				var ctl = RS.controls[i];
				try { if(ctl.constructor == timer) { RS.controls[i].setMode(0); }} catch(err) { }
				try { if(ctl.constructor == movieplayer) {
						var id=RS.controls[i].controlID;
						var el = document.getElementById(id);
						el.parentNode.removeChild(el);  }} catch(err) { }
				try { if(ctl.constructor == spinner) { ctl.setEnabled(false); }} catch(err) { }
			}			
		}
	},
	allowInteraction: function () {
		var blocker = document.getElementById('REALDisconnect');
		if ((blocker) && (blocker.style.display === 'block')) {
			blocker.style.top = '100%';
			setTimeout("document.getElementById('REALDisconnect').style.display = 'none'",10);
			setTimeout(RS.view.hideOverlay,11);
		}
	},
	handleConfirmation: function (event) {
		if ((RS.view.confirmationText !== null) && (RS.view.confirmationText !== '')) {
			var r = RS.view.confirmationText;
			event = event || window.event;
			if (event !== null) {
				event.returnValue = r;
			}
			return r;
		}
	},
	confirmationText: null,
	currentPage: '',
	lastHashTag: '',
	sizing: {
		resized: function () {
			RS.menus.dismissAllMenus();
			if (RS.view.sizing.notifyDelay !== 0) {
				clearTimeout(RS.view.sizing.notifyDelay);
			}
			RS.view.sizing.notifyControls(false);
			RS.view.sizing.notifyDelay = setTimeout(RS.view.sizing.notifyServer,250);
		},
		rotated: function () {
			RS.view.sizing.notifyServer();
		},
		notifyControls: function (complete) {
			if (RS.stopInit === true) {
				return;
			}
			
			var object, i;
			RS.view.refresh.lock();
			for (i = 0; i < RS.view.sizing.targets.length; i++) {
				object = RS.view.sizing.targets[i];
				object.resize();
				if (complete === true) {
					object.resizeComplete();
				}
			}
			RS.view.refresh.unlock();
		},
		notifyServer: function () {
			RS.view.sizing.notifyControls(true);
			
			var userdata = [];
			var d = RS.view.sizing.dimensions();
			userdata.push(d.width);
			userdata.push(d.height);
			RS.comm.triggerEvent('Event','Resized',userdata);
		},
		dimensions: function () {
			var view = document.getElementById('REALContainer');
			return { width : view.offsetWidth , height : view.offsetHeight };
		},
		notifyDelay: 0,
		targets: []
	},
	refresh: {
		lock: function () {
			RS.view.refresh.lockCount = Math.max(RS.view.refresh.lockCount,0) + 1;
		},
		unlock: function (caller) {
			if (RS.view.refresh.lockCount <= 0) {
				return;
			}
			RS.view.refresh.lockCount = RS.view.refresh.lockCount - 1;
			
			if (RS.view.refresh.lockCount === 0) {
				var i,obj;
				for (i = 0; i < RS.view.refresh.targets.length; i++) {
					obj = RS.view.refresh.targets[i];
					var el = document.getElementById(obj.controlID);
					if(el) {
						if ((obj.refresh) && (obj != caller)) {
							obj.refresh();
						}
					}
				}
				RS.view.refresh.targets = [];
			}
		},
		locked: function () {
			if (RS.view.refresh.lockCount > 0) {
				return true;
			} else {
				return false;
			}
		},
		lockCount: 0,
		targets: []
	}
};

RS.menus = {
	create: function (menu,depth) {
		if (typeof(depth) === 'undefined') {
			depth = -1;
		}
		
		var action = function (event) {
			event = event || window.event;
			RS.events.preventDefault(event);
			RS.events.stopPropagation(event);
			
			if (RS.menus.mouseDownItem === null) {
				return;
			}
			
			var menuItem;
			if (this.id) {
				menuItem = this;
			} else if ((event.srcElement) || (event.target)) {
				menuItem = event.srcElement || event.target;
			} else {
				RS.menus.mouseDownItem = null;
				return;
			}
			var menuID = menuItem.id.substring(0,10);
			menuItem = document.getElementById(menuID + '_item');
			
			if ((menuItem.className == 'item') && (menuItem === RS.menus.mouseDownItem)) {
				if (RS.menus.items.indexOf(menuID) === -1) {
					if (RS.menus.menuTarget !== null) {
						RS.menus.menuTarget.menuItemSelected(menuID);
					}
					RS.menus.dismissAllMenus();
				}
			}
		};
		
		var mouseDown = function (event) {
			event = event || window.event;
			RS.events.preventDefault(event);
			RS.events.stopPropagation(event);
			
			var menuItem;
			if (this.id) {
				menuItem = this;
			} else if ((event.srcElement) || (event.target)) {
				menuItem = event.srcElement || event.target;
			} else {
				return;
			}
			var menuID = menuItem.id.substring(0,10);
			RS.menus.mouseDownItem = document.getElementById(menuID + '_item');
		};
		
		var item = document.getElementById(menu.menuID + '_item');
		if (!item) {
			item = document.createElement('li');
			item.id = menu.menuID + '_item';
			
			RS.events.addListener(item,'mousedown',mouseDown,false);
			RS.events.addListener(item,'contextmenu',action,false);
		}
		
		if (menu.text === '-') {
			item.className = 'separator';
			return item;
		}
		
		var caption;
		if (item.children.length < 1) {
			caption = document.createElement('span');
			caption.id = menu.menuID + "_caption";
			RS.events.addListener(caption,'mousedown',mouseDown,false);
			RS.events.addListener(caption,'contextmenu',action,false);
			item.appendChild(caption);
		} else {
			caption = item.children[0];
		}
		
		if (menu.children.length > 0) {
			RS.menus.items[menu.menuID] = menu;
			RS.menus.items.push(menu.menuID);
			
			caption.className = 'submenu';
			
			var shell = document.getElementById(menu.menuID);
			if (!shell) {
				shell = document.createElement('div');
				shell.id = menu.menuID;
				shell.style.display = 'none';
				
				var pointer = document.createElement('img');
				pointer.id = menu.menuID + '_pointer';
				pointer.className = 'pointer';
				pointer.src = '/scoringhut.cgi/framework/spacer.gif';
				RS.events.addListener(pointer,'contextmenu',RS.input.contextClick,false);
				shell.appendChild(pointer);
				
				document.getElementById('REALMenus').appendChild(shell);
			}
			var ul = document.getElementById(menu.menuID + '_inner');
			if (!ul) {
				ul = document.createElement('ul');
				ul.id = menu.menuID + '_inner';
				ul.className = 'menu';
				ul.style.display = 'inline-block';
				shell.appendChild(ul);
			} else {
				if (ul.hasChildNodes()) {
					while (ul.childNodes.length >= 1) {
						ul.removeChild(ul.firstChild);
					}
				}
			}
			
			var i;
			for (i = 0; i < menu.children.length; i++) {
				ul.appendChild(RS.menus.create(menu.children[i],depth + 1));
			}
			
			var showMenu = function (event) {
				event = event || window.event;
				RS.events.preventDefault(event);
				RS.events.stopPropagation(event);
				
				var menuItem;
				if (this.id) {
					menuItem = this;
				} else if ((event.srcElement) || (event.target)) {
					menuItem = event.srcElement || event.target;
				} else {
					return;
				}
				var menuID = menuItem.id.substring(0,10);
				menuItem = document.getElementById(menuID + '_item');
				
				var pos = getPosition(menuItem);
				var r = new Rect(pos.x,pos.y,menuItem.offsetWidth,menuItem.offsetHeight);
				var x = r.right;
				var edge = 1;
				var width = document.getElementById('REALContainer').offsetWidth - 40;
				if (width - r.right < r.width()) {
					x = r.left;
					edge = 3;
				}
				RS.menus.displayMenuForPoint(menuItem.id.substring(0,10),x,r.verticalCenter(),edge,depth + 1);
			};
			RS.events.addListener(item,'mouseover',showMenu,false);
		} else {
			var hideMenus = function (event) {
				event = event || window.event;
				RS.events.preventDefault(event);
				RS.events.stopPropagation(event);
				
				RS.menus.dismissAllMenus(depth + 1);
			};
			RS.events.addListener(item,'mouseover',hideMenus,false);
		}
		
		if (menu.enabled === true) {
			RS.events.addListener(item,'mouseup',action,false);
			item.className = 'item';
		} else {
			item.className = 'disabled';
		}
		while (caption.firstChild !== null) {
			caption.removeChild(caption.firstChild);
		}
		caption.appendChild(document.createTextNode(menu.text));
		
		return item;
	},
	remove: function (menuID) {
	},
	findSuitableEdgeForRect: function (originRect, menuRect) {
		var dimensions = RS.view.sizing.dimensions();
		var bottomSpace = dimensions.height - originRect.bottom;
		var rightSpace = dimensions.width - originRect.right;
		var leftSpace = originRect.left;
		var topSpace = originRect.top;
		
		if (topSpace >= menuRect.height() + 20) {
			return 0;
		}
		if (bottomSpace >= menuRect.height() + 20) {
			return 2;
		}
		if (rightSpace >= menuRect.width() + 20) {
			return 1;
		}
		if (leftSpace >= menuRect.width() + 20) {
			return 3;
		}
		
		return 4;
	},
	displayMenuForRect: function (menuID, rect, mouseX, mouseY, depth) {
		if (RS.menus.visibleItems.length === 0) {
			var container = document.getElementById('REALMenus');
			container.style.visibility = 'hidden';
			container.style.display = 'block';
		}
		document.getElementById(menuID).style.display = 'inline-block';
		
		var ul = document.getElementById(menuID + '_inner');
		var menuRect = new Rect(0,0,ul.offsetWidth,ul.offsetHeight)
		var edge = RS.menus.findSuitableEdgeForRect(rect,menuRect);
		
		switch (edge) {
			case 0:
				RS.menus.displayMenuForPoint(menuID,rect.horizontalCenter(),rect.top,edge,depth);
				break;
			case 1:
				RS.menus.displayMenuForPoint(menuID,rect.right,rect.verticalCenter(),edge,depth);
				break;
			case 2:
				RS.menus.displayMenuForPoint(menuID,rect.horizontalCenter(),rect.bottom,edge,depth);
				break;
			case 3:
				RS.menus.displayMenuForPoint(menuID,rect.left,rect.verticalCenter(),edge,depth);
				break;
			case 4:
				if ((mouseX === null) || (mouseY === null)) {
					RS.menus.displayMenuForPoint(menuID,rect.horizontalCenter(),rect.verticalCenter(),edge,depth);
				} else {
					RS.menus.displayMenuForPoint(menuID,mouseX,mouseY,-1,depth);
				}
				break;
		}
	},
	displayMenuForPoint: function (menuID, x, y, edge, depth) {
		if (typeof(depth) === 'undefined') {
			depth = 0;
		}
		RS.menus.dismissAllMenus(depth);
		
		var container = document.getElementById('REALMenus');
		var menu = document.getElementById(menuID);
		var pointer = document.getElementById(menuID + '_pointer');
		var menuInner = document.getElementById(menuID + '_inner');
		if ((container) && (menu) && (menuInner) && (pointer)) {
		} else {
			return;
		}
		container.style.visibility = 'hidden';
		container.style.display = 'block';
		menu.style.display = 'inline-block';
		container.style.visibility = 'visible';
		var menuRect = new Rect(0,0,menuInner.offsetWidth,menuInner.offsetHeight);
		
		switch (edge) {
			case -1:
				var originRect = new Rect(x - 1,y - 1,2,2);
				edge = RS.menus.findSuitableEdgeForRect(originRect,menuRect);
				break;
			case 5:
				var rightSpace = container.offsetWidth - x;
				if (rightSpace > menuRect.width()) {
					edge = 1;
				} else {
					edge = 3;
				}
				break;
			case 6:
				var bottomSpace = container.offsetHeight - y;
				if (bottomSpace > menuRect.height()) {
					edge = 2;
				} else {
					edge = 0;
				}
				break;
		}
		
		if (x < 35) {
			edge = 1;
		} else if (x > container.offsetWidth - 35) {
			edge = 3;
		}
		
		var menuOuter,pointerRect,diff;
		
		menu.style.position = 'absolute';
		switch (edge) {
			case 0:
				menuOuter = new Rect(x - Math.floor(menuRect.width() / 2),y - (menuRect.height() + 20),menuRect.width(),menuRect.height() + 20);
				pointerRect = new Rect(Math.floor(menuRect.width() / 2) - 20,menuRect.height(),40,20);
				
				if (menuOuter.left < 10) {
					diff = 10 - menuOuter.left;
					menuOuter.offset(diff,0);
					pointerRect.offset(diff * -1,0);
				} else if (menuOuter.right > container.offsetWidth - 10) {
					diff = menuOuter.right - (container.offsetWidth - 10);
					menuOuter.offset(diff * -1,0);
					pointerRect.offset(diff,0);
				}
				pointerRect.left = Math.min(Math.max(pointerRect.left,5),menuRect.width() - 45);
				pointerRect.right = pointerRect.left + 40;
				
				menu.style.top = menuOuter.top + 'px';
				menu.style.left = menuOuter.left + 'px';
				menu.style.visibility = 'visible';
				menuInner.style.top = '0px';
				menuInner.style.left = '0px'
				pointer.width = '40';
				pointer.height = '20';
				pointer.style.top = pointerRect.top + 'px';
				pointer.style.left = pointerRect.left + 'px';
				pointer.style.backgroundPosition = '0px 20px';
				
				break;
			case 1:
				menuOuter = new Rect(x,y - Math.floor(menuRect.height() / 2),menuRect.width() + 20,menuRect.height());
				pointerRect = new Rect(0,Math.floor(menuRect.height() / 2) - 20,20,40);
				
				if (menuOuter.top < 10) {
					diff = 10 - menuOuter.top;
					menuOuter.offset(0,diff);
					pointerRect.offset(0,diff * -1);
				} else if (menuOuter.bottom > container.offsetHeight - 10) {
					diff = menuOuter.bottom - (container.offsetHeight - 10);
					menuOuter.offset(0,diff * -1);
					pointerRect.offset(0,diff);
				}
				pointerRect.top = Math.min(Math.max(pointerRect.top,5),menuRect.height() - 45);
				pointerRect.bottom = pointerRect.top + 40;
				
				menu.style.top = menuOuter.top + 'px';
				menu.style.left = menuOuter.left + 'px';
				menu.style.visibility = 'visible';
				menuInner.style.top = '0px';
				menuInner.style.left = '20px';
				pointer.width = '20';
				pointer.height = '40';
				pointer.style.top = pointerRect.top + 'px';
				pointer.style.left = pointerRect.left + 'px';
				pointer.style.backgroundPosition = '0px 0px';
				break;
			case 2:
				menuOuter = new Rect(x - Math.floor(menuRect.width() / 2),y,menuRect.width(),menuRect.height() + 20);
				pointerRect = new Rect(Math.floor(menuRect.width() / 2) - 20,0,40,20);
				
				if (menuOuter.left < 10) {
					diff = 10 - menuOuter.left;
					menuOuter.offset(diff,0);
					pointerRect.offset(diff * -1,0);
				} else if (menuOuter.right > container.offsetWidth - 10) {
					diff = menuOuter.right - (container.offsetWidth - 10);
					menuOuter.offset(diff * -1,0);
					pointerRect.offset(diff,0);
				}
				pointerRect.left = Math.min(Math.max(pointerRect.left,5),menuRect.width() - 45);
				pointerRect.right = pointerRect.left + 40;
				
				menu.style.top = menuOuter.top + 'px';
				menu.style.left = menuOuter.left + 'px';
				menu.style.visibility = 'visible';
				menuInner.style.top = '20px';
				menuInner.style.left = '0px';
				pointer.width = '40';
				pointer.height = '20';
				pointer.style.top = pointerRect.top + 'px';
				pointer.style.left = pointerRect.left + 'px';
				pointer.style.backgroundPosition = '0px 0px';
				break;
			case 3:
				menuOuter = new Rect(x - (menuRect.width() + 20),y - Math.floor(menuRect.height() / 2),menuRect.width() + 20,menuRect.height());
				pointerRect = new Rect(menuRect.width(),Math.floor(menuRect.height() / 2) - 20,20,40);
				
				if (menuOuter.top < 10) {
					diff = 10 - menuOuter.top;
					menuOuter.offset(0,diff);
					pointerRect.offset(0,diff * -1);
				} else if (menuOuter.bottom > container.offsetHeight - 10) {
					diff = menuOuter.bottom - (container.offsetHeight - 10);
					menuOuter.offset(0,diff * -1);
					pointerRect.offset(0,diff);
				}
				pointerRect.top = Math.min(Math.max(pointerRect.top,5),menuRect.height() - 45);
				pointerRect.bottom = pointerRect.top + 40;
				
				menu.style.top = menuOuter.top + 'px';
				menu.style.left = menuOuter.left + 'px';
				menu.style.visibility = 'visible';
				menuInner.style.top = '0px';
				menuInner.style.left = '0px';
				pointer.width = '20';
				pointer.height = '40';
				pointer.style.top = pointerRect.top + 'px';
				pointer.style.left = pointerRect.left + 'px';
				pointer.style.backgroundPosition = '20px 0px';
				break;
			case 4:
				menuOuter = new Rect(x - Math.floor(menuRect.width() / 2),y - Math.floor(menuRect.height() / 2),menuRect.width(),menuRect.height());
				
				menu.style.top = menuOuter.top + 'px';
				menu.style.left = menuOuter.left + 'px';
				menu.style.visibility = 'visible';
				menuInner.style.top = '0px';
				menuInner.style.left = '0px';
				pointer.style.display = 'none';
				break;
		}
		RS.menus.visibleItems.push(menuID);
	},
	dismissMenu: function (menuID) {
		var idx = RS.menus.visibleItems.indexOf(menuID);
		if (idx === -1) {
			return;
		}
		
		var menu = document.getElementById(menuID);
		menu.style.display = 'none';
		RS.menus.visibleItems.splice(idx,1);
		
		if (RS.menus.visibleItems.length === 0) {
			var container = document.getElementById('REALMenus');
			container.style.display = 'none';
			container.style.visibility = 'visible';
			RS.menus.menuTarget = null;
		}
	},
	dismissAllMenus: function (depth) {
		if (typeof(depth) === 'undefined') {
			depth = 0;
		}
		while (RS.menus.visibleItems.length > depth) {
			RS.menus.dismissMenu(RS.menus.visibleItems[depth]);
		}
	},
	items: [],
	visibleItems: [],
	mouseDownItem: null,
	menuTarget: null,
	action: 'context'
};

// Library Functions
RS.begin = function (sessionID)
{
	gcfCheck();
	if (RS.stopInit === true) {
		return;
	}
	
	RS.sessionID = sessionID;
	RS.appURL = '/scoringhut.cgi';
	RS.sessionURL = RS.appURL + "/" + RS.sessionID;
	
	try {
		var test = document.createElement('div');
		var touchEventName = 'ontouchstart';
		RS.input.mIsTouchUI = (touchEventName in test);
		if (!RS.input.mIsTouchUI) {
			test.setAttribute(touchEventName,'return');
			RS.input.mIsTouchUI = typeof test[touchEventName] == 'function';
		}
		test = null;
	} catch (touchErr) {
		RS.input.mIsTouchUI = false;
	}
	
	document.onmousemove = RS.input.move;
	document.onmouseup = RS.input.end;
	document.onkeydown = RS.input.keyDown;
	document.onkeyup = RS.input.keyUp;
	document.onkeypress = RS.input.keyPress;
	window.onresize = RS.view.sizing.resized;
	
	RS.events.addListener(document,'mousewheel', trackMouseWheel, false);
	RS.events.addListener(window,'mousewheel', trackMouseWheel, false);
	RS.events.addListener(window,'DOMMouseScroll', trackMouseWheel, false);
		
	window.onbeforeunload = RS.view.handleConfirmation;
	
	RS.events.addListener(document.body,'contextmenu',RS.input.contextClick,false);
	RS.events.addListener(window,'blur',function () { RS.menus.dismissAllMenus(0) },false);
	
	cacheImage('/scoringhut.cgi/framework/appicon128.png');
	cacheImage('/scoringhut.cgi/framework/pagestop.png');
	cacheImage('/scoringhut.cgi/framework/dimmer.png');
	cacheImage('/scoringhut.cgi/framework/pointer.png');
	cacheImage('/scoringhut.cgi/framework/submenu-normal.png');
	cacheImage('/scoringhut.cgi/framework/submenu-hover.png');
	cacheImage('/scoringhut.cgi/framework/AlerterButtons.png');
	
	setTimeout('RS.view.setLoaderState(1)',0);
	var commDelay = 1;
	if(RS.ua.mobile=="Android") { commDelay = 1000; };
	setTimeout(RS.comm.begin,commDelay);
};

function didFinishLoading (pageID)
{
	var i, controlID, control;
	for (i = 0; i < RS.controls.length; i++) {
		controlID = RS.controls[i];
		control = RS.controls[controlID];
		if (control.hasFinishedLoading === false) {
			control.didFinishLoading();
			control.hasFinishedLoading = true;
		}
	}
}

function getPosition (e)
{ 
	var left = 0; 
	var top  = 0; 
	
	while (e.offsetParent){ 
		left += e.offsetLeft; 
		top += e.offsetTop; 
		e = e.offsetParent;
	} 
	
	left += e.offsetLeft; 
	top += e.offsetTop; 
	
	return {x:left, y:top};
}

function mouseCoords (ev)
{
	ev = ev || window.event;
	if (!ev) {
		return {x:0, y:0};
	}
	if (ev.pageX || ev.pageY) {
		return {x:ev.pageX, y:ev.pageY};
	}
	return {x:ev.clientX + document.body.scrollLeft - document.body.clientLeft, y: ev.clientY + document.body.scrollTop - document.body.clientTop};
}


function getMouseOffset (target, ev)
{
	ev = ev || window.event;
	var docPos = getPosition(target);
	var mousePos = mouseCoords(ev);
	return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
}

function prepareFormData (fields)
{
	var queries = [];
	var element;
	var v;
	for (var i = 0; i < fields.length; i++) {
		element = fields[i];
		if (!element) {
			continue;
		}
		v = encodeURIComponent(element.value());
		queries.push(element.name() + '=' + v);
	}
	return queries;
}

function addPostLoadObject (object)
{
	RS.postLoadObjects.push(object);
}

function getPageDimensions ()
{
	var page = document.getElementById(RS.view.currentPage);
	return { width : page.offsetWidth , height : page.offsetHeight };
}

function addWheelTarget (target)
{
	RS.wheelTargets.push(target);
}

function preventEventDefault(ev)
{
	if (ev) {
		if (ev.preventDefault) {
			ev.preventDefault();
		}
		ev.returnValue = false;
	}
}

function trackMouseWheel (ev)
{
	var deltaX = 0;
	var deltaY = 0;
	if (!ev) {
		ev = window.event;
	}
	if ((ev.wheelDeltaX !== undefined) && (ev.wheelDeltaY !== undefined)) {
		deltaX = ev.wheelDeltaX / 120;
		deltaY = ev.wheelDeltaY / 120;
		if (!window.opera) {
			deltaX = deltaX * -1;
			deltaY = deltaY * -1;
		}
	} else if (ev.wheelDelta) {
		deltaY = ev.wheelDelta / 120;
		if (!window.opera) {
			deltaY = deltaY * -1;
		}
	} else if (ev.detail) {
		deltaY = (ev.detail / 3);
	}
	
	if ((deltaX === 0) && (deltaY === 0)) {
		return;
	}
	
	var i, target, pos, mousePos, handled;
	
	mousePos = mouseCoords(ev);
	handled = false;
	for (i = 0; i < RS.wheelTargets.length; i++) {
		target = RS.wheelTargets[i];
		pos = getPosition(target.object());
		
		if ((handled === false) && (mousePos.x >= pos.x) && (mousePos.x <= pos.x + target.object().offsetWidth) && (mousePos.y >= pos.y) && (mousePos.y <= pos.y + target.object().offsetHeight)) {
			if (target.enabled()) {
				handled = target.mouseWheel(deltaX, deltaY);
				if (handled) {
					break;
				}
			}
		}
	}
		
	if ((handled === true) && (ev)) {
		RS.events.preventDefault(ev);
	}
}

function findChildrenByClass (parent, className)
{
	var children = parent.children;
	var results = Array();
	var i;
	
	for (i = 0; i < children.length; i++) {
		if (children.item(i).className == className) {
			results.push(children.item(i));
		}
	}
	
	return results;
}

function isRefreshingLocked ()
{
	return (RS.refreshLocks > 0);
}

function cacheImage (url,useCache)
{
	if(useCache==undefined) useCache = true;
	var img,i,s;
	if(useCache) {
		for (i = 0; i < RS.imageCache.length; i++) {
			s = RS.imageCache[i].src;
			s = s.substring(s.length - url.length);
			if (s === url) {
				img = RS.imageCache[i];
				break;
			}
		}
	}
	
	if (img == null) {
		img = new Image();
		img.src = url;
		img.loaded = false;
		RS.imageCache.push(img);
	} else {
		img.loaded = true;
	}
	
	return img;
}

// Compatibility

function outerHTML (element)
{
	try {
		if (RS.serializer === null) {
			RS.serializer = new XMLSerializer();
		}
		return RS.serializer.serializeToString(element);
	} catch (err) {
		return element.outerHTML;
	}
}

function createRandomString (length)
{
	var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
	var s = "";
	var i,r;
	for (i = 0; i < length; i++) {
		r = Math.floor(Math.random() * chars.length);
		s += chars.substring(r,r + 1);
	}
	return s;
}

// Style Manipulation

function createStyleSheet (source,id)
{
	if (source == "") { return; }
	var sheet;
	var oldsheet = document.getElementById(id);
	if(!oldsheet || id == "") {
		sheet = document.createElement("style");
		sheet.type = "text/css";
		sheet.rel = "stylesheet";
		sheet.media = "all";
		if(id!==undefined && id!=="") sheet.id = id;
	} else {
		sheet = oldsheet;
	}

	try {
		sheet.innerHTML = source;
	} catch (err) {
		try {
			sheet.styleSheet.cssText = source;
		} catch (err) {
			sheet.style.cssText = source;
		}
	}
	if(!oldsheet) {
		document.getElementsByTagName("head")[0].appendChild(sheet);
	}
	return sheet;
}

function markControlChanged (field)
{
	var i;
	for (i = 0; i < RS.changedFields.length; i++) {
		if (RS.changedFields[i] == field) {
			return;
		}
	}
	RS.changedFields.push(field);
}

// Input Support (Mouse, Fingers, etc)

RS.input.isTouchUI = function ()
{
	return RS.input.mIsTouchEnabled;
};

RS.input.install = function (element)
{
	if (RS.input.installedElements.indexOf(element.id) == -1) {
		RS.events.addListener(element,"mousedown",RS.input.begin,false);
		RS.events.addListener(element,"mouseover",RS.input.enter,false);
		RS.events.addListener(element,"mouseout",RS.input.exit,false);
		RS.events.addListener(element,"dblclick",RS.input.doubleClick,false);
		RS.input.installedElements.push(element.id);
	}
};

RS.events.addListener(document,"load", function() {
	document.addEventListener("touchstart", RS.input.touchHandler, true);
	document.addEventListener("touchmove", RS.input.touchHandler, true);
	document.addEventListener("touchend", RS.input.touchHandler, true);
	document.addEventListener("touchcancel", RS.input.touchHandler, true);
});

RS.input.begin = function (event)
{
	var id = '';
	if (this.id) {
		id = this.id.substring(0,8);
	} else if ((event.srcElement) || (event.target)) {
		var element = event.srcElement || event.target;
		while (((element.id === '') || (RS.controls.indexOf(element.id) == -1)) && (element.offsetParent !== null)) {
			element = element.offsetParent;
		}
		id = element.id.substring(0,8);
	}
	if ((id === '') || (!event)) {
		return;
	}
	
	var control = RS.controls[id];
	if (!control) {
		return;
	}
	
	RS.input.forwardEvent(event, control);
};

RS.input.move = function (event)
{
	event = event || window.event;
	if (RS.input.target !== null) {
		RS.input.setLastEvent(RS.input.target.object(),event);
		if (RS.input.lastCoordinates.length > 0) {
			preventEventDefault(event);
		}
		return;
	} else if (RS.input.pointerTarget !== null) {
		var control = RS.input.pointerTarget;
		RS.input.setLastEvent(control.object(),event);
		if (RS.input.lastCoordinates.length > 0) {
			if (control.implementedEvents.indexOf('MouseMove') > -1) {
				RS.comm.triggerEvent(control.controlID,'MouseMove',RS.input.mouseEventData(event,RS.input.lastCoordinates),event);
			}
			var handled = control.mouseMove(event, RS.input.lastCoordinates);
			if (handled === true) {
				preventEventDefault(event);
			}
		}
		return;
	}
};

RS.input.end = function (event)
{
	if (!event) {
		event = RS.input.lastEvent;
	}
	if (RS.input.target === null) {
		return;
	}
	if (RS.input.holdInterval > 0) {
		clearInterval(RS.input.holdInterval);
		RS.input.holdInterval = 0;
	}
	
	if (RS.input.lastCoordinates.length > 0) {
		if (RS.input.target.implementedEvents.indexOf('MouseUp') > -1) {
			RS.comm.triggerEvent(RS.input.target.controlID,'MouseUp',RS.input.mouseEventData(event,RS.input.lastCoordinates),event);
		}
		var handled = RS.input.target.touchEnd(event, RS.input.lastCoordinates);
		if (handled === true) {
			try {
			preventEventDefault(event);
			} catch(e) { }
		}
	}
	RS.input.target = null;
	RS.input.lastCoordinates = [];
};



RS.input.enter = function (event)
{
	if (RS.input.pointerTarget !== null) {
		return;
	}
	var id = '';
	if (this.id) {
		id = this.id.substring(0,8);
	} else if ((event.srcElement) || (event.target)) {
		var element = event.srcElement || event.target;
		while (((element.id === '') || (RS.controls.indexOf(element.id) == -1)) && (element.offsetParent !== null)) {
			element = element.offsetParent;
		}
		id = element.id.substring(0,8);
	}
	if ((id === '') || (!event)) {
		return;
	}
	
	var control = RS.controls[id];
	if (!control) {
		return;
	}
	
	RS.input.pointerTarget = control;
	RS.input.setLastEvent(control.object(),event);
	if (RS.input.lastCoordinates.length > 0) {
		var el = control.object();
		var pos = getPosition(el);
		var mc = mouseCoords(event);
		if(!control.mouseWithin && mc.x >= pos.x && mc.x <= (pos.x + el.offsetWidth) && mc.y >= pos.y && mc.y <= (pos.y + el.offsetHeight)) {
			control.mouseWithin = true;
			if (control.implementedEvents.indexOf('MouseEnter') > -1) {
				RS.comm.triggerEvent(control.controlID,'MouseEnter',null,event);
			}
		}
		var handled = control.mouseEnter(event);
		if (handled === true) {
			preventEventDefault(event);
		}
	}
};

RS.input.exit = function (event)
{
	if (RS.input.pointerTarget === null) {
		return;
	}
	
	var control = RS.input.pointerTarget;
	
	RS.input.setLastEvent(control.object(),event);
	if (RS.input.lastCoordinates.length > 0) {
		var el = control.object();
		var pos = getPosition(el);
		var mc = mouseCoords(event);
		if(control.mouseWithin && (mc.x < pos.x || mc.x > (pos.x + el.offsetWidth) || mc.y < pos.y || mc.y > (pos.y + el.offsetHeight))) {
			control.mouseWithin = false;
			if (control.implementedEvents.indexOf('MouseExit') > -1 && RS.view.currentPage != control.controlID) {
				RS.comm.triggerEvent(control.controlID,'MouseExit',null,event);
			}
		}
		var handled = control.mouseExit(event);
		if (handled === true) {
			preventEventDefault(event);
		}
	}
	
	RS.input.pointerTarget = null;
};

RS.input.doubleClick = function (event)
{
	var id = '';
	if (this.id) {
		id = this.id.substring(0,8);
	} else if ((event.srcElement) || (event.target)) {
		var element = event.srcElement || event.target;
		while (((element.id === '') || (RS.controls.indexOf(element.id) == -1)) && (element.offsetParent !== null)) {
			element = element.offsetParent;
		}
		id = element.id.substring(0,8);
	}
	if ((id === '') || (!event)) {
		return;
	}
	
	var control = RS.controls[id];
	if (!control) {
		return;
	}
	
	RS.input.setLastEvent(control.object(),event);
	if (RS.input.lastCoordinates.length > 0) {
		if (control.implementedEvents.indexOf('DoubleClick') > -1) {
			RS.comm.triggerEvent(control.controlID,'DoubleClick',RS.input.mouseEventData(event,RS.input.lastCoordinates),event);
		}
		var handled = control.doubleClick(event);
		if (handled === true) {
			preventEventDefault(event);
		}
	}
};

RS.input.getCoordinates = function (target, event)
{
	var results = [];
	var result;
	var offset = {x: 0, y: 0};
	if (target) {
		offset = getPosition(target);
	}
	if (RS.input.isTouchUI() === true) {
		for (var i = 0; i < event.touches.length; i++) {
			result = {pageX: event.touches[i].pageX, pageY: event.touches[i].pageY};
			result.x = result.pageX - offset.x;
			result.y = result.pageY - offset.y;
			results.push(result);
		}
	} else {
		if (event.pageX) {
			result = {pageX: event.pageX, pageY: event.pageY};
		} else {
			result = {
				pageX: event.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft),
				pageY: event.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop)
			};
		}
		result.x = result.pageX - offset.x;
		result.y = result.pageY - offset.y;
		results.push(result);
	}
	return results;
};

RS.input.refireMove = function ()
{
	if (RS.input.target === null) {
		return;
	}
	if (RS.input.lastCoordinates.length > 0) {
		if (RS.input.target.implementedEvents.indexOf('MouseDrag') > -1) {
			RS.comm.triggerEvent(RS.input.target.controlID,'MouseDrag',RS.input.mouseEventData(event,RS.input.lastCoordinates),event);
		}
		RS.input.target.touchMove(null, RS.input.lastCoordinates);
	}
};

RS.input.forwardEvent = function (event, control)
{
	RS.input.target = control;
	RS.input.setLastEvent(control.object(),event);
	var r = false;
	if (RS.input.lastCoordinates.length > 0) {
		if (control.implementedEvents.indexOf('MouseDown') > -1) {
			RS.comm.triggerEvent(control.controlID,'MouseDown',RS.input.mouseEventData(event,RS.input.lastCoordinates),event);
		}
		var handled = control.touchBegin(event, RS.input.lastCoordinates);
		if (handled === true) {
			r = true;
			preventEventDefault(event);
		}
		if(control.mEnabled) {
			handled = control.touchMove(event, RS.input.lastCoordinates);
		}
		if (handled === true) {
			RS.input.holdInterval = setInterval(RS.input.refireMove,10);
		}
	}
	return r;
};

RS.input.mouseEventData = function (event, coordinates)
{
	var userdata = [];
	
	userdata.push(coordinates.length);
	for (var i = 0; i < coordinates.length; i++) {
		userdata.push(coordinates[i].pageX);
		userdata.push(coordinates[i].pageY);
	}
	userdata.push(event.button);
	
	return userdata;
};

RS.input.keyDown = function (event)
{
	event = event || window.event;
	var handled = false;
	var charCode = (event.charCode) ? event.charCode : ((event.which) ? event.which : 0);
	var keyName = (event.keyIdentifier) ? event.keyIdentifier : String.fromCharCode(charCode);
	var props = [event.keyCode,charCode,keyName,event.shiftKey,event.ctrlKey,event.altKey];
	if(event.metaKey) props[props.length] = event.metaKey;
	if (RS.input.focusControl !== null) {
		handled = RS.input.focusControl.keyDown(event);
		if(event.keyCode == 46 || event.keyCode == 8) {
			//Make sure Backspace and Delete keys fire KeyPressed event (WebKit)
			RS.input.handleKey(event,"KeyPressed");
		}
	}
};

RS.input.keyPress = function (event) {
	event = event || window.event;
	var charCode = (event.charCode) ? event.charCode : ((event.which) ? event.which : 0);
	if(!(event.keyCode == 8 || event.keyCode == 46)) { //Don't let keyPress fire for Delete or Backspace
		if(RS.input.handleKey(event,"KeyPressed")) {
			return true;
		}
	}
}

RS.input.keyUp = function (event)
{
	event = event || window.event;
	try {
		if (event.keyCode == 13) {
			//Don't fire a keyUp event for Return/Enter
			RS.events.stopPropagation(event);
			RS.events.preventDefault(event);
			return;
		}
	} catch(err) {}
	var charCode = (event.charCode) ? event.charCode : ((event.which) ? event.which : 0);
	var keyName = (event.keyIdentifier) ? event.keyIdentifier : String.fromCharCode(charCode);
	if(keyName.length !== 1 && keyName.indexOf("U+")==-1) {
		return RS.input.handleKey(event,"KeyPressed");
	}
};

RS.input.handleKey = function(event, eventName) {
	var handled = false;
	var charCode = (event.charCode) ? event.charCode : ((event.which) ? event.which : 0);
	var keyName = (RS.input.lastKey !== "" && RS.input.lastKey !== undefined) ? RS.input.lastKey : ((event.keyIdentifier) ? event.keyIdentifier : String.fromCharCode(charCode));
	var keyCode = (event.keyCode > 0) ? event.keyCode : charCode;
	if (event.modifiers) {
		event.shiftKey = (event.shiftKey || (event.modifiers && 4));
		event.altKey = (event.altKey || (event.modifiers && 1));
		event.ctrlKey = (event.ctrlKey || (event.modifiers && 2));
	}
	var props = [keyCode,charCode,keyName,event.shiftKey,event.ctrlKey,event.altKey];
	if(event.metaKey) props[props.length] = event.metaKey;
	if (RS.input.focusControl !== null) {
		handled = RS.input.focusControl.keyUp(event);
		if ((handled !== true) && (RS.input.focusControl.implementedEvents.indexOf('KeyPressed') > -1)) {
			setTimeout(function() { RS.comm.triggerEvent(RS.input.focusControl.controlID,'KeyPressed',props,event); },0);
			handled = true;
		}
	}
	if ((handled !== true) && (RS.controls[RS.view.currentPage].implementedEvents.indexOf('KeyPressed') > -1)) {
		setTimeout(function() { RS.comm.triggerEvent(RS.view.currentPage,'KeyPressed',props,event); },0);
		handled = true;
	}
	return handled;
}

RS.input.setFocusControl = function (control)
{
	if(RS.input.focusControl !== null) {
		if(RS.input.focusControl.implementedEvents.indexOf('LostFocus') > -1) {
			RS.comm.triggerEvent(RS.input.focusControl.controlID,'LostFocus',"",null);
		}
		if(typeof RS.input.focusControl.lostFocus === "function") {
			RS.input.focusControl.lostFocus();
		}
	}
	RS.input.focusControl = control;
	if(RS.input.focusControl !== null) {
		if(RS.input.focusControl.implementedEvents.indexOf('GotFocus') > -1) {
			RS.comm.triggerEvent(RS.input.focusControl.controlID,'GotFocus',"",null);
		}
		if(typeof RS.input.focusControl.gotFocus === "function") {
			RS.input.focusControl.gotFocus();
		}
	}
};

RS.input.setLastEvent = function (relObject,event) {
	if(event) {
		RS.input.lastCoordinates = RS.input.getCoordinates(relObject,event);
		RS.input.lastEvent = event;
	}
};

RS.input.touchHandler = function (event) {
    var touches = event.changedTouches,
        first = touches[0],
        type = "";
         switch(event.type)
    {
        case "touchstart": type = "mousedown"; break;
        case "touchmove":  type="mousemove"; break;        
        case "touchend":   type="mouseup"; break;
        default: return;
    }

             //initMouseEvent(type, canBubble, cancelable, view, clickCount, 
    //           screenX, screenY, clientX, clientY, ctrlKey, 
    //           altKey, shiftKey, metaKey, button, relatedTarget);
    
    var simulatedEvent = document.createEvent("MouseEvent");
    simulatedEvent.initMouseEvent(type, true, true, window, 1, 
                              first.screenX, first.screenY, 
                              first.clientX, first.clientY, false, 
                              false, false, false, 0/*left*/, null);
    if(first.target.id==document.body.id) {
		first.target.dispatchEvent(simulatedEvent);
		event.preventDefault();
    }
};

RS.input.contextClick = function (event) {
	event = event || window.event;
	if (!event) {
		return;
	}
	
	var id = '';
	if (this.id) {
		id = this.id.substring(0,8);
	} else if ((event.srcElement) || (event.target)) {
		var element = event.srcElement || event.target;
		while (((element.id === '') || (RS.controls.indexOf(element.id) == -1)) && (element.offsetParent !== null)) {
			element = element.offsetParent;
		}
		id = element.id.substring(0,8);
	}
	if ((id === '') || (!event)) {
		return;
	}
	
	var control = RS.controls[id];
	if (!control) {
		return;
	}
	
	var coordinates = RS.input.getCoordinates(null,event);
	var menuID = control.menuID;
	if (menuID === '') {
		menuID = RS.controls[RS.view.currentPage].menuID;
	}
	if (menuID !== '') {
		RS.menus.dismissAllMenus();
		RS.menus.displayMenuForPoint(menuID,coordinates[0].pageX,coordinates[0].pageY,-1);
		RS.menus.menuTarget = control;
		RS.menus.action = 'context';
	}
	RS.events.preventDefault(event);
	RS.events.stopPropagation(event);
	return false;
};

// Debug Console

RS.console.error = function (msg) {
	var form = document.getElementById('REALAlerterDialog').children[0];
	if (form) {
		form.errorbody.value = msg;
		RS.console.present();
		form.userdetails.focus();
	}
};

RS.console.log = function (msg) {
	if ((typeof console) != 'undefined') {
		console.log(msg);
	}
};

RS.console.isVisible = function () {
	return RS.console.mIsVisible;
};

RS.console.present = function () {
	var cn = document.getElementById('REALAlerter');
	if (cn) {
		if (RS.console.mIsVisible === false) {
			RS.console.mIsVisible = true;
			RS.view.showOverlay();
			
			cn.style.display = 'block';
			setTimeout("document.getElementById('REALAlerter').style.opacity = '1'",1);
		}
	}
};

RS.console.dismiss = function () {
	var cn = document.getElementById('REALAlerter');
	if (cn) {
		if (RS.console.mIsVisible === true) {
			RS.console.mIsVisible = false;
			
			cn.style.opacity = '0';
			setTimeout("document.getElementById('REALAlerter').style.display = 'none'",260);
			setTimeout(RS.view.hideOverlay,260);
		}
	}
};

RS.console.submit = function () {
	var form = document.getElementById('REALAlerterDialog').children[0];
	if (form) {
		form.submit();
	}
};

// Events
RS.events.listeners = [];
RS.events.listenersDisabled = [];

//Define the listenerClass
RS.events.listener = function(el, eventName, callback, capture) {
	this.el = el;
	this.event = eventName;
	this.callback = callback;
	this.capture = capture;
};

RS.events.addListener = function (el, eventName, fn, capture) {
	if(window.addEventListener) {
		el.addEventListener(eventName, fn, capture);
	} else {
		if(el.attachEvent) {
			el.attachEvent("on"+eventName, fn);
		} else {
			el["on"+eventName] = fn;
		}
	}
	var lstn = new RS.events.listener(el,eventName,fn,capture);
	RS.events.listeners.push(lstn);
};

RS.events.removeListener = function (el, eventName, fn, capture) {
	if(window.removeEventListener) {
		el.removeEventListener(eventName, fn, capture);
	} else {
		if(el.detachEvent) {
			el.detachEvent("on"+eventName, fn);
		} else {
			el["on"+eventName] = null;
		}
	}
	for(i=0;i<RS.events.listeners.length;i++) {
		var lstn = RS.events.listeners[i];
		if((lstn.el.nodeName == el.nodeName || lstn.el.id == el.id) && lstn.event == eventName && lstn.callback == fn && lstn.capture == capture) {
			RS.events.listeners.splice(i,1);
		}
	}
};

RS.events.preventDefault = function (event) {
	event.returnValue = false;
	if (event.preventDefault) {
		event.preventDefault();
	}
};

RS.events.stopPropagation = function (event) {
	event.cancelBubble = true;
	if (event.stopPropagation) {
		event.stopPropagation();
	}
};

RS.events.fireEvent = function(el, eventname, props) {
	props = props || null;
	if(document.createEvent) {
		var evt = document.createEvent("Event");
		evt.initEvent(eventname,true,true);
		for(var prop in props) {
		    if(props.hasOwnProperty(prop))
		    	evt[prop] = props[prop];
		}
		return el.dispatchEvent(evt);
	} else { //Do it the IE way
		var ieEvt = document.createEventObject();
		ieEvt.cancelBubble = false;
		ieEvt.returnValue = true;
		for(var prop in props) {
		    if(props.hasOwnProperty(prop))
		    	ieEvt[prop] = props[prop];
		}
		var ieEventName = "on"+eventname;
		try {
			el.fireEvent(ieEventName,ieEvt);
		} catch(err) {
			//If IE<9 and it is a custom event, call it directly
			for(i=0;i<RS.events.listeners.length;i++) {
				var lstn = RS.events.listeners[i];
				if((lstn.el.nodeName == el.nodeName || lstn.el.id == el.id) && lstn.event == eventname) {
					lstn.callback(ieEvt);
				}
			}
		}
	}
};

//DOM
RS.DOM.addClass = function(el,className) {
	var classNames = RS.DOM.getArrayOfClassNames(el);
	if(classNames.indexOf(className)===-1) {
		classNames.push(className);
		el.className = classNames.join(' ');
	}
};

RS.DOM.removeClass = function(el,className) {
	var classNames = RS.DOM.getArrayOfClassNames(el);
	var newNames = [];
	for(var i = 0; i < classNames.length; i++) {
		if(className != classNames[i]) {
			newNames.push(classNames[i]);
		}
	}
	el.className = newNames.join(' ');
};

RS.DOM.hasClass = function(el,className) {
	var classNames = RS.DOM.getArrayOfClassNames(el);
	for(var i = 0;i<classNames.length;i++) {
		if(className == classNames[i]) {
			return true;
		}
	}
	return false;
};

RS.DOM.getAppliedStyle = function(el,styleName) {
	var style = "";
	if(window.getComputedStyle) {
		style = el.ownerDocument.defaultView.getComputedStyle(el,null).getPropertyValue(RS.utils.toHyphens(styleName));
	} else if(el.currentStyle) {
		style = el.currentStyle(RS.utils.toCamelCase(styleName));
	}
	return style;
};

RS.DOM.getArrayOfClassNames = function(el) {
	var classNames = [];
	if(el.className) {
		classNames = el.className.split(' ');
	}
	return classNames;
};

RS.DOM.get = function(el) {
	if(typeof el === "string") {
		el = document.getElementById(el);
	}
	return el;
};

RS.DOM.getElementsByClassName = function(className, tag, root) {
	tag = tag || "*";
	root = (root) ? RS.DOM.get(root) : null || document;
	if(!root) {
		return [];
	}
	var nodes = [];
	var elements = root.getElementsByTagName(tag);
	var hasClass = RS.DOM.hasClass;
	for(var i = 0, len = elements.length; i < len; ++i) {
		if(hasClass(elements[i], className)) {
			nodes[nodes.length] = elements[i];
		}
	}
	return nodes;
};

// Utils
RS.utils.toCamelCase = function(hyphenatedValue) {
	var result = hyphenatedValue.replace(/-\D/g, function(character) { return character.charAt(1).toUpperCase(); });
	return result;
};

RS.utils.toHyphens = function(camelCaseValue) {
	var result = camelCaseValue.replace(/[A-Z]/g, function(character) { return ('-' + character.charAt(0).toLowerCase());});
	return result;
};

RS.utils.tabCapture = function() {
	if(event.keyCode==9) {
		RS.events.preventDefault(event);
		return false;
	}
	return true;
};

// Framework Base Class

function frameworkSubclass (subClass, baseClass)
{	
	function inheritance() {}
	inheritance.prototype = baseClass.prototype;
	
	subClass.prototype = new inheritance();
	subClass.prototype.constructor = subClass;
	subClass.baseConstructor = baseClass;
	subClass.superClass = baseClass.prototype;
}

function frameworkObject (target, events)
{
	if (!events) {
		events = [];
	}
	
	this.controlID = target;
	this.implementedEvents = events;
	
	var installEvents = ['MouseDown','MouseUp','MouseDrag','MouseEnter','MouseExit','MouseMove','DoubleClick'];
	for (var i = 0; i < events.length; i++) {
		if (installEvents.indexOf(events[i]) > -1) {
			RS.input.install(this.object());
			break;
		}
	}
	
	RS.controls.push(this.controlID);
	RS.controls[this.controlID] = this;
}

frameworkObject.prototype = {
	controlID: "",
	mObject: null,
	mMouseOffsetX: 0,
	mMouseOffsetY: 0,
	mMouseDown: false,
	mEnabled: false,
	mBaseStyle: "",
	mOpacity: 1.0,
	shouldBecomeVisible: true,
	hasFinishedLoading: false,
	implementedEvents: [],
	menuID: "",
	name: function () {
		return this.controlID;
	},
	object: function () {
		var locate = false;
		try {
			if (this.mObject === null) {
				locate = true;
			} else if (this.mObject.offsetParent === null) {
				locate = true;
			}
		} catch (err) {
			locate = true;
		}
		if (locate === true) {
			this.mObject = document.getElementById(this.controlID);
		}
		return this.mObject;
	},
	parent: function () {
		return this.object().offsetParent;
	},
	value: function () {
		return null;
	},
	mouseDown: function () {
	},
	mouseDrag: function () {
	},
	mouseUp: function () {
	},
	mouseWheel: function () {
	},
	resize: function () {
		this.refresh();
	},
	resizeComplete: function () {
	},
	destroy: function () {
        var parent = this.object().offsetParent;
        if (parent) {
            parent.removeChild(this.object());
        }
		RS.controls.splice(RS.controls.indexOf(this.controlID),1);
	},
	enabled: function () {
		return this.mEnabled;
	},
	setEnabled: function (value) {
		this.setDisabledAppearance(! value);
		this.mEnabled = value;
	},
	setDisabledAppearance: function (value) {
		if(value == this.mEnabled) {
			var el = this.object();
			if (! value) {
				RS.DOM.removeClass(el,"disabled");
			} else {
				RS.DOM.addClass(el,"disabled");
			}
			if(typeof this.setAppearance == 'function') {
				this.setAppearance(!value);
			}
		}
	},
	visible: function ()
	{
		if (this.object().style.visibility == 'hidden') {
			return false;
		} else {
			return true;
		}
	},
	setVisible: function (value)
	{
		if (value) {
			this.object().style.display = 'block';
		} else {
			this.object().style.display = 'none';
		}
	},
	setStyle: function (value)
	{
		var className;
		if (value === null) {
			className = "";
		} else {
			className = value;
		}
		if (this.mBaseStyle !== "") {
			if (className !== "") {
				className = this.mBaseStyle + " " + className;
			} else {
				className = this.mBaseStyle;
			}
		}
		this.object().className = className;
	},
	refresh: function ()
	{
		if (RS.view.refresh.locked() === true) {
			if (RS.view.refresh.targets.indexOf(this) == -1) {
				RS.view.refresh.targets.push(this);
			}
		} else {
			this.willRefresh();
		}
	},
	willRefresh: function ()
	{
		// subclasses should override
	},
	focus: function ()
	{
		// subclasses should override
	},
	didFinishLoading: function ()
	{
		var obj = this.object();
		if (!obj) {
			return;
		}
		var style = obj.style;
		if (!style) {
			return;
		}
		if (this.shouldBecomeVisible === false) {
			style.display = 'none';
		}
		style.visibility = 'visible';
		this.finishedLoading();
	},
	finishedLoading: function ()
	{
		// subclasses should override
	},
	opacity: function ()
	{
		if (this.object().style.opacity) {
			return parseFloat(this.object().style.opacity);
		} else {
			return this.mOpacity;
		}
	},
	setOpacity: function (value)
	{
		this.mOpacity = value;
		
		var obj = this.object();
		obj.style.opacity = value.toString();
		try {
			obj.filters.alpha.opacity = value*100;
		} catch(err) {
			obj.style.filter = "alpha(opacity=" + (value*100) + ")";
		}
	},
	touchBegin: function (event, coordinates)
	{
	},
	touchMove: function (event, coordinates)
	{
	},
	touchEnd: function (event, coordinates)
	{
	},
	mouseEnter: function (event)
	{
	},
	mouseMove: function (event, coordinates)
	{
	},
	mouseExit: function (event)
	{
	},
	doubleClick: function (event, coordinates)
	{
	},
	keyDown: function (event)
	{
	},
	keyUp: function (event)
	{
	},
	presentContextualMenu: function (event) {
		if (this.menuID !== "") {
			var rect = this.bounds();
			
			RS.menus.dismissAllMenus();
			if (event) {
				var coordinates = RS.input.getCoordinates(null,event);
				RS.menus.displayMenuForPoint(this.menuID,coordinates[0].pageX,coordinates[0].pageY,-1);
			} else {
				RS.menus.displayMenuForRect(this.menuID,rect,null,null);
			}
			RS.menus.menuTarget = this;
			RS.menus.action = 'context';
		}
	},
	bounds: function () {
		var pos = getPosition(this.object());
		return new Rect(pos.x,pos.y,this.object().offsetWidth,this.object().offsetHeight);
	},
	menuItemSelected: function (menuID) {
		RS.comm.triggerEvent(this.controlID,'MenuSelected',[menuID]);
	}
};

// Rect

function Rect (left, top, width, height) {
	this.left = left;
	this.top = top;
	this.right = left + width;
	this.bottom = top + height;
}

Rect.prototype = {
	left: 0,
	top: 0,
	right: 0,
	bottom: 0,
	width: function () {
		return this.right - this.left;
	},
	height: function () {
		return this.bottom - this.top;
	},
	area: function () {
		return this.width() * this.height();
	},
	union: function (source) {
		var result = new Rect(0,0,0,0);
		result.left = Math.min(this.left,source.left);
		result.top = Math.min(this.top,source.top);
		result.right = Math.max(this.right,source.right);
		result.bottom = Math.max(this.bottom,source.bottom);
		return result;
	},
	containsPoint: function (x,y) {
		if ((x >= this.left) && (x <= this.right) && (y >= this.top) && (y <= this.bottom)) {
			return true;
		} else {
			return false;
		}
	},
	offset: function (left, top) {
		this.left = this.left + left;
		this.top = this.top + top;
	},
	verticalCenter: function () {
		return this.top + Math.floor(this.height() / 2);
	},
	horizontalCenter: function () {
		return this.left + Math.floor(this.width() / 2);
	}
};

function loadScript(id,url,callback,location,source) {
	if(location==undefined || location=="") location = "head";
	var targetEl = location;
	if(typeof location == "string") {
		targetEl = document.getElementsByTagName(location)[0];
		if(!targetEl) {
			targetEl = document.getElementById(location);
		}
	}
	if(targetEl) {
		var el = document.getElementById(id);
		if(!el) {
			var script = document.createElement("script")
			script.id = id;
			script.type = "text/javascript";
			if(url!=undefined && url!="") {
				if(script.readyState) {
					script.onreadystatechange = function(){
						if(script.readyState == "loaded" || script.readyState == "complete") {
							script.onreadystatechange = null;
							if(callback!=null) {
								callback();
							}
						}
					};
				} else {
					script.onload = function(ev) {
						if(callback!=null) {
							callback();
						}
					};
				}
				script.src = url;
			} else {
				if(source != undefined) script.innerHTML = source;
				if(callback!=null) {
					callback();
				}
			}
			targetEl.appendChild(script);
		} else {
			callback();
		}
	}
}

/*
Copyright © 2011 Yahoo! Inc. All rights reserved.
Redistribution and use of this software 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 Yahoo! Inc. nor the names of YUI's contributors may be used to endorse or promote products derived from this software without specific prior written permission of Yahoo! Inc.
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.
*/
// User agent sniffer
RS.ua = function() {
	var numberfy = function(s) {
		var c = 0;
		return parseFloat(s.replace(/\./g, function() {
			return (c++ == 1) ? '' : '.';
		}));
	},
	nav = navigator,
	o = {
		ie: 0,
		opera: 0,
		gecko: 0,
		webkit: 0,
		mobile: null,
		air: 0,
		caja: nav.cajaVersion,
		secure: false,
		os: null
	},	    
	ua = navigator && navigator.userAgent, 
	loc = window && window.location,
	href = loc && loc.href,
	m;
	o.secure = href && (href.toLowerCase().indexOf("https") === 0);
    if (ua) {
        if ((/windows|win32/i).test(ua)) {
            o.os = 'windows';
        } else if ((/macintosh/i).test(ua)) {
            o.os = 'macintosh';
        } else if ((/linux/i).test(ua)) {
        	o.os = 'linux';
        }
    
        // Modern KHTML browsers should qualify as Safari X-Grade
        if ((/KHTML/).test(ua)) {
            o.webkit=1;
        }

        // Modern WebKit browsers are at least X-Grade
        m=ua.match(/AppleWebKit\/([^\s]*)/);
        if (m&&m[1]) {
            o.webkit=numberfy(m[1]);

            // Mobile browser check
            if (/ Mobile\//.test(ua)) {
                o.mobile = "iOS"; // iPhone or iPod Touch
            } else if(/Android/.test(ua)) {
            	o.mobile = "Android";
            } else {
                m=ua.match(/NokiaN[^\/]*/);
                if (m) {
                    o.mobile = m[0]; // Nokia N-series, ex: NokiaN95
                }
            }

            m=ua.match(/AdobeAIR\/([^\s]*)/);
            if (m) {
                o.air = m[0]; // Adobe AIR 1.0 or better
            }

        }

        if (!o.webkit) { // not webkit
            // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
            m=ua.match(/Opera[\s\/]([^\s]*)/);
            if (m&&m[1]) {
                o.opera=numberfy(m[1]);
                m=ua.match(/Opera Mini[^;]*/);
                if (m) {
                    o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
                }
            } else { // not opera or webkit
                m=ua.match(/MSIE\s([^;]*)/);
                if (m&&m[1]) {
                    o.ie=numberfy(m[1]);
                } else { // not opera, webkit, or ie
                    m=ua.match(/Gecko\/([^\s]*)/);
                    if (m) {
                        o.gecko=1; // Gecko detected, look for revision
                        m=ua.match(/rv:([^\s\)]*)/);
                        if (m&&m[1]) {
                            o.gecko=numberfy(m[1]);
                        }
                    }
                }
            }
        }
    }

    return o;
}();

/* END: GLOBAL */

/* BEGIN: IMAGEVIEW */

// imageview/framework.js
//
// Provides sync abilities to WebImage class
//
// © 2010 REAL Software Inc. -- All Rights Reserved
// This code contains patent-pending technology.

function imageview (target, events)
{
	imageview.baseConstructor.call(this,target,events);
	
	this.setSource(this.image().src);
	RS.view.sizing.targets.push(this);
}
frameworkSubclass(imageview, frameworkObject);
imageview.prototype.mSource = null;
imageview.prototype.mVerticalAlignment = 0;
imageview.prototype.mHorizontalAlignment = 0;

imageview.prototype.setSource = function (url)
{
	var img = cacheImage(url,false);
	img.loaded = false;
	if (img) {
		img.targetObject = this;
		img.onload = function () {
			img.targetObject.mSource = img;
			img.targetObject.refresh();
			img.onload = null;
		};
	}
};

imageview.prototype.image = function ()
{
	return this.object().children.item(0);
};

imageview.prototype.willRefresh = function ()
{
	if (!this.mSource) {
		return;
	}
	
	var image = this.image();
	image.src = this.mSource.src;
	image.width = this.mSource.width;
	image.height = this.mSource.height;
	image.style.width = this.mSource.width + 'px';
	image.style.height = this.mSource.height + 'px';
	
	switch (this.mVerticalAlignment) {
		case 0:
		case 2:
			// middle
			image.style.top = '50%';
			image.style.bottom = '';
			image.style.marginTop = '-' + (this.mSource.height / 2) + 'px';
			break;
		case 1:
			// top
			image.style.top = '0px';
			image.style.bottom = '';
			image.style.marginTop = '0px';
			break;
		case 3:
			// bottom
			image.style.top = '';
			image.style.bottom = '0px';
			image.style.marginTop = '0px';
			break;
	}
	
	switch (this.mHorizontalAlignment) {
		case 0:
		case 2:
			// center
			image.style.left = '50%';
			image.style.right = '';
			image.style.marginLeft = '-' + (this.mSource.width / 2) + 'px';
			break;
		case 1:
			// left
			image.style.left = '0px';
			image.style.right = '';
			image.style.marginLeft = '0px';
			break;
		case 3:
			// right
			image.style.left = '';
			image.style.right = '0px';
			image.style.marginLeft = '0px';
			break;
	}
};

imageview.prototype.setVerticalAlignment = function (value)
{
	this.mVerticalAlignment = value;
	this.refresh();
};

imageview.prototype.setHorizontalAlignment = function (value)
{
	this.mHorizontalAlignment = value;
	this.refresh();
};

imageview.prototype.touchBegin = function (event, coordinates)
{
	return true;
};

imageview.prototype.touchMove = function (event, coordinates)
{
	return true;
};

imageview.prototype.touchEnd = function (event, coordinates)
{
	return true;
};

/* END: IMAGEVIEW */

/* BEGIN: POPUPMENU */

// PopupMenu/framework.js
//
// Utility class for working with popupmenus
//
// © 2010 REAL Software Inc. -- All Rights Reserved
// This code contains patent-pending technology.

function popupmenu (target, events)
{
	popupmenu.baseConstructor.call(this,target,events);
	addPostLoadObject(this);
	
	var cid = this.controlID;
	RS.events.addListener(this.field(),"change",function () { RS.controls[cid].action() },false);
	RS.events.addListener(this.field(),"focus",function () { RS.input.setFocusControl(RS.controls[cid]) },false);
	RS.events.addListener(this.field(),"blur",function () { RS.input.setFocusControl(null) },false);
}
frameworkSubclass(popupmenu, frameworkObject);
popupmenu.prototype.selectedIndex = -1;

popupmenu.prototype.action = function ()
{
	this.selectedIndex = this.field().selectedIndex;
	markControlChanged(this);
	if (this.implementedEvents.indexOf('SelectionChanged') > -1) {
		RS.comm.triggerEvent(this.controlID,'SelectionChanged');
	}
};

popupmenu.prototype.field = function ()
{
	return this.object().children[0];
};

popupmenu.prototype.willRefresh = function ()
{
	this.field().selectedIndex = this.selectedIndex;
};

popupmenu.prototype.appendRow = function (row)
{
	var field = this.field();
	field.length = field.length + 1;
	field.options[field.length - 1] = new Option(row,field.length - 1);
};

popupmenu.prototype.insertRow = function (index,row)
{
	var field = this.field();
	field.length = field.length + 1;
	
	var i;
	for (i = field.length - 1; i > index; i--) {
		field.options[i].text = field.options[i - 1].text;
		field.options[i].value = i;
	}
	
	field.options[index] = new Option(row,index);
	
	if (field.selectedIndex >= index) {
		this.selectedIndex = this.selectedIndex + 1;
		field.selectedIndex = this.selectedIndex;
	}
};

popupmenu.prototype.removeRow = function (index)
{
	var field = this.field();
	if ((index < 0) || (index >= field.length)) {
		return;
	}
	
	if (field.length == 1) {
		field.length = 0;
		return;
	}
	
	if (index == field.length - 1) {
		field.length = Math.max(field.length - 1,0);
		return;
	}
	
	var i;
	for (i = index; i < field.length - 1; i++) {
		field.options[i].text = field.options[i + 1].text;
		field.options[i].value = i;
	}
	
	if (field.selectedIndex > index) {
		this.selectedIndex = this.selectedIndex - 1;
		field.selectedIndex = this.selectedIndex;
	} else if (field.selectedIndex = index) {
		this.selectedIndex = -1;
		field.selectedIndex = -1;
	}
	
	field.length = Math.max(field.length - 1,0);
};

popupmenu.prototype.changeRow = function (index,row)
{
	this.field().options[index].text = row;
};

popupmenu.prototype.deleteAllRows = function ()
{
	this.field().length = 0;
	this.selectedIndex = -1;
};

popupmenu.prototype.value = function ()
{
	return this.selectedIndex;
};

popupmenu.prototype.setValue = function (input)
{
	var i;
    var field = this.field();
    var options = field.options;
	
	input = parseInt(input,10);
	this.selectedIndex = input;
    
    for (i = 0; i < options.length; i++) {
        options[i].defaultSelected = (i == input);
    }
    field.selectedIndex = input;
};

popupmenu.prototype.setEnabled = function (input)
{
	if (input === true) {
		this.field().disabled = false;
	} else {
		this.field().disabled = true;
	}
	this.mEnabled = input;
};

popupmenu.prototype.length = function ()
{
	return this.field().length;
};

popupmenu.prototype.setLength = function (input)
{
	this.field().length = input;
};

popupmenu.prototype.setStyle = function (value)
{
	var className;
	if (value === null) {
		className = "";
	} else {
		className = value;
	}
	if (this.mBaseStyle !== "") {
		if (className !== "") {
			className = this.mBaseStyle + " " + className;
		} else {
			className = this.mBaseStyle;
		}
	}
	this.field().className = className;
};

popupmenu.prototype.postLoad = function ()
{
	this.refresh();
};

popupmenu.prototype.keyDown = function (event)
{
	return true;
};

popupmenu.prototype.keyUp = function (event)
{
	return true;
};

/* END: POPUPMENU */

/* BEGIN: BUTTON */

// Button/framework.js
//
// Provides sync abilities to WebButton class
//
// © 2010 REAL Software Inc. -- All Rights Reserved
// This code contains patent-pending technology.

function button (target, events)
{
	button.baseConstructor.call(this,target,events);
	
	var cid = this.controlID;
	if (events.indexOf('Action') > -1) {
		RS.events.addListener(this.field(),"click",function () { RS.controls[cid].action() },false);
	}
	RS.events.addListener(this.field(),"focus",function () { RS.input.setFocusControl(RS.controls[cid]) },false);
	RS.events.addListener(this.field(),"blur",function () { RS.input.setFocusControl(null) },false);
}
frameworkSubclass(button, frameworkObject);

button.prototype.action = function ()
{
	RS.comm.triggerEvent(this.controlID,'Action');
}

button.prototype.field = function ()
{
	return this.object().children[0];
};

button.prototype.value = function ()
{
	return this.field().value;
};

button.prototype.setValue = function (input)
{
	this.field().value = input;
};

button.prototype.setEnabled = function (value)
{
	if (value === true) {
		this.field().disabled = false;
	} else {
		this.field().disabled = true;
	}
	this.mEnabled = value;
};

button.prototype.setStyle = function (value)
{
	var className;
	if (value === null) {
		className = "";
	} else {
		className = value;
	}
	if (this.mBaseStyle !== "") {
		if (className !== "") {
			className = this.mBaseStyle + " " + className;
		} else {
			className = this.mBaseStyle;
		}
	}
	this.field().className = className;
};

button.prototype.keyDown = function (event)
{
	return true;
};

button.prototype.keyUp = function (event)
{
	return true;
};

/* END: BUTTON */

/* BEGIN: TEXTLABEL */

// TextLabel/framework.js
//
// Utility class for working with TextLabels
//
// © 2010 REAL Software Inc. -- All Rights Reserved
// This code contains patent-pending technology.

function textlabel (target,events)
{
	textlabel.baseConstructor.call(this,target,events);
	var insides = this.object().innerHTML;
	this.object().innerHTML = '<div style="position: absolute; left: 0px; right: 0px; top: 50%; overflow: hidden;">M</div>';
	this.mLineHeight = this.innerCell().offsetHeight;
	this.innerCell().innerHTML = insides;
	this.refresh();
}
frameworkSubclass(textlabel, frameworkObject);
textlabel.prototype.mMultiline = false;
textlabel.prototype.mLineHeight = 0;

textlabel.prototype.value = function ()
{
	return this.innerCell().innerHTML;
};

textlabel.prototype.setAppearance = function(enabled) {
	try {
		var el = this.innerCell().children[0];
		if(enabled) {
			if(this.storedhref) el.setAttribute('href',this.storedhref);
			el.style.textDecoration = "";
		} else {
			this.storedhref = el.getAttribute('href');
			el.removeAttribute('href');
			if(this.storedhref != undefined && this.storedhref != "") {
				el.style.textDecoration = "underline";
			}
		}
	} catch(err) {	}
}

textlabel.prototype.setValue = function (input)
{
	this.innerCell().innerHTML = input;
	this.refresh();
};

textlabel.prototype.innerCell = function ()
{
	var obj = this.object().children[0];
	return obj;
};

textlabel.prototype.setMultiline = function (value)
{
	if (value != this.mMultiline) {
		this.mMultiline = value;
		this.refresh();
	}
};

textlabel.prototype.willRefresh = function ()
{
	var cell = this.innerCell();
	if (this.mMultiline) {
		cell.style.top = '0px';
		cell.style.bottom = '0px';
		cell.style.marginTop = '';
		cell.style.height = '';
		cell.style.maxHeight = '';
	} else {
		cell.style.top = '50%';
		cell.style.bottom = '';
		cell.style.marginTop = '-' + Math.floor(this.mLineHeight / 2) + 'px';
		cell.style.height = this.mLineHeight + 'px';
		cell.style.maxHeight = cell.style.height;
	}
};

/* END: TEXTLABEL */

/* BEGIN: SCROLLBAR */

// ScrollBar/framework.js
//
// Utility class for working with scrollbars
//
// © 2010 REAL Software Inc. -- All Rights Reserved
// This code contains patent-pending technology.

function scrollbar (target, events)
{
	scrollbar.baseConstructor.call(this,target,events);
	
    var obj = this.object();
	obj.innerHTML = '<div class="decrease"></div><div class="increase"></div><div class="track"><div class="thumb"></div></div>';
	
	switch (this.object().className) {
	case 'scrollbar vertical':
		this.mVertical = true;
		this.baseStyle = this.object().className;
		break;
	case 'scrollbar horizontal':
		this.mVertical = false;
		this.baseStyle = this.object().className;
		break;
	default:
		this.mVertical = this.object().offsetHeight > this.object().offsetWidth;
		if (this.mVertical) {
			this.baseStyle = 'scrollbar vertical';
		} else {
			this.baseStyle = 'scrollbar horizontal';
		}
		break;
	}
    
	RS.input.install(this.object());
	RS.view.sizing.targets.push(this);
	addWheelTarget(this);
}

frameworkSubclass(scrollbar, frameworkObject);
scrollbar.prototype.mMinimum = 0;
scrollbar.prototype.mMaximum = 0;
scrollbar.prototype.mValue = 0;
scrollbar.prototype.mPageStep = 20;
scrollbar.prototype.mLineStep = 1;
scrollbar.prototype.mMouseDownX = 0;
scrollbar.prototype.mMouseDownY = 0;
scrollbar.prototype.mVertical = true;
scrollbar.prototype.mInsideThumb = false;
scrollbar.prototype.mInsideTrack = false;
scrollbar.prototype.mHoldWidget = null;
scrollbar.prototype.mAnimator = 0;
scrollbar.prototype.valueChanged = '';
scrollbar.prototype.animator = 0;
scrollbar.prototype.animationElapsed = 0;
scrollbar.prototype.animationPeriod = Math.floor(1000 / 60);
scrollbar.prototype.animationStartValue = 0;
scrollbar.prototype.animationEndValue = 0;
scrollbar.prototype.animationDuration = 500;

scrollbar.prototype.touchBegin = function (event, coordinates)
{
	if(this.mEnabled) {
		if (this.mMaximum <= this.mMinimum) {
			return true;
		}
		var x = coordinates[0].x;
		var y = coordinates[0].y;
		
		var track = findChildrenByClass(this.object(),'track');
		track = track[0];
		var thumb = findChildrenByClass(track,'thumb');
		thumb = thumb[0];
		
		if (this.mAnimator !== 0) {
			clearInterval(this.mAnimator);
			this.mAnimator = 0;
		}
		
		if (this.mVertical) {
			this.mInsideTrack = ((y >= track.offsetTop) && (y <= track.offsetTop + track.offsetHeight));
			if (this.mInsideTrack) {
				y = y - track.offsetTop;
				this.mInsideThumb = ((y >= thumb.offsetTop) && (y <= thumb.offsetTop + thumb.offsetHeight));
				if (this.mInsideThumb) {
					y = y - thumb.offsetTop;
				}
			} else {
				this.mInsideThumb = false;
			}
		} else {
			this.mInsideTrack = ((x >= track.offsetLeft) && (x <= track.offsetLeft + track.offsetWidth));
			if (this.mInsideTrack) {
				x = x - track.offsetLeft;
				this.mInsideThumb = ((x >= thumb.offsetLeft) && (x <= thumb.offsetLeft + thumb.offsetWidth));
				if (this.mInsideThumb) {
					x = x - thumb.offsetLeft;
				}
			} else {
				this.mInsideThumb = false;
			}
		}
		
		this.mMouseDownX = x;
		this.mMouseDownY = y;
		if ((this.mInsideTrack) && (!this.mInsideThumb)) {
			this.jumpTo(x,y);
		} else {
			return this.touchMove(event, coordinates);
		}
		
		return true;
	}
};

scrollbar.prototype.touchMove = function (event, coordinates)
{
	if(this.mEnabled) {
		if (this.mMaximum <= this.mMinimum) {
			return true;
		}
		var x = coordinates[0].x;
		var y = coordinates[0].y;
		
		var track = findChildrenByClass(this.object(),'track');
		track = track[0];
		var thumb = findChildrenByClass(track,'thumb');
		thumb = thumb[0];
		
		var pixelRange;
		if (this.mVertical) {
			pixelRange = track.offsetHeight - thumb.offsetHeight;
		} else {
			pixelRange = track.offsetWidth - thumb.offsetWidth;
		}
		
		var valueRange = this.maximum() - this.minimum();
		var pct;
		
		if (this.mInsideThumb === true) {
			var deltaX = x - this.mMouseDownX;
			var deltaY = y - this.mMouseDownY;
			
			if ((deltaX === 0) && (deltaY === 0)) {
				return true;
			}
					
			if (this.mVertical) {
				deltaY = deltaY - track.offsetTop;
			} else {
				deltaX = deltaX - track.offsetLeft;
			}
			
			if (this.mVertical) {
				pct = deltaY / pixelRange;
			} else {
				pct = deltaX / pixelRange;
			}
			this.setValue(pct * valueRange);
		} else if (this.mInsideTrack === true) {
			return true;
		} else {
			var decrease = findChildrenByClass(this.object(),'decrease');
	        if (decrease.length === 0) {
	            decrease = findChildrenByClass(this.object(),'decrease pressed');
	        }
	        var increase = findChildrenByClass(this.object(),'increase');
	        if (increase.length === 0) {
	            increase = findChildrenByClass(this.object(),'increase pressed');
	        }
	        decrease = decrease[0];
	        increase = increase[0];
	        
	        if ((x >= decrease.offsetLeft) && (x <= decrease.offsetLeft + decrease.offsetWidth) && (y >= decrease.offsetTop) && (y <= decrease.offsetTop + decrease.offsetHeight)) {
	            this.setValue(this.value() - this.lineStep());
	            this.mHoldWidget = decrease;
	            if (decrease.className == 'decrease') {
	                decrease.className = 'decrease pressed';
	            }
	        } else if ((x >= increase.offsetLeft) && (x <= increase.offsetLeft + increase.offsetWidth) && (y >= increase.offsetTop) && (y <= increase.offsetTop + increase.offsetHeight)) {
	            this.setValue(this.value() + this.lineStep());
	            this.mHoldWidget = increase;
	            if (increase.className == 'increase') {
	                increase.className = 'increase pressed';
	            }
	        } else {
	            return true;
	        }
		}
		
		return true;
	}
};

scrollbar.prototype.touchEnd = function (event, coordinates)
{
	if(this.mEnabled) {
		if (this.mMaximum <= this.mMinimum) {
			return true;
		}
		
		if (this.mHoldWidget) {
			if (this.mHoldWidget.className == 'decrease pressed') {
				this.mHoldWidget.className = 'decrease';
			} else if (this.mHoldWidget.className == 'increase pressed') {
				this.mHoldWidget.className = 'increase';
			}
			this.mHoldWidget = null;
		}
		
		return true;
	}
};

scrollbar.prototype.mouseWheel = function (deltaX, deltaY)
{
	if(this.mEnabled) {
		if (this.mVertical) {
	        this.setValue(this.value() + (deltaY * this.lineStep()));
	    } else {
	        this.setValue(this.value() + (deltaX * this.lineStep()));
	    }
		return true;
	}
};

scrollbar.prototype.jumpTo = function (x,y)
{
	var track = findChildrenByClass(this.object(),'track');
	track = track[0];
	var thumb = findChildrenByClass(track,'thumb');
	thumb = thumb[0];
	
	var thumbSize, trackSize, pixelMin, pixelMax;
	if (this.mVertical) {
		thumbSize = thumb.offsetHeight;
		trackSize = track.offsetHeight;
	} else {
		thumbSize = thumb.offsetWidth;
		trackSize = track.offsetWidth;
	}
	pixelMin = thumbSize / 2;
	pixelMax = trackSize - (thumbSize / 2);
	
	var amt;
	if (this.mVertical) {
		amt = (y - pixelMin) / (pixelMax - pixelMin);
	} else {
		amt = (x - pixelMin) / (pixelMax - pixelMin);
	}
	amt = Math.min(Math.max(amt,0),1);
	
	var valueRange = this.maximum() - this.minimum();
	
	this.setValueAnimated(amt * valueRange);
};

scrollbar.prototype.setValue = function (newValue)
{
	newValue = parseInt(newValue,10);
    var v = Math.max(Math.min(newValue,this.maximum()),this.minimum());
	if (this.mValue == v) {
		return;
	}
	this.mValue = v;
	this.refresh();
	markControlChanged(this);
	if (this.valueChanged !== '') {
		eval(this.valueChanged);
	} else if (this.implementedEvents.indexOf('ValueChanged') > -1) {
		RS.comm.triggerEvent(this.controlID,'ValueChanged');
	}
};

scrollbar.prototype.setValueAnimated = function (newValue)
{
	newValue = parseInt(newValue,10);
    if (this.animator > 0) {
        clearInterval(this.animator);
    }
    this.animationElapsed = 0;
    this.animationStartValue = this.value();
    this.animationEndValue = newValue;
    this.animator = setInterval("RS.controls['" + this.controlID + "'].animationStep();",this.animationPeriod);
};

scrollbar.prototype.animationStep = function ()
{
    this.animationElapsed = Math.min(this.animationElapsed + this.animationPeriod,this.animationDuration);
    var duration = (this.animationDuration / 1000);
    var elapsed = ((this.animationElapsed / 1000) / duration) - 1;
    var diff = this.animationEndValue - this.animationStartValue;
    var value = (diff * (((((elapsed)) * elapsed) * elapsed) + 1)) + this.animationStartValue;
    
    this.setValue(value);
    
    if (this.animationElapsed >= this.animationDuration) {
        clearInterval(this.animator);
        this.animator = 0;
        if (this.value() != this.animationEndValue) {
            this.setValue(this.animationEndValue);
        }
    }
};

scrollbar.prototype.value = function ()
{
	return parseInt(this.mValue,10);
};

scrollbar.prototype.setMinimum = function (newValue)
{
	this.mMinimum = parseInt(newValue,10);
	if (this.mValue < this.mMinimum) {
		this.setValue(this.mMinimum);
	} else {
		this.refresh();
	}
};

scrollbar.prototype.minimum = function ()
{
	return parseInt(this.mMinimum,10);
};

scrollbar.prototype.setMaximum = function (newValue)
{
	this.mMaximum = parseInt(newValue,10);
	if (this.mValue > this.mMaximum) {
		this.setValue(this.mMaximum);
	} else {
		this.refresh();
	}
};

scrollbar.prototype.maximum = function ()
{
	return parseInt(this.mMaximum,10);
};

scrollbar.prototype.setPageStep = function (newValue)
{
	this.mPageStep = parseInt(newValue,10);
	this.refresh();
};

scrollbar.prototype.pageStep = function ()
{
	return parseInt(this.mPageStep,10);
};

scrollbar.prototype.setLineStep = function (newValue)
{
	this.mLineStep = parseInt(newValue,10);
	this.refresh();
};

scrollbar.prototype.lineStep = function ()
{
	return parseInt(this.mLineStep,10);
};

scrollbar.prototype.willRefresh = function ()
{
	var track = findChildrenByClass(this.object(),'track');
	track = track[0];
	var thumb = findChildrenByClass(track,'thumb');
	thumb = thumb[0];
	
	if (this.mMaximum <= this.mMinimum) {
		// neutral
		thumb.style.display = 'none';
		if (this.mVertical) {
			track.style.top = '-15px';
			track.style.bottom = '-15px';
		} else {
			track.style.left = '-15px';
			track.style.right = '-15px';
		}
		return;
	} else if (thumb.style.display == 'none') {
		thumb.style.display = 'block';
		if (this.mVertical) {
			track.style.top = '15px';
			track.style.bottom = '15px';
		} else {
			track.style.left = '15px';
			track.style.right = '15px';
		}
	}
	
	var valueRange = this.maximum() - this.minimum();
	var valuePercent = (this.value() - this.minimum()) / (this.maximum() - this.minimum());
	var pixelRange;
	if (this.mVertical) {
		pixelRange = track.offsetHeight;
	} else {
		pixelRange = track.offsetWidth;
	}
	var thumbSize = Math.max(pixelRange * this.pageStep() / (valueRange + this.pageStep()),16);
	if(isNaN(thumbSize)) { thumbSize = 16; }
	pixelRange = pixelRange - thumbSize;
	
	var low, high;
	if (this.mVertical) {
		low = Math.floor(Math.max(valuePercent * pixelRange,0));
		high = Math.floor(Math.max(track.offsetHeight - (low + thumbSize),0));
		thumb.style.top = low + 'px';
		thumb.style.bottom = high + 'px';
		thumb.style.height = '';
	} else {
		low = Math.floor(Math.max(valuePercent * pixelRange,0));
		high = Math.floor(Math.max(track.offsetWidth - (low + thumbSize),0));
		thumb.style.left = low + 'px';
		thumb.style.right = high + 'px';
		thumb.style.width = '';
	}
};

scrollbar.prototype.resize = function ()
{
	this.refresh();
};

/* END: SCROLLBAR */

/* BEGIN: LISTBOX */

// ListBox/framework.js
//
// Utility class for working with listboxes
//
// © 2010 REAL Software Inc. -- All Rights Reserved
// This code contains patent-pending technology.

function listbox (target, events)
{
	listbox.baseConstructor.call(this,target,events);
	
	var myName = this.controlID;
	this.mSelectedRows = [];
	this.mColumnWidths = [];
	this.mColumnStyles = [];
	this.mBaseStyle = 'listbox';
	
	var headers = document.getElementById(myName + "_headers");
	var content = document.getElementById(myName + "_content");
	content.style.top = headers.offsetHeight + 'px';
	
	var element;
	element = document.createElement('div');
	element.id = myName + '_vScroll';
	element.className = "scrollbar vertical";
	this.object().appendChild(element);
	element = document.createElement('div');
	element.id = myName + '_hScroll';
	element.className = "scrollbar horizontal";
	this.object().appendChild(element);
	element = document.createElement('div');
	element.id = myName + '_scrollCorner';
	element.className = "corner";
	element.innerHTML = '&nbsp;';
	this.object().appendChild(element);
	
	this.verticalScroller = new scrollbar(myName + "_vScroll",[]);
	this.verticalScroller.setEnabled(true);
	this.verticalScroller.valueChanged = "RS.controls['" + myName + "'].scrollVertical()";
	this.horizontalScroller = new scrollbar(myName + "_hScroll",[]);
	this.horizontalScroller.setEnabled(true);
	this.horizontalScroller.valueChanged = "RS.controls['" + myName + "'].scrollHorizontal()";
	RS.input.install(this.object());
	RS.view.sizing.targets.push(this);
	addWheelTarget(this);
	this.refresh();
	
	//Add some listeners to make the touch experience a little better
	try {
		content.addEventListener('touchstart',this, false);
		content.addEventListener('touchmove',this,false);
		content.addEventListener('touchend',this,false);
		content.addEventListener('touchcancel',this,false);
	} catch(err) {
		//If it can't handle addEventListener, it's probably not iOS or Android
	}
}

frameworkSubclass(listbox, frameworkObject);
listbox.prototype.verticalScroller = null;
listbox.prototype.horizontalScroller = null;
listbox.prototype.mSelectedRows = null;
listbox.prototype.mColumnWidths = null;
listbox.prototype.mColumnStyles = null;
listbox.prototype.mMinimumRowHeight = -1;
listbox.prototype.mPrimaryRowColor = '#FFFFFF';
listbox.prototype.mAlternateRowColor = '#EDF3FE';
listbox.SCROLLBARWIDTH = 15;
listbox.SCROLLBARHEIGHT = 15;

listbox.prototype.scrollVertical = function ()
{
	var rows = document.getElementById(this.controlID + "_rows");
	if (rows) {
		rows.style.top = Math.floor(this.verticalScroller.value() * -1) + 'px';
	}
};

listbox.prototype.scrollHorizontal = function ()
{
	var rows = document.getElementById(this.controlID + "_rows");
	var headers = document.getElementById(this.controlID + "_headers");
	if ((rows) && (headers)) {
		rows.style.left = Math.floor(this.horizontalScroller.value() * -1) + 'px';
		headers.style.left = rows.style.left;
	}
};

listbox.prototype.willRefresh = function ()
{
	var headers = document.getElementById(this.controlID + "_headers");
	var content = document.getElementById(this.controlID + "_content");
	var rows = document.getElementById(this.controlID + "_userrows");
	
	var i, headerChildren, min;
	headerChildren = headers.children;
	headerChildren = headerChildren.item(0).rows;
	headerChildren = headerChildren.item(0).cells;
	
	for (i = this.mColumnWidths.length; i < headerChildren.length -1; i++) {
		this.mColumnWidths[i] = '*';
	}
	min = Math.min(this.mColumnWidths.length,headerChildren.length - 1);
	
	var thisObject = this.object();
	var headersHeight = 22;
	var viewportWidth = thisObject.offsetWidth;
	var viewportHeight = thisObject.offsetHeight - headersHeight;
	var columnWidths;
	var contentWidth = 0;
	var contentHeight = 0;
	var needsHorizontalScroll = false;
	var needsVerticalScroll = false;
	var last = {'viewportWidth':0,'viewportHeight':0};
	var padderWidth, w, style;
	var finish = false;
	var cssWidths, padder;
	var row, r, cell, c;

	//See if we'll be needing scrollbars today, and adjust the viewportWidth and height
	headersHeight = headers.offsetHeight;
	contentHeight = rows.offsetHeight;
	if ((contentHeight > viewportHeight) && (needsVerticalScroll === false)) {
		needsVerticalScroll = true;
		viewportWidth = viewportWidth - listbox.SCROLLBARWIDTH;
	}
	if ((contentWidth > viewportWidth) && (needsHorizontalScroll === false)) {
		needsHorizontalScroll = true;
		viewportHeight = viewportHeight - listbox.SCROLLBARHEIGHT;
	}
	
	while (finish !== true) {
		//If the viewport width has changed, we need to do it again
		if ((viewportWidth != last.viewportWidth) || (viewportHeight != last.viewportHeight)) {	
			//Calculate the individual column widths
			columnWidths = this.actualColumnWidths(viewportWidth);
			//Figure out how wide all of the columns are together
			contentWidth = 0;
			for (i = 0; i < columnWidths.length; i++) {
				contentWidth = contentWidth + columnWidths[i];
			}
			//Update the stored viewport size (so we'll know if they changed again)
			last.viewportWidth = viewportWidth;
			last.viewportHeight = viewportHeight;
			
			//padderWidth is the width of the column that needs to be created to make up any space to the right of the defined columns
			padderWidth = thisObject.offsetWidth;
			
			//Calculate how wide each section should be in CSS units, removing 1 pixel per column to account for the borders
			cssWidths = [];
			for (i = 0; i < columnWidths.length; i++) {
				w = columnWidths[i]-1;
				w = Math.max(w - 6,0); // Allow for padding
				padderWidth = padderWidth - columnWidths[i]; //subtract each column width
				cssWidths[i] = w;
			}
			
			//For each of the headers, set the min, max and current widths
			for (i = 0; i < Math.min(headerChildren.length,cssWidths.length); i++) {
				style = headerChildren[i].style;
				if ((style) && (style.width != cssWidths[i])) {
					style.display = '';
					style.width = cssWidths[i]+'px';
					style.minWidth = cssWidths[i]+'px';
					style.maxWidth = cssWidths[i]+'px';
				}
				
				if(RS.ua.ie==0 || RS.ua.ie > 8) {
					//Do the same for all of the following rows
					for(r=0;r<rows.rows.length;r++) {
						var widthOffset = 0;
						if(i>0) {
							widthOffset = 0;
						}
						style = rows.rows[r].cells[i].style;
						style.display = '';
						
						style.width = (cssWidths[i]-widthOffset)+'px';
						style.minWidth = (cssWidths[i]-widthOffset)+'px';
						style.maxWidth = (cssWidths[i]-widthOffset)+'px';
					}
				}
			}
			
			//After the columns are inserted, calculate the width of the padder
			if (!padder) {
				padder = this.padder();
				padder.style.borderRight = 'none';
			}
			
			padderWidth = Math.max(padderWidth,0); //Make sure padderWidth is at least 0
			if (padderWidth > 0) {
				padderWidth = Math.max(listbox.SCROLLBARWIDTH,padderWidth); //If it's greater than zero, make it at least the width of the scrollbar
			}
			
			if((RS.ua.ie > 6 && RS.ua.ie < 9)) {
				for (r = 0; r < rows.rows.length; r++) {
					row = rows.rows[r];
					for (c = 0; c < row.cells.length; c++) {
						cell = row.cells[c];
						style = cell.style;
						if (style) {
							if (c < cssWidths.length) {
								w = cssWidths[c] + 'px';
							} else {
								if(RS.ua.ie == 8) {
									w = viewportWidth + 'px';
								} else {
									w = padderWidth + 'px';
								}
							}
							style.width = w;
							style.minWidth = w;
							style.maxWidth = w;
						}
					}
				}
			} else {				
				if (padder.style.width != padderWidth + 'px') {
					padder.style.minWidth = padderWidth + 'px';
					padder.style.maxWidth = padderWidth + 'px';
					padder.style.width = padderWidth + 'px';
				}
			}
			headersHeight = headers.offsetHeight;
			contentHeight = rows.offsetHeight;
			content.style.top = headersHeight + 'px';
			viewportHeight = content.offsetHeight;
			if (needsHorizontalScroll === true) {
				viewportHeight = viewportHeight - listbox.SCROLLBARHEIGHT;
			}
		}
		if ((contentHeight > viewportHeight) && (needsVerticalScroll === false)) {
			needsVerticalScroll = true;
			viewportWidth = viewportWidth - listbox.SCROLLBARWIDTH;
			continue;
		}
		if ((contentWidth > viewportWidth) && (needsHorizontalScroll === false)) {
			needsHorizontalScroll = true;
			viewportHeight = viewportHeight - listbox.SCROLLBARHEIGHT;
			continue;
		}
		finish = true;
	}
	
	if (needsVerticalScroll === true) {
		this.verticalScroller.setLineStep(this.minimumRowHeight());
		this.verticalScroller.setPageStep(viewportHeight);
		this.verticalScroller.setMaximum(contentHeight - viewportHeight);
		this.verticalScroller.object().style.display = 'block';
		if (needsHorizontalScroll === true) {
			this.verticalScroller.object().style.bottom = '14px';
		} else {
			this.verticalScroller.object().style.bottom = '-1px';
		}
		this.verticalScroller.object().style.top = headersHeight + 'px';
	} else {
		this.verticalScroller.object().style.display = 'none';
		if (this.verticalScroller.value() !== 0) {
			this.verticalScroller.setValue(0);
		}
		if (this.verticalScroller.maximum() !== 0) {
			this.verticalScroller.setMaximum(0);
		}
	}
	
	if (needsHorizontalScroll === true) {
		this.horizontalScroller.setLineStep(10);
		this.horizontalScroller.setPageStep(viewportWidth);
		this.horizontalScroller.setMaximum(contentWidth - viewportWidth);
		this.horizontalScroller.object().style.display = 'block';
		if (needsVerticalScroll === true) {
			this.horizontalScroller.object().style.right = '14px';
		} else {
			this.horizontalScroller.object().style.right = '-1px';
		}
	} else {
		this.horizontalScroller.object().style.display = 'none';
		if (this.horizontalScroller.value() !== 0) {
			this.horizontalScroller.setValue(0);
		}
		if (this.horizontalScroller.maximum() !== 0) {
			this.horizontalScroller.setMaximum(0);
		}
	}
	
	w = Math.max(contentWidth,this.object().offsetWidth) + 'px';
	var rowsTable = document.getElementById(this.controlID + "_rows");
	rowsTable.style.width = w;
	rowsTable.style.minWidth = w;
	rowsTable.style.maxWidth = w;
	headers.style.width = w;
	headers.style.minWidth = w;
	headers.style.maxWidth = w;
	
	var corner = document.getElementById(this.controlID + "_scrollCorner");
	var cornerDisplay = 'none';
	if ((needsHorizontalScroll === true) && (needsVerticalScroll === true)) {
		cornerDisplay = 'block';
	}
	if (corner.style.display != cornerDisplay) {
		corner.style.display = cornerDisplay;
	}
	
	if (viewportHeight > contentHeight) {
		var autoContent = document.getElementById(this.controlID + "_autocontent");
		var excess = viewportHeight - contentHeight;
		var requiredRows = Math.ceil(excess / this.mMinimumRowHeight);
		
		var autoRows = autoContent.rows;
		for (i = autoRows.length + rows.rows.length; i < requiredRows + rows.rows.length; i++) {
			row = autoContent.insertRow(-1);
			cell = row.insertCell(-1);
			cell.setAttribute("colSpan","99");
			cell.innerHTML = "&nbsp;";
			cell.style.minHeight = this.mMinimumRowHeight + 'px';
			cell.style.height = cell.style.minHeight;
			cell.style.width = w;
			cell.style.minWidth = cell.style.width;
			cell.style.maxWidth = cell.style.width;
			row.style.backgroundColor = (i % 2 === 0) ? this.mPrimaryRowColor : this.mAlternateRowColor;
			c++;
		}
	}
	
	this.verticalScroller.refresh();
	this.horizontalScroller.refresh();
};

listbox.prototype.finishedLoading = function ()
{
	this.recolorRows();
};

listbox.prototype.actualColumnWidths = function (width)
{
	var widths = [];
	var remaining = width;
	var i, numFractions, exp, last, body, w, fillPercent;
	var n = this.columnCount();
	
	numFractions = 0;
	fillPercent = 0;
	for (i = 0; i < n; i++) {
		exp = this.mColumnWidths[i];
		last = exp.substring(exp.length - 1);
		body = exp.substring(0,exp.length - 1);
		
		if (last == '%') {
			w = Math.floor(width * (parseFloat(body) / 100));
			fillPercent = fillPercent + parseFloat(body);
		} else if (last == '*') {
			if (body === '') {
				body = '1';
			}
			numFractions = numFractions + parseFloat(body);
			widths[i] = body + '*';
			continue;
		} else {
			w = parseFloat(exp);
			//fillPercent = fillPercent + Math.round((w/width) * 100);
		}
		
		remaining = remaining - w;
		widths[i] = w;
	}
	for (i = 0; i < n; i++) {
		exp = widths[i].toString();
		last = exp.substring(exp.length - 1);
		body = exp.substring(0,exp.length - 1);
		
		if (last == '*') {
			w = Math.floor(remaining * (parseFloat(body) / numFractions));
			fillPercent = fillPercent + Math.round((w / width) * 100);
		} else {
			continue;
		}
		
		numFractions = numFractions - 1;
		remaining = remaining - w;
		widths[i] = w;
	}
	return widths;
};

listbox.prototype.totalColumnWidths = function (width) {
	var cols = this.actualColumnWidths(width);
	var total = 0;
	var i = 0;
	for(i=0;i<cols.length;i++) {
		total += cols[i];
	}
	return total;
};

listbox.prototype.padder = function ()
{
	return document.getElementById(this.controlID + "_padder");
};

listbox.prototype.touchBegin = function (event, coordinates)
{
	if(this.mEnabled) {
		var x = coordinates[0].x;
		var y = coordinates[0].y;
		
		var vScrollWidth = this.verticalScroller.object().offsetWidth;
		var vScrollHeight = this.verticalScroller.object().offsetHeight;
		var vScrollLeft = this.verticalScroller.object().offsetLeft;
		var vScrollTop = this.verticalScroller.object().offsetTop;
		
		var hScrollWidth = this.horizontalScroller.object().offsetWidth;
		var hScrollHeight = this.horizontalScroller.object().offsetHeight;
		var hScrollLeft = this.horizontalScroller.object().offsetLeft;
		var hScrollTop = this.horizontalScroller.object().offsetTop;
		
		if ((x >= vScrollLeft) && (y >= vScrollTop) && (x <= vScrollLeft + vScrollWidth) && (y <= vScrollTop + vScrollHeight) && (this.verticalScroller.object().style.display != 'none')) {
			return RS.input.forwardEvent(event,this.verticalScroller);
		} else if ((x >= hScrollLeft) && (y >= hScrollTop) && (x <= hScrollLeft + hScrollWidth) && (y <= hScrollTop + hScrollHeight) && (this.horizontalScroller.object().style.display != 'none')) {
			return RS.input.forwardEvent(event,this.horizontalScroller);
		}
		
		return true;
	}
};

listbox.prototype.touchEnd = function (event, coordinates)
{
	if(this.mEnabled) {
		var x = coordinates[0].x;
		var y = coordinates[0].y;
		
		var headers = document.getElementById(this.controlID + "_headers");
		var headersHeight = headers.offsetHeight;
		
		if (y <= headersHeight) {
			return;
		} else {
			y = y - headersHeight;
		}
		
		var rows = document.getElementById(this.controlID + "_rows");
		var userRows = document.getElementById(this.controlID + "_userrows");
		y = y - rows.offsetTop;
		
		if (y > userRows.offsetHeight) {
			this.setSelectedRows(null);
			return;
		}
		var selectType;
		if (event.shiftKey === true) {
			selectType = 2;
		} else {
			if (RS.platform == 1) {
				if (event.metaKey === true) {
					selectType = 3;
				} else {
					selectType = 1;
				}
			} else {
				if (event.ctrlKey === true) {
					selectType = 3;
				} else {
					selectType = 1;
				}
			}
		}
		
		var children = userRows.children;
		var i, s, row;
		for (i = 0; i < children.length; i++) {
			if ((y >= children[i].offsetTop) && (y <= children[i].offsetTop + children[i].offsetHeight)) {
				row = i;
				break;
			}
		}
		
		if (this.implementedEvents.indexOf('CellClick') > -1) {
			var column = -1, cx = 0;
			if (headersHeight > 0) {
				children = headers.rows[0].cells;
			} else {
				children = children[row].cells;
			}
			for (i = 0; i < (children.length - 1); i++) {
				if ((x >= cx) && (x <= cx + children[i].offsetWidth)) {
					column = i;
					break;
				} else {
					cx = cx + children[i].offsetWidth;
				}
			}
			if (column > -1) {
				RS.comm.triggerEvent(this.name(),'CellClick',[row,column],event);
			}
		}
		
		s = row.toString();
		var selRows;
		if (selectType == 1) {
			this.setSelectedRows([s],true);
		} else if (selectType == 2) {
			selRows = this.mSelectedRows;
			var low = row, high = row;
			for (i = 0; i < selRows.length; i++) {
				low = Math.min(selRows[i],low);
				high = Math.max(selRows[i],high);
			}
			selRows = [];
			for (i = low; i <= high; i++) {
				selRows.push(String(i));
			}
			this.setSelectedRows(selRows,false);
		} else if (selectType == 3) {
			selRows = this.mSelectedRows;
			if (selRows.indexOf(s) == -1) {
				selRows.push(s);
			} else {
				selRows.splice(selRows.indexOf(s),1);
			}
			this.setSelectedRows(selRows,false);
		}
		return true;
	}
};

listbox.prototype.mouseWheel = function (deltaX, deltaY)
{
	if(this.mEnabled) {
		this.verticalScroller.setValue(this.verticalScroller.value() + (deltaY * this.verticalScroller.lineStep()));
		this.horizontalScroller.setValue(this.horizontalScroller.value() + (deltaX * this.horizontalScroller.lineStep()));
		return true;
	}
};

listbox.prototype.columnCount = function ()
{
	var headers = document.getElementById(this.controlID + "_headers");
	var tbody = headers.children.item(0);
	var columns = tbody.children.item(0).children;
	return columns.length - 1;
};

listbox.prototype.appendRow = function (columnData)
{
	this.insertRow(-1,columnData);
};

listbox.prototype.insertRow = function (rowIndex, columnData)
{
	var userContents = document.getElementById(this.controlID + "_userrows");
	var i;
	var selRows = this.mSelectedRows;
	if ((selRows) && (rowIndex >= 0)) {
		for (i = 0; i < selRows.length; i++) {
			if (selRows[i] >= rowIndex) {
				selRows[i] = parseInt(selRows[i],10) + 1;
				selRows[i] = selRows[i].toString();
			}
		}
		this.mSelectedRows = selRows;
	}
    
    var rows = userContents.rows;
    var row, cell;
    if ((rowIndex < 0) || (rowIndex >= rows.length)) {
        rowIndex = -1;
    }
    row = userContents.insertRow(rowIndex);
	var c = this.columnCount();
	var s = '';
    for (i = 0; i < c; i++) {
        cell = row.insertCell(-1);
		cell.style.minHeight = this.mMinimumRowHeight + 'px';
		cell.style.height = cell.style.minHeight;
		s = this.columnStyle(i);
		if (s !== '') {
			s = s + ' ';
		}
        cell.className = s + "column" + (i + 1);
        if (i < columnData.length) {
            cell.innerHTML = columnData[i];
        } else {
            cell.innerHTML = "&nbsp;";
        }
    }
    cell = row.insertCell(-1);
    cell.className = "padder";
    cell.innerHTML = "&nbsp;";
    
	this.recolorRows();
	this.refresh();
};

listbox.prototype.removeRow = function (rowIndex)
{
	var userContents = document.getElementById(this.controlID + "_userrows");
    userContents.deleteRow(rowIndex);
	
	var selRows = this.mSelectedRows;
	var idx = selRows.indexOf(String(rowIndex));
	if (idx != -1) {
		selRows.splice(idx,1);
		this.setSelectedRows(selRows,false,false);
	}
	
	this.recolorRows();
	this.refresh();
};

listbox.prototype.deleteAllRows = function ()
{
	var userContents = document.getElementById(this.controlID + "_userrows");
	var c = userContents.rows.length;
    var i;
    for (i = 0; i < c; i++) {
        userContents.deleteRow(0);
    }
    
	this.mSelectedRows = [];
	this.setSelectedRows(this.mSelectedRows,false,false);
	this.recolorRows();
	this.refresh();
};

listbox.prototype.recolorRows = function ()
{
	var rowColor = this.mPrimaryRowColor;
	var rows = document.getElementById(this.controlID + "_rows").rows;
	var r,style;
	
	for (r = 0; r < rows.length; r++) {
		style = rows[r].style;
		if (style) {
			style.backgroundColor = rowColor;
		}
		if (this.isRowSelected(r) === true) {
			if (rows[r].className != 'selected') {
				rows[r].className = 'selected';
			}
		} else {
			if (rows[r].className !== null) {
				rows[r].className = null;
			}
		}
		rowColor = (rowColor == this.mPrimaryRowColor) ? this.mAlternateRowColor : this.mPrimaryRowColor;
	}
};

listbox.prototype.setSelectedRows = function (rows, scroll, triggerEvent)
{
	if (rows === null) {
        rows = [];
    }
	if ((this.mSelectedRows.length === 0) && (rows.length === 0)) {
		return;
	}
	if(triggerEvent === undefined) { triggerEvent = true; }
	this.mSelectedRows = rows;
	this.recolorRows();
    
    if ((scroll === null) || (scroll === true)) {
        if (rows.length > 0) {
            this.scrollToRow(rows[0]);
        }
    }
    
	markControlChanged(this);
	if (triggerEvent) {
		if (this.implementedEvents.indexOf('SelectionChanged') > -1) {
			RS.comm.triggerEvent(this.name(),'SelectionChanged');
		}
	}
};

listbox.prototype.scrollToRow = function (rowIndex)
{
    var body = document.getElementById(this.controlID + "_rows");
    var rows = body.rows;
    if ((rowIndex < 0) || (rowIndex >= rows.length)) {
        return;
    }
    var row = rows[rowIndex];
    var viewportHeight = document.getElementById(this.controlID + "_content").offsetHeight;
    var position = body.offsetTop;
    var top = row.offsetTop + position;
    var bottom = top + row.offsetHeight;
    
    if (top < 0) {
        this.verticalScroller.setValueAnimated(top - position);
    } else if (bottom > viewportHeight) {
        var diff = bottom - viewportHeight;
        this.verticalScroller.setValueAnimated(this.verticalScroller.value() + diff);
    }
};

listbox.prototype.isRowSelected = function (rowIndex)
{
	
	if (this.mSelectedRows.indexOf(String(rowIndex)) == -1) {
		return false;
	} else {
		return true;
	}
};

listbox.prototype.value = function ()
{
	return this.mSelectedRows.join(",");
};

listbox.prototype.setValue = function (input)
{
	var rows = input.split(",");
	this.setSelectedRows(rows);
};

listbox.prototype.setColumnWidths = function (widthString)
{
	var widths = widthString.split(",");
	this.mColumnWidths = widths;
	this.refresh();
};

listbox.prototype.setColumnHeading = function (column, heading)
{
	if ((column < 0) || (column >= this.columnCount())) {
		return;
	}
	var headers = document.getElementById(this.controlID + "_headers");
	var tbody = headers.children.item(0);
	var columns = tbody.children.item(0).children;
	
	columns[column].innerHTML = heading;
	this.refresh();
};

listbox.prototype.setColumnCount = function (count)
{
	var currentCount = this.columnCount();
	
	if (currentCount == count) {
		return;
	}
	
	var headers = document.getElementById(this.controlID + "_headers");
	var headers_body = headers.children.item(0);
	var user_rows = document.getElementById(this.controlID + "_userrows");
	var auto_rows = document.getElementById(this.controlID + "_autocontent");
	
	this.alterColumns(headers_body, count);
	this.alterColumns(user_rows, count);
	this.alterColumns(auto_rows, count);
	this.recolorRows();
	this.refresh();
};

listbox.prototype.alterColumns = function (tbody, columnCount)
{
	var rows = tbody.rows;
	var cells,currentCount;
	if (rows.length > 0) {
		cells = rows.item(0).cells;
		currentCount = cells.length - 1;
	} else {
		currentCount = this.columnCount();
	}
    if (currentCount == columnCount) {
        return;
    }
    
	var r,c,cell;
	for (r = 0; r < rows.length; r++) {
		cells = rows.item(r).children;
        if (columnCount > currentCount) {
            // add cells
            for (c = currentCount; c < columnCount; c++) {
                cell = rows.item(r).insertCell(c);
                cell.className = "column" + (c + 1);
                cell.innerHTML = "&nbsp;";
            }
		} else {
            // remove cells
            for (c = columnCount; c < currentCount; c++) {
                rows.item(r).deleteCell(columnCount);
            }
        }
	}
};

listbox.prototype.setMinimumRowHeight = function (value)
{
	value = Math.max(1,parseInt(value,10));
	if (value != this.mMinimumRowHeight) {
		var rows = document.getElementById(this.controlID + "_rows").rows;
		var row,r,c,style;
		for (r = 0; r < rows.length; r++) {
			row = rows[r];
			for (c = 0; c < row.cells.length; c++) {
				style = row.cells[c].style;
				if ((style) && (style.minHeight != value + 'px')) {
					style.minHeight = value + 'px';
					style.height = style.minHeight;
				}
			}
		}
		this.mMinimumRowHeight = value;
		this.refresh();
	}
};

listbox.prototype.minimumRowHeight = function ()
{
	return this.mMinimumRowHeight;
};

listbox.prototype.setPrimaryRowColor = function (value)
{
	if (value != this.mPrimaryRowColor) {
		this.mPrimaryRowColor = value;
		this.recolorRows();
	}
};

listbox.prototype.setAlternateRowColor = function (value)
{
	if (value != this.mAlternateRowColor) {
		this.mAlternateRowColor = value;
		this.recolorRows();
	}
};

listbox.prototype.setCellContents = function (row, column, contents)
{
	var tbody = document.getElementById(this.controlID + "_userrows");
	var cell = tbody.children.item(row).children.item(column);
	if (cell) {
		cell.innerHTML = contents;
	}
};

listbox.prototype.setEnabled = function (value)
{
	this.setDisabledAppearance(!value);
	this.mEnabled = value;
	this.refresh();
};

listbox.prototype.setHeadingVisible = function (value)
{
	var headers = document.getElementById(this.controlID + "_headers");
	if (value === true) {
		headers.style.display = "block";
	} else {
		headers.style.display = "none";
	}
	this.refresh();
};

listbox.prototype.columnStyle = function (column) {
	if ((column < 0) || (column >= this.columnCount()) || (column >= this.mColumnStyles.length)) {
		return '';
	} else {
		return this.mColumnStyles[column];
	}
};

listbox.prototype.setColumnStyle = function (column, className)
{
	var c = this.columnCount();
	if ((column < 0) || (column >= c)) {
		return;
	}
	
	while (this.mColumnStyles.length < c) {
		this.mColumnStyles.push('');
	}
	
	var oldStyle = this.mColumnStyles[column];
	if (className) {
		this.mColumnStyles[column] = className;
	} else {
		this.mColumnStyles[column] = '';
	}
	var newStyle = this.mColumnStyles[column];
	
	var rows = document.getElementById(this.controlID + '_userrows').rows;
	var i, className;
	for (i = 0; i < rows.length; i++) {
		className = rows[i].cells[column].className;
		if (oldStyle !== '') {
			if (className.indexOf(oldStyle) > -1) {
				className = className.replace(oldStyle,newStyle);
			} else {
				className = newStyle;
			}
		} else {
			className = newStyle;
		}
		rows[i].cells[column].className = newStyle;
	}
};

listbox.prototype.setCellStyle = function (row, column, className)
{
	var rows = document.getElementById(this.controlID + "_userrows").rows;
	if ((row >= 0) && (row < rows.length) && (column >= 0) && (column <= this.columnCount())) {
		var cell = rows[row].cells[column];
		var cn = this.columnStyle(column);
		if (cn !== '') {
			cn = cn + ' ';
		}
		cn = cn + 'column' + (column + 1) + '';
		if (className) {
			cn = cn + ' ' + className;
		}
		cell.className = cn;
	}
};

//methods for iOS and Android
listbox.prototype.handleEvent = function(event) {
	if(typeof(this[event.type]) === "function") {
		return this[event.type](event);
	}
}

listbox.prototype.touchstart = function(event) {
	if(this.mEnabled) {
		event.stopPropagation();
		//Record the start point
		var rows = document.getElementById(this.controlID + "_rows");
		var fingerOne = event.targetTouches[0];
		this.touchStartX = fingerOne.pageX;
		this.touchStartY = fingerOne.pageY;
		this.startPosX = rows.style.left || "0px";
		this.startPosY = rows.style.top || "0px";
	
		var rr = /px/g;
		this.startPosX = parseInt(this.startPosX.replace(rr,""));
		this.startPosY = parseInt(this.startPosY.replace(rr,""));
		this.touchMoved = false;
	}
};

listbox.prototype.touchmove = function(event) {
	if(this.mEnabled) {
		event.stopPropagation();
		event.preventDefault();
		//Move the list
		var fingerOne = event.targetTouches[0];
		var curX = fingerOne.pageX;
		var curY = fingerOne.pageY;
		this.dx = curX-this.touchStartX;
		this.dy = curY-this.touchStartY;
		this.touchScrollList(this.startPosX+this.dx,this.startPosY+this.dy);
		this.verticalScroller.setValue(-1*(this.startPosY+this.dy));
		this.horizontalScroller.setValue(-1*(this.startPosX+this.dx));
		this.touchMoved = true;
	}
};

listbox.prototype.touchend = function(event) {
	if(this.mEnabled) {
		event.stopPropagation();
		if(!this.touchMoved) {
			RS.input.touchHandler(event);
		} else {
			//set a css transform for the momentum		
		}
	}
};

listbox.prototype.touchcancel = function(event) {
	//Return to the original state
	rows.style.left = this.startPosX;
	rows.style.top = this.startPosY;
};

listbox.prototype.touchScrollList = function(xpos,ypos) {

	var rows = document.getElementById(this.controlID + "_rows");
	var userrows = document.getElementById(this.controlID + "_userrows");
	var headers = document.getElementById(this.controlID + "_headers");
	var content = document.getElementById(this.controlID + "_content");
	var vscroll = document.getElementById(this.controlID + "_vScroll");
	var hscroll = document.getElementById(this.controlID + "_hScroll");
	
	var maxNegX = -1 * (Math.max(parseInt(userrows.offsetWidth) - parseInt(content.offsetWidth)),0);
	var maxNegY = -1 * ((parseInt(userrows.offsetHeight) - parseInt(content.offsetHeight)));
	if (vscroll.style.display == "none") {
		maxNegX += vscroll.offsetWidth;
		maxNegY = 0;
	}
	if (hscroll.style.display == "none") {
		maxNegY += hscroll.offsetHeight;
		maxNegX = 0;
	}
	
	var x = Math.max(maxNegX,Math.min(0,Math.floor(xpos)));
	var y = Math.max(maxNegY,Math.min(0,Math.floor(ypos)));
	
	rows.style.left = x + 'px';
	rows.style.top = y + 'px';
	
	if(headers) {
		headers.style.left = rows.style.left;
	}
}

/* END: LISTBOX */

/* BEGIN: TEXTCONTROL */

// TextControl/framework.js
//
// Utility class for working with input-based TextControls
//
// © 2010 REAL Software Inc. -- All Rights Reserved
// This code contains patent-pending technology.

function textcontrol (target,events)
{
	textcontrol.baseConstructor.call(this,target,events);
	this.mBaseStyle = "textcontrol";
	var cid = this.controlID;
	RS.events.addListener(this.field(),"focus",function () { RS.input.setFocusControl(RS.controls[cid]) },false);
	RS.events.addListener(this.field(),"blur",function () { RS.input.setFocusControl(null) },false);
	this.catchMouseEvents();
}
frameworkSubclass(textcontrol, frameworkObject);
textcontrol.prototype.mDelayTimer = 0;

textcontrol.prototype.action = function ()
{
	if (this.implementedEvents.indexOf('TextChanged') > -1) {
		RS.comm.triggerEvent(this.controlID,'TextChanged');
	}
};

textcontrol.prototype.field = function ()
{
	return this.object().children[0];
};

textcontrol.prototype.value = function ()
{
	return this.field().value;
};

textcontrol.prototype.setValue = function (input)
{
	this.field().value = input;
};

textcontrol.prototype.setAppearance = function (enabled)
{
	if (enabled === true) {
		this.field().disabled = false;
	} else {
		this.field().disabled = true;
	}
};

textcontrol.prototype.focus = function ()
{
	this.field().focus();
};

textcontrol.prototype.setStyle = function (value)
{
	var className;
	if (value === null) {
		className = "";
	} else {
		className = value;
	}
	if (this.mBaseStyle !== "") {
		if (className !== "") {
			className = this.mBaseStyle + " " + className;
		} else {
			className = this.mBaseStyle;
		}
	}
	this.field().className = className;
};

textcontrol.prototype.insertText = function (position, value)
{
	var text = this.field().value;
	var pretext = text.substring(0,position - 1);
	var posttext = text.substring(position - 1);
	this.field().value = pretext + value + posttext;
};

textcontrol.prototype.keyDown = function (event)
{
	this.fireAction();
	return false;
};

textcontrol.prototype.keyUp = function (event)
{
	this.fireAction();
	return false;
};

textcontrol.prototype.fireAction = function() {
	markControlChanged(this);
	if (this.mDelayTimer !== 0) {
		clearTimeout(this.mDelayTimer);
		this.mDelayTimer = 0;
	}
	if (RS.comm.websockets.connected() === true) {
		this.mDelayTimer = setTimeout("RS.controls['" + this.controlID + "'].action()",100);
	} else {
		this.mDelayTimer = setTimeout("RS.controls['" + this.controlID + "'].action()",1000);
	}
};

textcontrol.prototype.setType = function(fieldType) {
	try {
		this.field().type = fieldType;
		this.catchMouseEvents();
	} catch(ex) {
		
	}
};

textcontrol.prototype.catchMouseEvents = function() {
	if (this.implementedEvents.indexOf('MouseUp') == -1) {
		var fieldType = this.field().type.toLowerCase();
		var that = this;
		switch(fieldType) {
		case "number":
			RS.events.addListener(this.field(),"mouseup",function(event) { textcontrol.prototype.touchEnd.call(that,event); },false);
			break;
		default:
			RS.events.removeListener(this.field(),"mouseup",function(event) { textcontrol.prototype.touchEnd.call(that,event); },false);
			break;
		}
	}
}

textcontrol.prototype.touchEnd = function(event)
{
	var fieldType = this.field().type.toLowerCase();
	if(fieldType == "number") {
		if (this.implementedEvents.indexOf('TextChanged') > -1) {
			markControlChanged(this);
			RS.comm.triggerEvent(this.controlID,'TextChanged');
		}
	}
};

/* END: TEXTCONTROL */

/* BEGIN: CHECKBOX */

// CheckBox/framework.js
//
// Utility class for working with checkboxes
//
// © 2010 REAL Software Inc. -- All Rights Reserved
// This code contains patent-pending technology.

function checkbox (target, events)
{
	checkbox.baseConstructor.call(this,target,events);
	var box = document.getElementById(this.controlID + '_box');
	if (box) {
		if (box.checked) {
			this.mValue = true;
		} else {
			this.mValue = false;
		}
	}
	var cid = this.controlID;
	RS.events.addListener(box,"focus",function () { RS.input.setFocusControl(RS.controls[cid]) },false);
	RS.events.addListener(box,"blur",function () { RS.input.setFocusControl(null) },false);
}
frameworkSubclass(checkbox, frameworkObject);
checkbox.prototype.mValue = false;

checkbox.prototype.toggle = function ()
{
	var box = document.getElementById(this.controlID + '_box');
	if (!box) {
		return;
	}

	if (box.disabled === true) {
		return;
	}
	
	this.setValue(!this.value(),false);
};

checkbox.prototype.value = function ()
{
	return this.mValue;
};

checkbox.prototype.setValue = function (input,silent)
{
	if (typeof(silent) == 'undefined') {
		silent = false;
	}
	var box = document.getElementById(this.controlID + '_box');
	if (input === true) {
		box.checked = true;
		box.value = '1';
		this.mValue = true;
	} else {
		box.checked = false;
		box.value = '0';
		this.mValue = false;
	}
	if (silent === false) {
		markControlChanged(this);
		if (this.implementedEvents.indexOf('ValueChanged') > -1) {
			RS.comm.triggerEvent(this.controlID,'ValueChanged');
		}
	}
};

checkbox.prototype.caption = function ()
{
	var caption = document.getElementById(this.controlID + '_caption');
	return caption.innerHTML;
};

checkbox.prototype.setCaption = function (input)
{
	var caption = document.getElementById(this.controlID + '_caption');
	caption.innerHTML = input;
};

checkbox.prototype.enabled = function ()
{
	var box = document.getElementById(this.controlID + '_box');
	if (box.disabled === true) {
		return false;
	} else {
		return true;
	}
};

checkbox.prototype.setEnabled = function (input)
{
	var box = document.getElementById(this.controlID + '_box');
	
	if (input === true) {
		box.disabled = false;
	} else {
		box.disabled = true;
	}
	
	this.setDisabledAppearance(!input);
	this.mEnabled = input;
};

/* END: CHECKBOX */

/* BEGIN: RADIOGROUP */

// RadioGroup/framework.js
//
// Utility class for working with radiogroups
//
// © 2010 REAL Software Inc. -- All Rights Reserved
// This code contains patent-pending technology.

function radiogroup (target, events)
{
	radiogroup.baseConstructor.call(this,target,events);
}
frameworkSubclass(radiogroup, frameworkObject);
radiogroup.prototype.selectedRow = -1;
radiogroup.prototype.selectedColumn = -1;

radiogroup.prototype.setCell = function (row, column)
{
	this.setValue(row + "," + column);
};

radiogroup.prototype.value = function ()
{
	return this.selectedRow + "," + this.selectedColumn;
};

radiogroup.prototype.setValue = function (input)
{
	var parts = input.split(",");
	var row = parts[0];
	var column = parts[1];
	var radio = document.getElementById(this.controlID + "_radio_" + row + "_" + column);
	
	if ((!radio) || (radio.disabled === true)) {
		return;
	}
	
	radio.checked = true;
	this.selectedRow = row;
	this.selectedColumn = column;
	markControlChanged(this);
	if (this.implementedEvents.indexOf('SelectionChanged') > -1) {
		RS.comm.triggerEvent(this.controlID,'SelectionChanged');
	}
};

radiogroup.prototype.setEnabled = function (value)
{
	var radio, caption;
	var rowID,columnID;
	
	this.mEnabled = (value === true);
	
	rowID = 0;
	columnID = 0;
	do {
		radio = document.getElementById(this.controlID + "_radio_" + rowID + "_" + columnID);
		caption = document.getElementById(this.controlID + "_caption_" + rowID + "_" + columnID);
		if ((!radio) || (!caption)) {
			if (columnID === 0) {
				break;
			} else {
				columnID = 0;
				rowID++;
				continue;
			}
		}
		
		if(value) {
			RS.DOM.removeClass(caption,'disabled');
		} else {
			RS.DOM.addClass(caption,'disabled');
		}
		
		this.drawCellEnabled(rowID,columnID,value);
		
		columnID++;
	} while (true !== false);
};

radiogroup.prototype.setCellCaption = function (row, column, caption)
{
	var captionCell = document.getElementById(this.controlID + "_caption_" + row + "_" + column);
	if (!captionCell) {
		return;
	}
	
	captionCell.innerHTML = caption;
};

radiogroup.prototype.setCellVisible = function (row, column, visible)
{
	var radio = document.getElementById(this.controlID + "_radio_" + row + "_" + column);
	var captionCell = document.getElementById(this.controlID + "_caption_" + row + "_" + column);
	if ((!captionCell) || (!radio)) {
		return;
	}
	
	var display;
	if (visible === true) {
		display = 'visible';
	} else {
		display = 'hidden';
	}
	radio.style.visibility = display;
	captionCell.style.visibility = display;
};

radiogroup.prototype.setCellEnabled = function (row, column, enabled)
{
	var enabledInput = document.getElementById(this.controlID + "_enabled_" + row + "_" + column);
	if (!enabledInput) {
		return;
	}
	
	if (enabled) {
		enabledInput.value = 'true';
	} else {
		enabledInput.value = 'false';
	}
	
	this.drawCellEnabled(row,column,enabled);
};

radiogroup.prototype.drawCellEnabled = function (row, column, enabled)
{
	var radio = document.getElementById(this.controlID + "_radio_" + row + "_" + column);
	var captionCell = document.getElementById(this.controlID + "_caption_" + row + "_" + column);
	var enabledInput = document.getElementById(this.controlID + "_enabled_" + row + "_" + column);
	
	if ((!captionCell) || (!radio) || (!enabledInput)) {
		return;
	}
	
	enabled = (enabled) && (this.mEnabled) && (enabledInput.value == 'true');
	
	if (enabled) {
		radio.disabled = false;
		captionCell.style.opacity = '1.0';
	} else {
		radio.disabled = true;
		captionCell.style.opacity = '0.5';
	}
};

/* END: RADIOGROUP */

/* BEGIN: CONTAINER */

// Container/framework.js
//
// Utility class for working with ContainerControls
//
// © 2010 REAL Software Inc. -- All Rights Reserved
// This code contains patent-pending technology.

function container (target, events)
{
	container.baseConstructor.call(this,target,events);
	
	//Add some listeners to make the touch experience a little better
	try {
		var content = this.object();
		content.addEventListener('touchstart',this, false);
		content.addEventListener('touchmove',this,false);
		content.addEventListener('touchend',this,false);
		content.addEventListener('touchcancel',this,false);
	} catch(err) {
		//If it can't handle addEventListener, it's probably not iOS or Android
	}
}

frameworkSubclass(container, frameworkObject);

container.prototype.setVisible = function (value)
{
	if (value) {
		this.object().style.display = 'block';
	} else {
		this.object().style.display = 'none';
	}
};

container.prototype.visible = function ()
{
	if (this.object().style.display == 'none') {
		return false;
	} else {
		return true;
	}
};

//methods for iOS and Android
container.prototype.handleEvent = function(event) {
	if(typeof(this[event.type]) === "function") {
		return this[event.type](event);
	}
}

container.prototype.touchstart = function(event) {
	//Record the start point
	var fingerOne = event.targetTouches[0];
	this.touchObj = { touchStartX: fingerOne.pageX,	touchStartY:fingerOne.pageY, startX: this.object().scrollLeft, startY: this.object().scrollTop, touchMoved: false }
};

container.prototype.touchmove = function(event) {
	event.preventDefault();
	console.log()
	//Move the list
	var fingerOne = event.targetTouches[0];
	var curX = fingerOne.pageX;
	var curY = fingerOne.pageY;
	this.touchObj.dx = curX - this.touchObj.touchStartX;
	this.touchObj.dy = curY - this.touchObj.touchStartY;
	this.object().scrollLeft = (this.touchObj.startX - this.touchObj.dx);
	this.object().scrollTop = (this.touchObj.startY - this.touchObj.dy);
	this.touchObj.touchMoved = true;
	this.touchObj.moveStart = (new Date()).getTime();
};

container.prototype.touchend = function(event) {
	if(!this.touchObj.touchMoved) {
		RS.input.touchHandler(event);
	}
	//Clean up the variables we created
	delete this.touchObj;
};

/* END: CONTAINER */
