//___________________________________________________________________________
Window.prototype.superceedCall = function(call_func,delay_ms,callId_int){
// creates a time buffer before calling a function. If another request is called
// with the same bufferID during interim, buffer is reset and original call is 
// cancelled

	if(callId_int){
		window.clearTimeout(callId_int);	
	}
	return window.setTimeout(call_func,delay_ms);
};
//___________________________________________________________________________
String.prototype.parseURIQuery = function(){
// parses the query section of a URI string.
// Returns an object containing each argument in a name-value pair

	var args				= new Object;
	var searchString		= this;
	var queryMatch_arr		= searchString.match(/\?([^$]*)/);
	if(queryMatch_arr){
		var queryString		= queryMatch_arr[0];
		while(queryString){
			var match_arr		= queryString.match(/^[&?]([^&=]*)=([^&]*)([^$]*)/);
			args[match_arr[1]] 	= match_arr[2];
			queryString			= match_arr[3];
		}
	}
	return args;
};
//___________________________________________________________________________
Math.sign = function(number){
	
	return number/Math.abs(number);
};
//___________________________________________________________________________
String.prototype.trim = function(){
// returns the string but with whitespace at start and end removed

	return 	this.match(/(\s*)(.*[^\s])(\s*$)/)[2];
};
//___________________________________________________________________________
Element.prototype.removeChildNodes = function removeChildNodes(){

	while(this.childNodes[0]){
		var node		= this.childNodes[0].removeFromParent();
		delete node;
	}
};
//___________________________________________________________________________
Element.prototype.createChild = function (tagName_str,content_str){

	var child_xml			= this.ownerDocument.createElement(tagName_str);
	this.appendChild(child_xml);
	
	if(content_str){
		var content_txt		= this.ownerDocument.createTextNode(content_str);
		child_xml.appendChild(content_txt);
	}
	return child_xml;
};
//___________________________________________________________________________
HTMLElement.prototype.appendBorders = function(imgPath_str,borderClass_str){
	
	this.setAttribute('borderedBox',true);
	var blankImgSrc_str		= window.transparentImgSrc_str;
	var imgContainer_h		= this.createChild('div','borderImageContainer '+borderClass_str);
	var imgClass_arr		= new Array('topCentre','topRight','centreRight','bottomRight',
									'bottomCentre','bottomLeft','centreLeft','topLeft');
	
	for(var index=0; index<imgClass_arr.length;index++){
		var imgClass_str	= imgClass_arr[index];
		var img_h			= imgContainer_h.createChild('img',imgClass_str);
		img_h.src			= imgPath_str+imgClass_arr[index]+'.png';
	}
	
	// a bug in firefox stops the images from detecting changes in the size of their 
	// surrounding box so we give them a little nudge to encourage them to move
	window.app.addEventListener('redraw',function(){
		
		imgContainer_h.parentNode.style.border 		= '1px solid transparent';
		imgContainer_h.parentNode.style.borderWidth = '0px';
	});
	//-----------------------------------------------------------------------
};
//___________________________________________________________________________
HTMLElement.prototype.createChild = function (tagName_str,class_str,id_str,innerHTML_str){

	var child_h				= document.createHElement(tagName_str,class_str,id_str,innerHTML_str);
	this.appendChild(child_h);
	return child_h;
};
//___________________________________________________________________________
HTMLDocument.prototype.createHElement = function(tagName_str,class_str,id_str,innerHTML_str){
	
	var element_h			= document.createElement(tagName_str);
	if(class_str){
		element_h.className	= class_str;
	}
	if(id_str){
		element_h.id		= id_str;
	}
	if(innerHTML_str){
		element_h.innerHTML	= innerHTML_str;
	}
	return element_h;
};
//___________________________________________________________________________
DocumentFragment.prototype.createChild = function(tagName_str,class_str,id_str){
	
	var child_h				= document.createHElement(tagName_str,class_str,id_str);
	this.appendChild(child_h);
	return child_h;
};
//___________________________________________________________________________
Array.flip = function flip(array_arg,generateKeys){
/* 
	switches each array element with its key
 	if generateKeys==true the function puts keys into elements and 
 	generates new sequential keys for those elements
*/
	var keyValue, index		=-1,key,newKey;
	var newArray			= new Array();
	var cellValue;
	for(key in array_arg){
		cellValue			= array_arg[key];
		if(typeof(cellValue)!='function'){
			newKey				= generateKeys? ++index: cellValue;
			newArray[newKey]	= key;
		}
	}
	return newArray;
}
//___________________________________________________________________________
Array.normalize = function normalize(array_arg){
// removes blank cells from an array
// can't be added as a prototype function as is then enumerated

	var index			= 0;
	do{
		var currCell	= array_arg[index];
		if(currCell){
			index++;
		}else{
			array_arg.splice(index,1);
		}
	}while(index<array_arg.length);
};
//___________________________________________________________________________
Array.unique = function unique(array_arr){
// removes repetitions from an indexed array

	var temp_arr		= new Array();
	var outArr_arr		= new Array();
	for(var index=0; index<array_arr.length; index++){
		var value		= array_arr[index];
		if(!temp_arr.value){
			outArr_arr.push(value);
			temp_arr[value] = true;
		}
	}
	return outArr_arr;
};
//___________________________________________________________________________
Array.enforce = function enforce(possibleArray){
// switches a scalar to an array. If not scalar then does nothing

	var arrayOut			= Array.detect(possibleArray)? possibleArray: new Array(possibleArray);
	return arrayOut;
};
//___________________________________________________________________________
Array.unenforce = function unenforce(arrayIn){
// converts an array with a single value back to a scalar

	var returnVal			= arrayIn.length>1? arrayIn: arrayIn[0];
	returnVal				= returnVal? returnVal: null;
	return returnVal;
};
//___________________________________________________________________________
Array.detect = function detect(possibleArray){
// converts an array with a single value back to a scalar

	if(possibleArray){
		var isArray			= false;
		if(typeof(possibleArray)=='object' && possibleArray.hasOwnProperty('length')){
			isArray			= true;
		}
	}else{
		isArray				= false;
	}
	return isArray;
};
//_____________________________________________________________
Array.containsValue = function(array_arr,value){
	
	var valueIndex_int		= false;
	for(var index=0; index<array_arr.length; index++){
		if(array_arr[index] == value){
			valueIndex_int	= index;
			break;
		}
	}
	return valueIndex_int;
};
//_____________________________________________________________
Array.areDifferent = function(array1_arr,array2_arr){
// determines whether two array have the same contents (in whatever order)	

	var different_bool		= false;
	if(array1_arr.length!=array2_arr.length){
		different_bool		= true;
	}else{
		// create a hash of one array and compare it to the second
		var hash_obj		= new Object();
		for(var index=0; index<array1_arr.length; index++){
			hash_obj[array1_arr[index]]	= true;
		}
		
		// compare array2_arr with hash of array1_arr
		for(index=0; index<array2_arr.length; index++){
			if(!hash_obj[array2_arr[index]]){
				different_bool = true;
				break;
			}
		}
	}
	return different_bool;
};
//_____________________________________________________________
Node.prototype.removeFromParent = function(){
// deletes the element from its parent	
	
	var child_node = this.parentNode.removeChild(this);
	delete child_node;
};
//___________________________________________________________________________
Element.prototype.xPathQuery = function xPathQuery(query_str,abbrResultType,namespaceResolver,result){
	
	// look for namespace resolver on document
	if(!namespaceResolver && this.ownerDocument.hasOwnProperty('namespaceResolver_func')){
		namespaceResolver = this.ownerDocument.namespaceResolver_func;
	}
	
	// define query
	var xPathQuery	= query_str;			// get rid of this change of variable
	
	// define optional parameters
	namespaceResolver	= namespaceResolver? namespaceResolver: null;
	result				= result? result: null;
	
	var resultType		= getResultType(abbrResultType);
	var xResult 		= this.ownerDocument.evaluate(xPathQuery, this, namespaceResolver, resultType, result);
	
	//<hack>
	// am not entirely sure what the proper way to test whether the query succeeded
	// so am just sucking and seeing
	if(abbrResultType=='firstNode'){
		try{
			xResult		= xResult? xResult.iterateNext() : null;
		}catch(e){
			xResult		= false;	
		}
	}
	//</hack>
	
	// see end of function for return value |||
	
	//---------------------------------------------------------
	function translateResult(result_xpr,abbrResultType){
		
		switch(abbrResultType){
			case 'string':
				var result	= result_xpr.stringValue;
				break;
			case 'number':
				result		= result_xpr.numberValue;
				break;
			default:
				result	= result_xpr;
		}
		return result;
	};
	//---------------------------------------------------------
	function getResultType(abbrResultType){
		
		var resultType;
		switch(abbrResultType){
			case 'string':
				resultType		= XPathResult.STRING_TYPE;
				break;
			case 'number':
				resultType		= XPathResult.NUMBER_TYPE;
				break;
			case 'boolean':
				resultType		= XPathResult.BOOLEAN;
				break;
			case 'unordered_node_iterator':
				resultType		= XPathResult.UNORDERED_NODE_ITERATOR_TYPE;
				break;
			case 'ordered_node_iterator':
				resultType		= XPathResult.ORDERED_NODE_ITERATOR_TYPE;
				break;
			case 'unordered_node_snapshot':
				resultType		= XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE;
				break;
			case 'ordered_node_snapshot':
				resultType		= XPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
				break;
			case 'any_unordered_node':
				resultType		= XPathResult.ANY_UNORDERED_NODE_TYPE;
				break;
			case 'first_ordered_node':
				resultType		= XPathResult.FIRST_ORDERED_NODE_TYPE;
				break;
			default:
				if(abbrResultType && abbrResultType!='firstNode'){
					throw new Error('Invalid xPathQuery result type: "'+abbrResultType+'"');
				}else{
					resultType	= XPathResult.ANY_TYPE;
				}
				break;
		}
		return resultType;
	};
	//---------------------------------------------------------
	
	return translateResult(xResult,abbrResultType);
};
//___________________________________________________________________________
// add serialisation functionality to XMLDocument
window.xmlSerializer = new XMLSerializer();
//-----------------------------------------
XMLDocument.prototype.saveXML = function saveXML(){
	var xmlString	= window.xmlSerializer.serializeToString(this);
	
	// Serializing documents created within Mozilla does not result in a prologue
	// being attached however documents acquired through xhr do. The function 
	// therefore looks for a prologue and if not present then generates it 
	if(xmlString.search(/^<\?xml/i)==-1){
		xmlString		= '<?xml version="'+this.xmlVersion+'"?>'+xmlString;
	}
	return xmlString;
};
Element.prototype.saveXML = function saveXML(){
	var xmlString	= window.xmlSerializer.serializeToString(this);
	return xmlString;
};
//_____________________________________________________________
