/**
*	StageElement JavaScript Class
*	
*	Defines a superclass that contains basic document handling functions
*	for use by subclasses.
*/	

function StageElement(container){
	//	If container element passed as string, get reference 
	//	to element in current document that has that id
	if(typeof container == "string") {container = document.getElementById(container);}
	//	If container not specified, then just use document
	if(!container) container = document;
	
	this.container = container;
	this.messages = "";
	this.id="StageElement";
	
	this.disableLog=false;
	this.logLoaded=false;
	
	//	Set variables about what kind of browser we have
	//	Check browser capabilities and set variables
	this.ie4=document.all;
	this.net4=document.layers;
	this.dom=(document.getElementById?true:false);
	
	//	Used to register global references to objects
	this.globalCallbackObject;
	this.GLOBAL_CALLBACK_NAME = "__globa1ca11bobj";
	
	//
	//	Add methods
	this.disableLogging=stageelement_disableLogging;
	this.createLogger=stageelement_createLogger;
	this.getTwoDigitNumber = stageelement_getTwoDigitNumber;
	this.getScreenHeight = stageelement_getScreenHeight;
	this.getScreenWidth = stageelement_getScreenWidth;
	this.getScrollHorizontal=stageelement_getScrollHorizontal; 
	this.getScrollVertical=stageelement_getScrollVertical;
	this.buildUniqueId = stageelement_buildUniqueId;
	this.findObj=stageelement_findObj;
	this.showObj=stageelement_showObj;
	this.hideObj=stageelement_hideObj;
	this.collapseObj=stageelement_collapseObj;
	this.unCollapseObj=stageelement_unCollapseObj;
	this.getContainer=stageelement_getContainer;
	this.getParent=stageelement_getParent;
	this.build=stageelement_build;
	this.buildGetDataNode=stageelement_buildGetDataNode;
	
	this.addTimeoutCall=stageelement_addTimeoutCall;
	this.addIntervalCall=stageelement_addIntervalCall;
	this.getGlobalCallbackStr=stageelement_getGlobalCallbackStr;
	this.getGlobalCallbackObject=stageelement_getGlobalCallbackObject;
	
	this.log=null;
	this.createLogger();
	}

/**
*	disableLogging
*	Can be called to disable logging by any class that extends
*	this class, if set to true, any log messages will be ignored.
*/
function stageelement_disableLogging(disable){this.disableLog=disable;}

/**
*	createLogger
*	Initially creates logger stub, which will be replaced by 
*	log4javascript logger if/when it loads.
*/
function stageelement_createLogger(){
	if(this.logLoaded == false){
		if((!this.ignoreLogs) && (typeof log4JsLogger == "object")){
			//alert("log for js logger created");
			//	Write logs that were saved while logger 
			//	was loaded to logger
			if(this.log!==null)this.log.writeSavedLogs(log4JsLogger);
			this.log=log4JsLogger;
			this.logLoaded = true;
			}
		else if(this.log === null){
			if(this.disableLog){
				//	Just load temp logger
				this.logLoaded = true;
			}
			this.log = new TempLogger(this,this.disableLog);
			return;
			}
		}
}	//	createLogger

/**
*	getTwoDigitNumber
*	Basic helper function accepts a number and returns a string that
*	has added zeros to make it 2 digits long.
*/
function stageelement_getTwoDigitNumber(numb){
	if(!numb) numb = 0;
	var nStr = "0";
	
	if(numb < 10) nStr += new String(numb);
	else nStr = new String(numb);
	
	return nStr;
}	//	getTwoDigitNumber

/**
*	getScreenHeight
*	Returns the screen height.
*/
function stageelement_getScreenHeight(){
	if(this.net4)return window.innerHeight;
	else if(this.ie4)return window.document.body.clientHeight;
	if(this.dom)return window.innerHeight;//	Dom except IE
	return 0;
}	//	getScreenHeight

/**
*	getScreenWidth
*	Returns the screen width.
*/
function stageelement_getScreenWidth(){
	if(this.net4)return window.innerWidth;
	if(this.ie4)return document.body.clientWidth;
	if(this.dom)return window.innerWidth;//	Dom except IE
	return 0;
}	//	getScreenWidth

/**
*	getScrollHorizontal
*	Returns the amount the window is scrolled horizontally.
*/
function stageelement_getScrollHorizontal(){
	if(window.pageXOffset){
		return window.pageXOffset;
		}
	else if(document.body.scrollLeft){
		return document.body.scrollLeft;
		}
	return 0;
}	//	getScrollHorizontal

/**
*	getScrollVertical
*	Returns the amount the window is scrolled vertically.
*/
function stageelement_getScrollVertical(){
	if(window.pageYOffset){
		return window.pageYOffset;
		}
	else if(document.body.scrollTop){
		return document.body.scrollTop;
		}
	return 0;
}	//	getScrollVertical

/**
*	buildUniqueId
*	Builds a unique property name for an associative array object, 
*	takes an object, and a base name, and adds numbers to
*	the base name until there are no properties in the object
*	that have the same name, returns the unique name.
*/
function stageelement_buildUniqueId(object, baseName){
	if(!object && (typeof object!="object"))return null;
	if(!object[baseName])return baseName;
	var numb=0;
	var name;
	while (numb < 200){
		name=baseName+this.getTwoDigitNumber(numb);
		if(!object[name])break;
		numb++;
		}
	return name;
}	//	buildUniqueId


/**
*	findObj
*	Finds the object specified in the passed string and returns a reference 
*	to the layer, if an object is passed, it simply returns the object.
*/
function stageelement_findObj(obj){
	if(obj && (typeof obj == "object")){return obj;}
	if(typeof obj != "string"){return null;}
	
	var doc=document;
	
	if((this.container.all)||(this.container.getElementById))doc=this.container;
	
	//	Try the easy methods good for most browsers
	if(doc.all){return doc.all[obj];}
	if(doc.getElementById){return doc.getElementById(obj);}
	//	If global findObject function defined, use it
	else if(findObject && (typeof findObject == "function")){return findObject(this.container,obj);}
	return null;
}	//	findObj


/**
*	Shows a dom object.
*/
function stageelement_showObj(obj){
	if(this.net4){obj.visibility="show";}
	else if(this.ie4||this.dom){
		if(obj.style){
			obj.style.visibility="visible";
			}
		}
}	//	showObj

/**
*	hideObj
*	Hides a dom object.
*/
function stageelement_hideObj(obj){
	if(this.net4)obj.visibility="hide";
	else if(this.ie4||this.dom){
		if(obj.style){
			obj.style.visibility="hidden";
			}
		}
}	//	hideObj

/**
*	collapseObj
*	shows/hides a dom object, when hidden the object is removed from positioning, 
*	which causes objects below it in pagination to move up.
*/
function stageelement_collapseObj(obj){
	if(this.ie4||this.dom){
		obj.style.display="none";
		}
}	//	collapseObj

/** 
*	unCollapseObj
*	See collapseObj notes.
*/
function stageelement_unCollapseObj(obj,display){
	if(!display)display="block";
	if(this.ie4||this.dom){
		obj.style.display=display;
		}
}	//	unCollapseObj

/**
*	getContainer
*	Returns a reference to the container used by this object.
*/
function stageelement_getContainer(){return this.container;};


/**
*	getParent
*	Returns the parent node of an object that is of a specific tag name
*	and/or has a specific id.  Returns the node when found, or returns null.<b> 
*/
function stageelement_getParent(node,tagName,id){
	if(!node.parentNode){return null;}
	var match=false;
	var hasTagName=false;
	
	if(tagName){
		if(node.tagName==tagName){
			if(id){hasTagName==true;}
			else {return node;}
			}
		}
	else {hasTagName=true;}
	
	if(id){
		if((node.id==id)&&hasTagName){
			return node;
			}
		}
	
	node=node.parentNode;
	
	return stageelement_getParent(node,tagName,id);
}	


/**
*	build
*	Builds code on the fly by combining a code object that is an array of
*	code snippets and variable definitions, with a data object, which contains
*	the values to insert into the code snippets. Code snippets can be strings, 
*	objects which define what property from the data object to insert into the 
*	output, or functions that accept the data object as a parameter and return
*	a string.
*/
function stageelement_build(code, dataObj) {
	var retStr="";var dataCodeItem;var i;
	if(typeof code=="string"){return code;}
	else{if((typeof code != "object") || (!code.length)){return "";}}
	for (i = 0; i < code.length; i++) {
		codeItem = code[i];
		if ((typeof codeItem=="object")&&(codeItem.id)){
			//	have multipart variable definition
			if(codeItem.id.indexOf(".")>0){retStr += this.buildGetDataNode(dataObj, codeItem.id);} 
			else{
				//	check if data object item specified is function, if so call function and add return value
				if(typeof dataObj[codeItem.id] == "function"){retStr += dataObj[codeItem.id]();}
				else{retStr += dataObj[codeItem.id];}
			}
		} 
		else {
			//	If code item is a function, call it and pass data object
			if(typeof codeItem=="function"){retStr += codeItem(dataObj);}
			//	Else just add code item to return string
			else{retStr += codeItem;}
		}
	}
	return retStr;
}	//	build

/**
*	buildGetDataNode
*	Helper function for build() method.
*/
function stageelement_buildGetDataNode(dataObj, id) {
	var idArray = id.split(".");
	var idObj = dataObj;
	if ((typeof idArray == "object") && (idArray.length)) {
		for (var i = 0; i < idArray.length; i++) {
			if (idObj[idArray[i]]) {idObj = idObj[idArray[i]];} 
			else {break;}
		}
	} 
	else{return "";}
	if(typeof idObj == "string") {return idObj;}
	else{return "";}
}	//	buildGetDataNode


/**
*	addTimeoutCall
*	Creates a setTimeout that calls a method of an object after a delay. Saves
*	the object/method call in the globalCallbackObject so the method can
*	execute in the proper scope.  Can also pass a parameter object to the method.
*/
function stageelement_addTimeoutCall(obj,method,delay,params){
	window.setTimeout(this.getGlobalCallbackStr(obj,method,params,true,"timeout"),delay);
}	//	addTimeoutCall


/**
*	addIntervalCall
*	Creates a setInterval that repeatedly calls a method of an object after a delay. 
*	Saves the object/method call in the globalCallbackObject so the method can
*	execute in the proper scope.  Can also pass a parameter object to the method.
*	If a duration is passed, the interval is automatically canceled after the
*	specified duration.  Returns the id of the interval, use window.clearInterval(id) 
*	with the returned id to stop the interval call if a duration is not set.
*/
function stageelement_addIntervalCall(obj,method,delay,params,duration){
	//this.log.info("setInterval(" + obj + ", " + method + "," + delay + "," + params + "," + duration+")");
	var id=window.setInterval(this.getGlobalCallbackStr(obj,method,params,false,"interval"),delay);
	if(duration && !isNaN(parseInt(duration))){
		addTimeoutCall(window,"clearInterval",duration,id,true);
		}
	return id;
}	//	addTimeoutCall

function stageelement_clearCallback(id){
	
}	//	

/**
*	getGlobalCallbackStr
*	Creates a string containing a global function call that can call the passed 
*	object's method with the included parameters.  Used in addTimeoutCall() and
*	addIntervalCal() methods.  Can be used to register an out of scope call to an 
*	object without having to save a global variable reference to that object.
*/
function stageelement_getGlobalCallbackStr(obj,method,params,clear,basename){
	if(!basename){basename="callback";}
	var cbo=this.getGlobalCallbackObject();
	var id=this.buildUniqueId(cbo, basename);
	cbo[id]={obj:obj,method:method,params:params,clear:clear};
	//this.log.warn("global call back object is " + this.GLOBAL_CALLBACK_NAME+".callFunction('"+id+"')");
	return "document['"+this.GLOBAL_CALLBACK_NAME+"'].callFunction('"+id+"')";
}	//	getGlobalCallbackStr


/**
*	getGlobalCallbackObject
*	Returns a reference to the global callback object. This object functions
*	as a singleton, only one should be created per document.
*/
function stageelement_getGlobalCallbackObject(){
	if(this.globalCallbackObject != null){
		return this.globalCallbackObject;
		}
	if((document[this.GLOBAL_CALLBACK_NAME] === undefined) || (document[this.GLOBAL_CALLBACK_NAME] === null)){
		document[this.GLOBAL_CALLBACK_NAME] = new Object();
		//	Create global callback object function that facilitates 
		//	calling a method on a reference to an object
		document[this.GLOBAL_CALLBACK_NAME].callFunction=function(id){
			if(this[id]){
				if(this[id].obj){
					this[id].obj[this[id].method](this[id].params);
					}
				else if(typeof this[id].method=="function"){
					this[id].method(this[id].params);
					}
				if(this[id].clear){
					this[id]=null;
					}
				}
			}
		document[this.GLOBAL_CALLBACK_NAME].clearFunction=function(id){
			if(this[id]){
				this[id]=null;
				}
			}
		}
	this.globalCallbackObject=document[this.GLOBAL_CALLBACK_NAME];
	//this.log.warn("Returning " + this.globalCallbackObject);
	return this.globalCallbackObject;
}	//	getGlobalCallbackObject


//
//	Temp stub logger, used while log4javascript logger loads,
//	if passed true to constructor, it will not keep logged messages
function TempLogger(parent,ignoreLogs){
	this.parent=parent;
	this.msgs={debug:[],info:[],warn:[],error:[],fatal:[],trace:[]};
	this.msgs.info.push("loaded temporary logger..");
	this.ignoreLogs=(ignoreLogs?ignoreLogs:false);
	
	this.debug=tLog_debug;
	this.info=tLog_info;
	this.warn=tLog_warn;
	this.error=tLog_error;
	this.fatal=tLog_fatal;
	this.trace=tLog_trace;
	this.writeSavedLogs=tLog_writeSavedLogs;
	this.writeLogs=tLog_writeLogs;
	}

function tLog_debug(msg){if(!this.ignoreLogs){this.msgs.debug.push(msg);this.parent.createLogger();}}
function tLog_info(msg){if(!this.ignoreLogs){this.msgs.info.push(msg);this.parent.createLogger();}}
function tLog_warn(msg){if(!this.ignoreLogs){this.msgs.warn.push(msg);this.parent.createLogger();}}
function tLog_error(msg){if(!this.ignoreLogs){this.msgs.error.push(msg);this.parent.createLogger();}}
function tLog_fatal(msg){if(!this.ignoreLogs){this.msgs.fatal.push(msg);this.parent.createLogger();}}
function tLog_trace(msg){if(!this.ignoreLogs){this.msgs.trace.push(msg);this.parent.createLogger();}}
function tLog_writeSavedLogs(logger){
	for(type in this.msgs){this.writeLogs(logger,type,this.msgs[type]);}
}
function tLog_writeLogs(logger,type,msgs){
	for(var i=0; i<msgs.length; i++){logger[type](msgs[i]);}
	}

