// API Class

function API() {this._init();}
API.prototype = new Object();
API.prototype.toString = function() {return "API";};
API.prototype._init = function() {
	// debug vars
	this.STACK = 1;
	this.WRITE = 2;
	this.CURR = 3;
	this.WARN = 4;
	this.SHOUT = 5;
	this.debugOn = 1;
	this.debugLevel = this.CURR;
	this.writeCall("in API._init");
};

// DEBUG UTILITIES

API.prototype.writeCall = function(message) {
	this._displayDebug("Method Entered: " + message, this.STACK);
};

API.prototype.write = function(message) {
	this._displayDebug(message, this.WRITE);
};

API.prototype.writeArgs = function() {
	var message=""; 
	var a = this.writeArgs.arguments; 
	for(var i=0; i<a.length;i++) {
		this._displayDebug(i + ": " + a[i], this.WRITE);
	}
};

API.prototype.writeCurrent = function(message) {
	this._displayDebug(message, this.CURR);
};

API.prototype.warn = function(message) {
	this._displayDebug("WARNING: " + message, this.WARN);
};

API.prototype.shout = function(message) {
	this._displayDebug(message, this.SHOUT);
};

API.prototype.clearDebug = function() {
	var nMessageArea = this.gebi("debug")
	if(nMessageArea) {nMessageArea.innerHTML = ""}
};

API.prototype._displayDebug = function (message, iPriority) {
	var nMessageArea = this.gebi("debug");
	if(nMessageArea && this.debugOn && (iPriority >= this.debugLevel)) {
		if(message != "clear") {nMessageArea.innerHTML += message + "<br>";}
		else {nMessageArea.innerHTML = ""}
	}
	else if(iPriority == this.SHOUT) {
		alert(message);
	}
};

// getElementById()
API.prototype.gebi = function(sID, oNode) {
	oNode = oNode ? oNode : document;
	return oNode.getElementById(sID);
}

// getElementByTagName()
API.prototype.gebt = function(sID, oNode) {
	oNode = oNode ? oNode : document;
	return oNode.getElementsByTagName(sID);
}


// NODE UTILITIES

// node constructor
API.prototype.newNode = function(sType, sContent) {
	if(sType == "text") {
		return document.createTextNode(sContent);
	}
	else {
		return document.createElement(sType);
	}
}

API.prototype.hasAttribute = function(node, sAttribute) {
	if(node.nodeType != 1) {return undefined;}
	if( (document.all && node[sAttribute]) || (! document.all && node.hasAttribute(sAttribute))) {
		return true;
	}
	else {return false;}
};

API.prototype.getAttribute = function(node, sAttribute) {
	if(this.hasAttribute(node, sAttribute)) {
		return node.getAttribute(sAttribute);
	}
	else {return undefined;}
};

// set an open-ended number of attributes on a node
API.prototype.nSet = function(oNode, sAtt, sValue) {
	for(var i=1; i < this.nSet.arguments.length; i=i+2) {
		oNode.setAttribute(this.nSet.arguments[i], this.nSet.arguments[i+1]);
	}
}

API.prototype.removeAttribute = function(node, sAttribute) {
	if(this.hasAttribute(node, sAttribute)) {
		node.removeAttribute(sAttribute);
	}
};


// NODE/OBJECT UTILITIES

API.prototype.setNodeValue = function(node, sKey, sValue) {
	// attribute
	if( ! this.hasAttribute(node, sKey)) {
		var newAttribute = document.createAttribute(sKey);
		newAttribute.value = sValue;
		node.setAttributeNode(newAttribute);
	}
	else {node.setAttribute(sKey, sValue);}
	// property
	node[sKey] = sValue;
};


// EVENT UTILITIES

API.prototype.setHandlers = function(node, type, obj, sHandler, arg) {
	this.write("in API.setHandlers: sHandler: " + sHandler + " arg: " + arg);
	var args = this.setHandlers.arguments;
	this.setListener(node, type, this.eventsHandler);
	type = "a" + type; 
	if (! node[type])  {
		node[type] =  new Array( {obj: obj, func: sHandler, arg: arg} );
	}
	else{
		node[type].push( {obj: obj, func: sHandler, arg: arg} );
	}
}

API.prototype.eventsHandler = function(evt) {
	var handlers, node, i; 
	evt = (evt) ? evt : ((event) ? event : null);
	// do not use this for node since won't work with code-manufactured events
	var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
	if(node.inactiveNode) {
		// do nothing
	}
	else {
 		handlers = node["a" +  evt.type];
 		var oHandlerInfo, arg;
		for(var i = 0; i < handlers.length; i++) {
			oHandlerInfo = handlers[i];
			arg = (typeof oHandlerInfo.arg == "undefined") ? null : oHandlerInfo.arg;
			if(oHandlerInfo.obj) {
				oHandlerInfo.obj[oHandlerInfo.func](arg, node, evt);
			}
			else {
				//var func = eval(oHandlerInfo.func);
				var func = this.isFunction(oHandlerInfo.func) ? oHandlerInfo.func : eval(oHandlerInfo.func);
				func.call(node, arg, node, evt);
			}
		}
   	 }
};

// get event
API.prototype.getE = function(evt) {
	var evt = (evt) ? evt : ((event) ? event : null);
	if(!evt) {warn("No event"); return 0;}
	return evt;
}

// get event's src obj
API.prototype.getEsrc = function(evt) {
	var evt = this.getE(evt);
	var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
	if(!node) {warn("No event src node"); return 0;}
	return node;
}

// add listener/handler to node
// accepts open-ended number of arguments, but assumes bubble not capture if
// setting more than one listener
API.prototype.setListener = function(oNode, sEvt, fLisnr, bCapture) {
	var a = this.setListener.arguments;
	if(a.length < 5) {
		if(this.isArray(oNode)) {
			var max = oNode.length;
			for(var i = 0; i < max; i++) {
				this.setListener(oNode[i], sEvt, fLisnr, bCapture);
			}
		}
		else if(oNode.addEventListener) {
			oNode.addEventListener(sEvt, fLisnr, bCapture);
		}
		else if(typeof oNode == "object") {
			sEvt = "on" + sEvt;
			this.nSet(oNode, sEvt, fLisnr);
		}
	}
	else {
		for(var i=1; i < a.length; i = i+2) {
			this.setListener(oNode, a[i], a[i+1]);
		}
	}
}


// OBJECT UTILITIES

API.prototype.Object2Array = function(obj) {
	if(this.isArray(obj)) {return obj;}
	if(typeof obj != "object") {
		this.warn("Error: environment::Object2Array passed a non-Object of type: " + typeof obj);
		return;
	}
	var a = new Array();
	for(var key in obj) {
		if(key != "item" && key != "length" && key != "namedItem") {
			a.push(obj[key]);
		}
	}
	return a;
};

API.prototype.deepCopy = function(obj) {
	if( ! this.isObject(obj)) {return obj;}
	if(this.isArray(obj)) {return obj.deepCopy();}
	var oResult = new Object();
	for(var key in obj) {
		oResult[key] = this.deepCopy(obj[key]);
	}
	return oResult;
};


// ARRAY UTILITIES

Array.prototype.deepCopy = function() {
	var aResult = new Array();
	for (var i=0; i<this.length; i++) {
		aResult[i] = api.deepCopy(this[i]);
	}
	return aResult;
};

Array.prototype.shallowCopy = function() {
	var aResult = new Array();
	for (var i=0; i<this.length; i++) {
		aResult[i] = this[i];
	}
	return aResult;
};

// STRING UTILITIES

String.prototype.capitalize = function() {
	var firstCharacter = this.substr(0, 1);
	firstCharacter = firstCharacter.toUpperCase();
	return firstCharacter + this.substr(1);
};


// TYPE UTILITIES

API.prototype.isObject = function(obj) {
	if(typeof obj == "object"  && ! this.isArray(obj)) {return true;}
	else {return false;}
};

API.prototype.isArray = function(obj) {
	if(typeof obj == "object" && obj.push){return true;}
	else {return false;}
}

API.prototype.isFunction = function(obj) {
	if(typeof obj == "function"){return true;}
	else {return false;}
}

// allows leading -, but does not check for things like leading zeros, or negative 0
API.prototype.isInteger = function(str) {
	str = String(str);
	return (str == str.match(/^-{0,1}\d+$/));
}

/////////////////////////////////////////////////////////////////////////////////////////

var api = new API();
