if (typeof Node == "undefined") {
// set some node constants for IE.
var Node = {};
Node.ELEMENT_NODE =1;
Node.ATTRIBUTE_NODE =2;
Node.TEXT_NODE =3;
Node.CDATA_SECTION_NODE =4;
Node.ENTITY_REFERENCE_NODE =5;
Node.ENTITY_NODE =6;
Node.PROCESSING_INSTRUCTION_NODE =7;
Node.COMMENT_NODE =8;
Node.DOCUMENT_NODE =9;
Node.DOCUMENT_TYPE_NODE =10;
Node.DOCUMENT_FRAGMENT_NODE =11;
Node.NOTATION_NODE =12;
}

/** fake console stuff **/

	/**
* grab a variable value from the GET method ( www.example.com/index.html?variable=value&variable2=value2
* @returns The string value of the variable							
* @type String
*/											 
function getQueryVariable(variable) {
  var query = window.location.search.substring(1);
  var vars = query.split("&");
  variable=String(variable).toLowerCase();
  for (var i=0;i<vars.length;i++) {
    var pair = vars[i].split("=");
    if (String(pair[0].toLowerCase()) == variable) {
      return pair[1];
    }
  } 
  return false;
}

if (! Array.prototype.indexOf)
{
	Array.prototype.indexOf = function(value)
	{
		for(var count = 0 ;count < this.length;count++)
			if (value === this[count]) return count;
		return -1;
	}
}
if (!Array.prototype.indexOfNoCase)
{
	Array.prototype.indexOfNoCase = function(value)
	{	
		value = String(value).toUpperCase();
		for (var count =0;count < this.length;count++)
			if (value === String(this[count]).toUpperCase())
				return count;
		return -1;
	}
}
var allowdebug = getQueryVariable('debug');
var debugExceptions = getQueryVariable('debugExceptions');
/** 
* @class a fake console object to mimic the firebug's console object
*/
function FakeConsole(oldConsole)
{
	this.timers = new Object();
	this.counts = new Object();
	if (window.console)
		window.console.log('CREATING FAKE CONSOLE!');
	this.oldConsole = oldConsole;
}


/**
* if there is an inadequate console, store it here
*/
FakeConsole.prototype.oldConsole = false;
FakeConsole.prototype.isFake = true;
/**
* log to the debug window. discards other arguments
*/
FakeConsole.prototype.log = function(value){this.debugout('log: '+this.arg(arguments));}
/**
* info to the debug window. discards other arguments
*/
FakeConsole.prototype.info = function(value){ this.debugout('info: '+this.arg(arguments));}

/**
* warn to the debug window. discards other arguments
*/
FakeConsole.prototype.warn = function(value) { this.debugout('warn: '+this.arg(arguments));}

/**
* error to the debug window. discards other arguments
*/
FakeConsole.prototype.error = function(value) {alert('error: '+this.arg(arguments));}

FakeConsole.prototype.group = function(value) {  this.debugout('group: '+this.arg(arguments));} 
FakeConsole.prototype.groupEnd = function() {this.debugout('groupend');}
FakeConsole.prototype.debug = function(value) {	this.debugout('debug: '+this.arg(arguments));}
/**
* assert to the debug window, if assertion fails (value is false) pop up debugger;
* @param {boolean} value value that should evaluate to not-false
* @param {String} message message to show when assertion fails
*/
FakeConsole.prototype.assert = function(value,message) { if (!value) {alert('assert failure : '+message);} }
/**
* Trace does nothing
*/
FakeConsole.prototype.trace = function(msg) {this.debugout('tried to trace: '+msg)};
/**
* profile does nothing
*/
FakeConsole.prototype.profile = function(msg) { this.debugout('tried to profile: '+msg)};
/**
* does nothing
*/
FakeConsole.prototype.profileEnd = function(msg) { this.debugout('end profile: '+msg)};
/**
* @private
*/
FakeConsole.prototype.timers = false;
/**
* @private
*/
FakeConsole.prototype.counts = false;
/**
* start the timer 'name' (outputs to debug window timer started)
* @param {String} name timer to start
*/
FakeConsole.prototype.time = function(name) { this.debugout((new Date())+':timer '+name +' started '+(this.timers[name] = new Date()));}
/**
* end the timer 'name' (does not destroy the timer)
* @param {String} name 
*/
FakeConsole.prototype.timeEnd = function(name) { this.debugout((new Date())+'timer '+name+' ended '+(new Date() - this.timers[name]));}
/**
* output the number of times this line has been called
*/
FakeConsole.prototype.count = function(name) { if (!this.counts[name]) this.counts[name] = 1;debugout('count: '+name+' executed '+(this.counts[name]++) +' times'); }

FakeConsole.prototype.arg = function(args)
{
	if ((!allowdebug) && (!this.oldConsole)) return '';
	var result = '';
	var dir = '';
	for (var count = 0;count < args.length;count++)
	{
		if (result != '') result += ', ';
		result += args[count];
		try
		{	if ((typeof(args[count]) == 'object') && (String(args[count]) == '[object]'))
				result += '['+this.dirstr(args[count],'{arg'+count+'}')+']';
		}
		catch (ex){}
		dir = '';
	}
	return result;
}
FakeConsole.prototype.dirstr = function(obj,name)
{	
	var result = '';
	if (!name) name = String(obj);
		result += 'fakedir: '+name+' || ';
	try
	{
		for(i in obj) 
			try
			{ 
				result +='.'+i+'={'+obj[i]+'}\n';
			}
			catch (ex) {result +='can\'t inspect .'+i+' '+ex.message;}
	}
	catch (ex2)
	{
		result +='can\'t inspect object '+ex2.message;
	}
	return result;
}
FakeConsole.prototype.dir = function(obj,name) 
{
	console.trace();
	if (name)
		this.debugout('fakedir: '+name);
	else
		this.debugout('fakedir: '+obj);
	try
	{
		for(i in obj) 
			try
			{ 
				this.debugout('.'+i+'='+obj[i]);
			}
			catch (ex) {this.debugout('can\'t inspect .'+i+' '+ex.message)}
	}
	catch (ex2)
	{
		this.debugout('can\'t inspect object '+ex2.message);
	}
}
	
FakeConsole.prototype.debugout = function(value)
{
	if (this.oldConsole)
		this.oldConsole.log(value);
	else if (allowdebug) this.default_debugout(value);
}
if (!window.console) window.console = new FakeConsole();
//safari already has a console, but it doesn't do debug
if (!window.console.debug) window.console = new FakeConsole(window.console);

if (!window.console.dir) window.console.dir = FakeConsole.prototype.dir;
if (!window.console.trace) window.console.trace = FakeConsole.prototype.trace;
if (!window.console.time) window.console.time = FakeConsole.prototype.time;
if (!window.console.timeEnd) window.console.timeEnd = FakeConsole.prototype.timeEnd;
/**
* default debugging function
* creates a new window and outputs debug info there
*/
FakeConsole.prototype.dwindow = false;
FakeConsole.prototype.dtable = false;
FakeConsole.prototype.dcount = 0;

FakeConsole.prototype.default_debugout = function(value) 
{
	if ((window.console)&& (!window.console.isFake))
		editable_row_clog(value);
	else
	{
		var odwindow = this.dwindow;
		if (this.dwindow)
		{
			if (this.dwindow.closed)
				this.dwindow = false;
		}
		if (!this.dwindow)
		{
			this.dwindow = window.open('', 'newWin','scrollbars,resizable,height=500,width=800'); 
			if (this.dwindow)
			{
				this.dtable = this.dwindow.document.createElement('TABLE');
				this.dwindow.document.lastChild.lastChild.insertBefore(this.dtable,this.dwindow.document.lastChild.lastChild.firstChild);
				this.dtable = this.dtable.appendChild(this.dwindow.document.createElement('TBODY'));
				this.dwindow.document.lastChild.lastChild.appendChild(this.dwindow.document.createElement('HR'));
				
				this.dwindow.focus();
			}
			
		}
		if (this.dwindow)
		{
			var tr = this.dwindow.document.createElement('TR');
			var td = tr.appendChild(this.dwindow.document.createElement('TD'));
			td.appendChild(this.dwindow.document.createTextNode(this.dcount));
			td = tr.appendChild(this.dwindow.document.createElement('TD'));
			td.style.width = '500px';
			td.style.overflow='scroll'; 
			td.style.backgroundColor = 'rgb('+0xFE+','+(this.dcount*16+128)+','+(this.dcount*16+128)+')';
			this.dcount++;
			this.dcount = (this.dcount >4?0:this.dcount);
			td.appendChild(this.dwindow.document.createTextNode(value));
			td = tr.appendChild(this.dwindow.document.createElement('TD'));
	
			td.style.width = '500px';
			td.style.overflow='scroll'; 
			td.style.backgroundColor = 'rgb('+(this.dcount*16+128)+','+(this.dcount*16+128)+','+0xFE+')';
			td.innerHTML = value;
			this.dtable.appendChild(tr,this.dtable.firstChild);
		}
		else alert('dwindow not available '+this.dwindow+':::'+value);
	}
}

function popupErrorMessage(message,htmlBody,src)
{
	var wname =  'popupErrorMessage'+String(window.location).replace(/[- \.\/:\?&#\=]/g,'_');
	var dwindow = window.open('',wname,'scrollbars,resizable,height=500,width=1000'); 
	if (dwindow)
	{
		var dtable = dwindow.document.createElement('TABLE');
				dwindow.document.lastChild.lastChild.insertBefore(dtable,dwindow.document.lastChild.lastChild.firstChild);
				dtable = dtable.appendChild(dwindow.document.createElement('TBODY'));
				var tr = dtable.appendChild(dwindow.document.createElement('TR'));
				var td = tr.appendChild(dwindow.document.createElement('TD'));
				td.colSpan = 2;
				td.style.cssText = 'font-size:large';
				td.appendChild(dwindow.document.createTextNode('An ERROR has occured, please save this page and attach it in an email to '));
				var alink = td.appendChild(dwindow.document.createElement('A'));
				td.appendChild(dwindow.document.createElement('BR'));
				td.appendChild(dwindow.document.createTextNode('(right click and select \'save page as\')'));
				td.appendChild(dwindow.document.createElement('BR'));
				td.appendChild(dwindow.document.createTextNode('In the email, explain what you were doing when the error occured'));
				alink.appendChild(dwindow.document.createTextNode('jamie@stoneycreekwinepress.com'));
				alink.href = 'mailto:jamie@stoneycreekwinepress.com';
				if (src)
				{
					td.appendChild(dwindow.document.createElement('BR'));
					var alink = td.appendChild(dwindow.document.createElement('A'));
					alink.appendChild(dwindow.document.createTextNode('Click here to view the error page'));
					alink.href=src;
					alink.target = '_blank';
				}
				var tr = dtable.appendChild(dwindow.document.createElement('TR'));
				var td = tr.appendChild(dwindow.document.createElement('TD'));
				td.colSpan = 2;
				td.style.cssText = 'border:1px solid #CCCCCC';
				td.style.whiteSpace = 'pre';
				td.appendChild(dwindow.document.createTextNode(message));
				tr = dtable.appendChild(dwindow.document.createElement('TR'));
				td = tr.appendChild(dwindow.document.createElement('TD'))
				td.style.cssText = 'border:1px solid red;background:#FFCCAA;vertical-align:top';
				td.innerHTML = htmlBody;
				td = tr.appendChild(dwindow.document.createElement('TD'));
				
				td.style.cssText = 'border:1px solid yellow;background:#AACCFF;vertical-align:top';
				td.appendChild(dwindow.document.createTextNode(htmlBody));
				dwindow.focus();
	}
	else alert('An error happened but I couldn\'t popup a window. Please turn off popup blockers for this site\n'+message);
				
}
/**
* function for a quick debug of an object
*
*/
function specobj(objname,obj)
{
	var outstr = objname+':'+obj+'\n';;
	try
	{
		for(i in obj)
		try
		{
			outstr += objname+'.'+i+' '+obj[i]+'\n';
		}
		catch(ex)
		{	outstr += 'error spec\'ing '+objname+'.'+i+'\n';//+' '+ex.fileName+' : '+ex.lineNumber+' : '+ex2.message+'\n';
		}
	}
	catch (ex2)
	{
		outstr += 'error spec\'ing '+objname+' '+ex2.fileName+' : '+ex2.lineNumber+' : '+ex2.message+'\n';
	}
	return outstr;
}
/**
* function to log to console (safari)
*/
function editable_row_clog(value)
{
	value = value.replace(/\%/g,'\%');
	if (window.console)
	try
	{
		window.console.debug(value);
	}
	catch (ex)
	{
		window.console.warn(ex.fileName+' : '+ex.lineNumber+ ' : '+ex.message);
		try
		{
			window.console.warn(value);
		}
		catch (ex2)
		{
//			debugger;
		}
	}
}



	
/******************************************************\
********************************************************
********* XML STUFF ************************************
********************************************************
\******************************************************/
function XmlHttpItem(sender,cbMethod,xmlDoc)
{
	this.sender = sender;
	this.cbMethod=cbMethod;
	this.xmlDoc = xmlDoc;
}
XmlHttpItem.prototype.tryCount = 0;
XmlHttpItem.prototype.contains = function(sender,cbMethod,xmlDoc)
{
	return ((this.sender == sender) && (this.cbMethod == cbMethod) && (this.xmlDoc == xmlDoc));
}

//attach this method to arrays that contain XmlHttpItems
XmlHttpItem.removeItem = function(sender,cbMethod,xmlDoc)
{
	for (var count = 0;count < this.length;count++)
	{
		if (this[count].contains(sender,cbMethod,xmlDoc))
		{
			console.debug('XmlHttpItem.removeItem found ',sender,cbMethod,xmlDoc);
			this.splice(count,1);
			return true;
		}
	}
	return false;
}
/**
* @class Transaction manager for xml queries. Adds queries together into one query, calls the appropriate response function in each sender when completed.
* 
*/
function XmlHttpManager(xmlRpcSrc)
{


	if (xmlRpcSrc !== undefined)
		this.setSrc(xmlRpcSrc);
	this.sendItems = new Array();
	this.sentItems = new Array();
	this.sendItems.removeItem = XmlHttpItem.removeItem;
	this.sentItems.removeItem = XmlHttpItem.removeItem;
//	console.debug('nextsaveID =' ,this.nextSaveID);
}
XmlHttpManager.prototype.xmlRpcRequest = false;
XmlHttpManager.prototype.nextSaveID = 100;
XmlHttpManager.prototype.holdCount = 0;
XmlHttpManager.prototype.sending = false;
XmlHttpManager.prototype.sendItems = false;
XmlHttpManager.prototype.sentItems = false;
XmlHttpManager.prototype._xmlRpcSrc = '/admin/index.cfm?method=xmlquery.status'

XmlHttpManager.prototype.hold = function()
{
	this.holdCount++;
	console.group('xmlHttpManagerHold'+this.holdCount);
}

XmlHttpManager.prototype.unHold = function()
{
	console.groupEnd('xmlHttpManagerHold'+this.holdCount);
	this.holdCount--;
	this.send();
}
XmlHttpManager.prototype.setSrc = function(newSrc)
{
	if (xmlRpcRequest)
		this.xmlRpcRequest.src = newSrc;
	this._xmlRpcSrc = newSrc;
}

XmlHttpManager.prototype.cancelSend = function(sender,cbMethod,xmlDoc)
{
	console.debug('senditems:',this.sendItems.length,' sentItems:',this.sentItems.length);
	// if it's in the 'to send' queue, just remove it
	
	if (this.sendItems.removeItem(sender,cbMethod,xmlDoc))
		return;
	//if it's not, find it in the sending queue and abort the xmlRpcRequest if possible (only if there is only one item being sent)
	if ((this.sentItems.length == 1) && (this.sentItems[0].contains(sender,cbMethod,xmlDoc)))
	{
		
		this.sentItems.length = 0;
		this.xmlRpcRequest.cancel(true);
		this.sending = false;
		if (this.sendItems.length > 0)
			this.send();	
		return;
	}
	else
	{
		
		if (this.sentItems.removeItem(sender,cbMethod,xmlDoc))
			return;
	}
}

XmlHttpManager.prototype.send = function(sender,cbMethod,xmlDoc)
{
	if (sender)
		this.sendItems.push(new XmlHttpItem(sender,cbMethod,xmlDoc));
	if ((!this.sending) && (this.holdCount <= 0))
	{
		if (!this.xmlRpcRequest)
		{
			this.xmlRpcRequest = new XMLRequest3(this,this.recieve);
			this.xmlRpcRequest.setSrc(this._xmlRpcSrc);
		}
		if (this.sendItems.length > 0)
		{
			this.sending = true;
			var sendingItems = this.sendItems.slice();
			this.sendItems.length = 0;
			var sendDoc = new XmlDoc('REQUEST');
			for (var count = 0;count < sendingItems.length;count++)
			{
				//sometimes si can be null 
				var si = sendingItems[count];
				if ((si) && (si.xmlDoc))
				{
					sendDoc.rootNode.appendChild(sendDoc.importNode(si.xmlDoc.queryElement,true));
					si.tryCount++;
					this.sentItems.push(si);
				}
				else 
				{
					console.debug('sentitems[',count,'] = ',sendingItems[count],'( ',sendingItems,')');
					console.trace();
				}
			}
			sendingItems.length = 0;
			this.xmlRpcRequest.send('value='+escape(Utf8.encode(sendDoc.serializeToString())),sendDoc);
		}
	}
}

XmlHttpManager.prototype.recieve = function()
{
	var recievedDoc = this.createDoc();
	recievedDoc.setXmlDoc(this.xmlRpcRequest.xmlHttpReq.responseXML,this.xmlRpcRequest.xmlHttpReq.responseText);
	var errorCode = this.xmlRpcRequest.xmlHttpReq.status;
	var errorText = this.xmlRpcRequest.xmlHttpReq.statusText;
	for (var count = 0;count < this.sentItems.length;count++)
	{
		var sentItem = this.sentItems[count];
//		console.debug('sent item: ',sentItem,count,'(',this.sentItems.length,')');
		recievedDoc.queryElement = recievedDoc.findChildNode(recievedDoc.firstChild,'QUERY','saveid',sentItem.xmlDoc.saveID);
		if (!recievedDoc.queryElement)
		{
			var qe = recievedDoc.firstChild.firstChild;
			var qestr = '';
			while ((qe) && (qe.getAttribute))
			{
				qestr = qe.getAttribute('saveid')+',';
				qe = qe.nextSibling;
			}
			if ((sentItem.tryCount < 5) && (errorCode != 200))
			{
				this.sendItems.push(sentItem);
			}
			else
			{
				console.warn("couldn't find query element in ",recievedDoc.firstChild,' saveid: ',sentItem.xmlDoc.saveID,'saveids:',qestr);
				popupErrorMessage('Error attempting to contact server ('+errorCode+') '+errorText,this.xmlRpcRequest.xmlHttpReq.responseText,
				   this.xmlRpcRequest.lastSrc);
			}
			
			console.group('responsetext_error_'+errorCode);
			console.debug(sentItem.xmlDoc.serializeToString());
			console.debug('error ',errorCode,errorText);
			//trim the whitespace around the output text (stupid coldfusion)
			//stupid javascript unicode doesn't recognize ascii newline or tab chars
			console.debug(String(this.xmlRpcRequest.xmlHttpReq.responseText).replace(new RegExp('(^[\s\x0D\x0A\x09]+|[\s\x0d\x0a\x09]+$)','g'),'' ));
			console.groupEnd('responsetext_error'+errorCode);
//			console.debug(this.xmlRpcRequest.xmlHttpReq.responseText);
		}
//		else console.debug('result',this.xmlRpcRequest.xmlHttpReq.responseText);
		recievedDoc.queryError = recievedDoc.findChildNode(recievedDoc.queryElement,'ERROR');
		if (recievedDoc.queryError)
		{
			var msg = recievedDoc.queryError.getAttribute('message')+cr()+recievedDoc.queryError.getAttribute('queryError')+cr()+recievedDoc.queryError.getAttribute('sql');
			popupErrorMessage('Error processing XML instructions on the server: '+msg,this.xmlRpcRequest.xmlHttpReq.responseText,this.xmlRpcRequest.lastSrc);
			//if (allowdebug)
				//alert(msg);
			//else
				//console.error(msg);
		}
		if (recievedDoc.queryElement)
		{
			var tableName = 'unknown table'
			if (sentItem.sender.tableName)
				tableName = sentItem.sender.tableName;
//			console.debug('calling callback method. table:',tableName,sentItem.sender,recievedDoc.queryElement);
			if (debugExceptions)
				sentItem.cbMethod.call(sentItem.sender,recievedDoc,sentItem.xmlDoc);
			else
			try
			{
				sentItem.cbMethod.call(sentItem.sender,recievedDoc,sentItem.xmlDoc);
			}
			catch (ex)
			{
				
				console.group('responsetext_error_'+errorCode);
				console.debug('error ',errorCode,errorText);
				console.debug(this.xmlRpcRequest.xmlHttpReq.responseText);
				console.groupEnd('responsetext_error'+errorCode);
				if ((sentItem.tryCount < 5) && (errorCode != 200))
					this.sendItems.push(sentItem);
				else 
				{	
					if (String(ex.message).search(/Component returned failure code: 0x80004005/) == -1)
						popupErrorMessage('Error with sentItem callback ('+errorCode+') '+errorText+'\n'+FakeConsole.prototype.dirstr(ex,'exception'),this.xmlRpcRequest.xmlHttpReq.responseText,this.xmlRpcRequest.lastSrc);
					else 
						this.sendItems.push(this.sentItem);//resend on this error
					console.warn('error with sentItem callback. error:',errorCode,':',errorText,' try count:',sentItem.tryCount,sentItem,ex);
					
					if (allowdebug)
						throw ex;
				}
			}
		}
	}
	this.sentItems.length = 0;
	this.sending = false;
	if (this.sendItems.length > 0) 
		this.send();
}

XmlHttpManager.prototype.createDoc = function()
{
	var result = new XmlDoc('REQUEST');
	result.saveID = this.nextSaveID++;
	return result;
}


var xmlHttpManager = new XmlHttpManager();
/** 
* @class DomDocument wrapper that merges the differences between IE XMLDOM and the standard W3 DOM document.implementation <br>
* Also contains attachment points for common nodes used in the EditableRow framework
* @param {String} rootTag tag name to use as the root of this xml document
* @param {EditableRows} rowset object that created this document
* @param {Function} (optional)completefunc function to call when document is finished loading (not tested)
*/
function XmlDoc(rootTag,rowSet,completefunc)
{
	this.rootTag = rootTag;
	this.completefunc = completefunc;
	this.setXmlDoc(newXmlDoc(rootTag,completefunc));
	this.firstChild = this.rootNode.firstChild;
	this.state = new Array();
	this.extraRowSets = new Array();
	this.rowSet = rowSet;
	//eo(this.XmlDoc.firstChild,'firstchild');
}
	/**
	* Array of extra rowsets that are piggybacking on this xml request
	* @type Array;
	*/
	XmlDoc.prototype.extraRowSets = false
	/**
	* function to execute when loading is complete
	* @type Function
	*/
	XmlDoc.prototype.completefunc = false;
	/**
	* root tag of this document
	* @type String
	*/
	XmlDoc.prototype.rootTag = false;
	/**
	* Owning rowset 
	* @
	*/
	XmlDoc.prototype.rowSet = false;
	/** 
	* FirstChild element of rootNode
	* @type DomNode
	*/
	XmlDoc.prototype.firstChild = false
	
	/**
	* Escape characters in a string to be valid inside an xml attribute
	*/
	XmlDoc.xmlEscape = function(xmlString)
	{
		return String(xmlString).replace('&','&amp;').replace('"','&quot;').replace('<','&lt;').replace('>','&gt;');
	}
	/**
	* returns a string representing the full text content of all child text nodes concatenated (may be very large string, by default textnodes are only 4096 characters?)
	* @type String
	*/
	XmlDoc.prototype.getFullTextContent = function(domNode)
	{
		var result = '';
		var child = domNode.firstChild;
		while (child)
		{
			if (child.nodeType == Node.TEXT_NODE)
				result += child.nodeValue;
			child = child.nextSibling;
		}
		return result;
	}
	/**
	* create a new element belonging to this document
	* @param {String} tagName name of the element that is being created
	*/
	XmlDoc.prototype.createElement = function(tagName) {return  this.xmlDoc.createElement(tagName);}
	/**
	* create a new text node belonging to this document
	* @param {String} nodeText text to fill the textNode with
	*/
	XmlDoc.prototype.createTextNode = function(nodeText) {return this.xmlDoc.createTextNode(nodeText);}
	/**
	* import a node from another xml document
	* @param {DOMNode} node to import
	* @param {bool} deep true if you want to import the node's children as well
	*/
	XmlDoc.prototype.importNode = function(node,deep) 
	{
//		console.debug('importNode',node,deep,node.firstChild);
//		console.assert(xmlDoc.importNode,'Document.importNode doesn\'t exist!');
		try
		{
			return this.xmlDoc.importNode(node,deep);
			//IE 7 all of the sudden has an importNode, but i don't trust it
			// if it fails, call the old fashioned clonenode
		}
		catch (ex)
		{
			return node.cloneNode(deep);
		}
	}
	
	/**
	* transfer the contents of a node into an HTML document (importnode doesn't seem to work)
	*
	*/
	XmlDoc.prototype.xmlToHtml = function(src,dest,trail)
	{
		if (!trail) trail = '';
		trail = trail + '\\'+src.tagName;
		var ddoc = dest.ownerDocument;
		var child = src.firstChild;
		var dchild;
		//conosle.debug(src.nodeType);
		console.debug('copying from ',src,' to ',dest, ' ',src.childNodes.length,' children ', trail, ' firstchild ',child);
		while (child)
		{
			if (child.nodeType == Node.TEXT_NODE)
			{
				//console.debug('copying text node ',child.nodeValue);
				dest.appendChild(ddoc.createTextNode(child.nodeValue));
			}
			else
			{
				//console.debug('adding ',child.tagName);
				dchild = dest.appendChild(ddoc.createElement(child.tagName));
				for (var count = 0;count < child.attributes.length;count++)
				{
					//console.debug('setting attribute ',child.attributes.item(count).name,child.attributes.item(count).value);
					if (child.attributes.item(count).name == 'style')
						dchild.style.cssText = child.attributes.item(count).value;
					else
						dchild.setAttribute(child.attributes.item(count).name,child.attributes.item(count).value);
				}
				this.xmlToHtml(child,dchild,trail);//recursively fill in the child node
			}
			child = child.nextSibling;
		}
		
	}
	/**
	* internal representation of the xml document (implementation dependant)
	* @private
	*/
	XmlDoc.prototype.xmlDoc = false;
	/**
	* Serialize the document into a string for transport
	* @returns A string copy of the document 
	* @type String
	*/
	XmlDoc.prototype.serializeToString = function() { };
	/**
	* The node in the document that represents the root node
	* @type DomNode
	*/
	XmlDoc.prototype.rootNode = false;
	/**
	* place to store the current QUERY element of the document
	* @type DomNode
	*/
	XmlDoc.prototype.queryElement = false;
	/**
	* place to store the current ROWSET element of the document
	* @type DomNode
	*/
	XmlDoc.prototype.rowSetElement = false;
	/**
	* place to store the current element 
	* @type DomNode
	*/
	XmlDoc.prototype.currentElement = false;
	/**
	* place to store column element
	* @type DomNode
 	*/
	XmlDoc.prototype.columnElement = false;
	/**
	* place to store row element
	* @type DomNode
	*/
	XmlDoc.prototype.rowElement = false;
	/**
	* element state stack for the element. saves queryElement rowSetElement and currentElement
	* @see #saveState
	* @see #restoreState
	* @type Array
	*/
	XmlDoc.prototype.state = false;
	/**
	* Save the state of your custom element members here (add them here if you use new ones)
	*/
	XmlDoc.prototype.saveState = function()
	{
		this.state.push({queryElement:this.queryElement,rowSetElement:this.rowSetElement,currentElement:this.currentElement,
					columnElement:this.columnElement,rowElement:this.rowElement});
	}
	/**
	* restore the state of custom element members (add them here too)
	*/
	XmlDoc.prototype.restoreState = function()
	{
		var aState = this.state.pop();
		this.queryElement = aState.queryElement;
		this.rowSetElement = aState.rowSetElement;
		this.currentElement = aState.currentElement;
		this.columnElement = aState.columnElement;
		this.rowElement = aState.rowElement;
	}
	/**
	* add a piggybacking rowset. when calling rowset gets 'saveDone' it will call each piggybacking rowset's {@link EditableRows#saveDoneUpdateRows} method
	* works for {@link EditableSelectQuery} objects too
	*/
	XmlDoc.prototype.addRowset = function(rowSet)
	{
		this.extraRowSets.push(rowSet);
	}
	
	/**
	* wrapper for the document.evaluate function that supposedly exists in mozilla AND IE (miracle of miracles)
	* @param {String} xpathExpression is a string representing the XPath to be evaluated.
    * @param {Node} contextNode specifies the context node for the query (see the [http://www.w3.org/TR/xpath XPath specification). It's common to pass document as the context node.
    * @param {Number} resultType is an integer that corresponds to the type of result XPathResult to return. Use named constant properties, such as XPathResult.ANY_TYPE, of the XPathResult constructor, which correspond to integers from 0 to 9.
	*/
	XmlDoc.prototype.evaluate = function(xpathExpression,contextNode,resultType)
	{
		try
		{
			if (resultType === undefined)
				resultType = XPathResult.ANY_TYPE;
			if (this.xmlDoc.evaluate)
			return this.xmlDoc.evaluate(xpathExpression,contextNode,null,resultType,null);
		}
		catch (ex){}
		console.warn('Tried to call xpath \'evaluate\' but it doesn\'t exist in the document');
		return null;
	}
	
	/**
	* serialize a node to a string (only in firefox)
	* @param {Node} node to serialize
	*/
	XmlDoc.prototype.serializeNode = function(node)
	{
		try
		{
			return new XmlSerializer().serializeToString(node);
		}
		catch (ex){}
		return '';
	}
	/**
	* set the contents of the xml document to the document represented in xmlDoc and xmlText (depending on whether we are using IE or gecko, we may need either)
	* @param {DomDocument} xmlDoc xml document with the contents we want to set
	* @param {String} xmlText text representation of the xml document with the contents we want to use
	*/
	XmlDoc.prototype.setXmlDoc = function(xmlDoc,xmlText)
	{
		//debugout('setting xml doc value '+xmlDoc.firstChild);
		if ((xmlDoc) &&
			  		( ((xmlDoc.firstChild) &&  (xmlDoc.firstChild.tagName)) ||
						(xmlDoc.rootNode) )
			)
		{
//			console.debug('setting xmldoc ! '+xmlDoc.firstChild.tagName+' '+xmlDoc.parseError.reason);
			//console.debug('setting xmldoc '+xmlDoc.parseError);
				//debugout('assigning xmlDoc '+xmlDoc.firstChild.tagName);
			this.xmlDoc = xmlDoc;
		}
		else if (xmlText !== undefined)
		{
			
			var noDomParser = false;
			//debugout('assigning xmlText');
			//nasty little hack to get rid of <?XML blah blah blah ?>
			//because IE thinks it's a freakin node
//			console.debug('assigning xmlText ',xmlText);
			try	{if (!DOMParser) noDomParser = true;	}
			catch (ex) {noDomParser = true;}
			if (noDomParser)
			{
				var xd = newXmlDoc(this.rootTag,this.completefunc);
				if (!xd.loadXML(xmlText.replace(/^<?[^>]*\?>/g,'')))
				{	console.warn(xmlText.replace(/^<?[^>]*\?>/g,''),xd.parseError.reason);
					throw {message:xd.parseError.reason,error:xd.parseError};
				}
				else
					this.setXmlDoc(xd);
			}
			else
			{
				var dp = new DOMParser();
				var xd = dp.parseFromString(xmlText,"text/xml");
				//console.debug(xd.firstChild,xd.firstChild.tagName);
				if ((xd.firstChild) && (xd.firstChild.tagName == 'parsererror'))
				{
					//console.trace('parseerrror');
					throw {message:new XMLSerializer().serializeToString(xd),error:xd.firstChild,source:xmlText};
				}
				else
					this.setXmlDoc(xd);
			}
		}	var noXmlSerializer = false;
			try {if (!XMLSerializer) noXmlSerializer = true} catch (ex) {noXmlSerializer = true}
			//console.debug('noXmlSerializer '+(noXmlSerializer?'true':'false'));
			if (noXmlSerializer)
			{ //IE specialization
				this.serializeToString = function()
				{
					return this.xmlDoc.xml;
				}
			}
			else
			{ //w3 DOM spec
				this.serializeToString = function ()
				{
					var s = new XMLSerializer();
					return s.serializeToString(this.xmlDoc);
				}
			}
		this.rootNode = this.xmlDoc.firstChild;
		this.firstChild = this.xmlDoc.firstChild;
	}
	/**
	* find a child node that has the specified tag name (and attribute value if specified)
	* @param {DomNode} parentNode search this node's childNodes 
	* @param {String} tagName tag name to look for. ie: [tagName ]
	* @param {String} attrName (optional) Search for the attribute by this name ie: [tagName attrName='attrValue']
	* @param {String} attrValue (optional} search for the node with attrName=attrValue
	*/
	XmlDoc.prototype.findChildNode = function(parentNode,tagName,attrName,attrValue)
	{
		//debugout('findchildnode looking for <'+tagName+' '+attrName+'='+attrValue+'>');
		if ((parentNode) && (parentNode.firstChild))
		{
			var foundNode = parentNode.firstChild;
			var nodeList = '';
			while ((foundNode) && ((foundNode.tagName != tagName) || ((attrName) && (foundNode.getAttribute(attrName) != attrValue))) )
			{
				//nodeList += '|<'+foundNode.tagName;
				//if (attrName)
				//	nodeList +=' '+attrName+'='+foundNode.getAttribute(attrName)+'>';
				foundNode = foundNode.nextSibling;
			}
			//debugout('searched '+nodeList);
			//if (foundNode) debugout('UREKA, found <'+foundNode.tagName+' '+attrName+'='+foundNode.getAttribute(attrName)+'>');
																											   
			return foundNode;
		}
	}

/**
* creates a new xml doc using browser specific methods 
* @param {String} rootTag tag name to use for root node
* @param {Function} completefunc function to call when load is completed
* @private
*/
function newXmlDoc(rootTag,completefunc)
{
	var xmlDoc;
	if (document.implementation && document.implementation.createDocument)
	{
		//debugout('creating document mozilla style');
		xmlDoc = document.implementation.createDocument("", rootTag, null);
		if (xmlDoc.documentElement == null)
		{
			//debugout('fixing safari bug with documentelement');
			newelem = xmlDoc.createElement(rootTag);
			//debugout(' new document element '+newelem);
			xmlDoc.appendChild(newelem);
		}
		if (completefunc)
			xmlDoc.onload = completefunc;
	}
	else if (window.ActiveXObject)
	{
		//debugout('creating document MS style');
		var msDoms = ["MSXML2.DomDocument.7.0","MSXML2.DomDocument.6.0",
					  "MSXML2.DomDocument.5.0","MSXML2.DomDocument.4.0","MSXML2.DomDocument.3.0",
					  	"MSXML2.DomDocument","Microsoft.XmlDom"];
		for (var count = 0;((count < msDoms.length) && (!xmlDoc));count++)
			try { xmlDoc = new ActiveXObject(msDoms[count]);console.debug('loaded '+msDoms[count]); } catch (ex) {} 
		//xmlDoc = new ActiveXObject("Msxml2.DOMDocument.3.0");
		if (completefunc)
			xmlDoc.onreadystatechange = function () {
				if (xmlDoc.readyState == 4) completefunc();
			};
			xmlDoc.loadXML("<"+rootTag+"/>");
 	}
	else
	{
		alert('Your browser can\'t handle this script. Please upgrade to Mozilla Firefox (www.getfirefox.com)');
		return;
	}
	//debugout('new xmldoc = '+xmlDoc);
	return xmlDoc;
}

/**
*
*  UTF-8 data encode / decode
*  http://www.webtoolkit.info/
*
**/

var Utf8 = {

	// public method for url encoding
	encode : function (string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";

		for (var n = 0; n < string.length; n++) {

			var c = string.charCodeAt(n);

			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}

		}

		return utftext;
	},

	// public method for url decoding
	decode : function (utftext) {
		var string = "";
		var i = 0;
		var c = c1 = c2 = 0;

		while ( i < utftext.length ) {

			c = utftext.charCodeAt(i);

			if (c < 128) {
				string += String.fromCharCode(c);
				i++;
			}
			else if((c > 191) && (c < 224)) {
				c2 = utftext.charCodeAt(i+1);
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
				i += 2;
			}
			else {
				c2 = utftext.charCodeAt(i+1);
				c3 = utftext.charCodeAt(i+2);
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}

		}

		return string;
	}

}


document.ipexits = 0;
document.gpexits = {};
function clog(value)
{
	if (window.console)
		window.console.log(value);
}

function default_debugout(value) {alert(value); }
function default_eo(obj,objname) {alert(value+' '+value2);}
function default_resetdebug(){}

var debugobj = {debugOut :default_debugout, eo:default_eo,resetDebug:default_resetdebug};
function set_xmlrpc2_debug_object(dobj)
{
	debugobj = dobj;
	debugobj.debugOut('xmlrpc2 debug object:' +dobj);
	return dobj;
}
function fdebugout(debugstr)
{
	var d = document.getElementById('debugbox');
	if ((d) && (d.insertBefore))
	{
		nc = document.createElement('br');
		d.insertBefore(nc,d.firstChild);
		var nc = document.createTextNode(Next++ +' '+debugstr);
		d.insertBefore(nc,d.firstChild);
	}
	var d = document.getElementById('debugboxswitch');
	if ((d)&&(d.style) )
		d.style.display='';
}

function XMLRequest3(readyObj,readyFunc,responseRootTag)
{
	this.xmlHttpReq = this.getNewXHR();
	
	this.readyFunc = readyFunc;
	this.readyObj = readyObj;
	console.debug('new xmlRequest3 ');//,readyFunc,readyObj);
	this.src = '';
	this.requestQueue = new Array();
	//register ourself with the window (for safari)
	if (!window.___XMLRequest3s)
		window.___XMLRequest3s = new Array();
	window.___XMLRequest3s[window.___XMLRequest3s.length] = this;
	if (responseRootTag) this.responseRootTag = responseRootTag;
		try
		{
			this.locationRoot = String(window.location).match(/^https?:\/\/[^\/]*/);
		}
		catch (ex)
		{
			console.warn('trying to get window.location error '+ex.message);
			this.locationRoot = String(window.location.href).match(/^https?:\/\/\[$\/]*/);
		}
				if ((this.locationRoot.length) && (this.locationRoot.length > 0))
					this.locationRoot = this.locationRoot[0];
				else this.locationRoot = 'http://www.stoneycreekwinepress.com';
}

XMLRequest3.prototype.xmlHttpReq = false;
XMLRequest3.prototype.readyFunc = false;
XMLRequest3.prototype.readyObj = false;
XMLRequest3.prototype.requestQueue = false;
//set completed when readystate == 4 and we have handled the result
XMLRequest3.prototype.readyHandled = true;
XMLRequest3.prototype.src = '';
XMLRequest3.prototype.busy = false;//set to true during a send
XMLRequest3.responseRootTag = null;

XMLRequest3.prototype.lastSrc = '';//source for previous send (in case of an error so we can send feedback and manually run the http request

XMLRequest3.prototype.setSrc = function(newsrc)
{
	this.src = newsrc;
}

XMLRequest3.prototype.getNewXHR = function(clear)
{
	if (window.XMLHttpRequest)
	{
			if ((this.xmlHttpReq) && (clear))
			{			
				this.xmlHttpReq.onreadystatechange = null;
				this.xmlHttpReq.___xr___ = null;
				this.xmlHttpReq.XMLRequest3 = null;
			}
			var result = new XMLHttpRequest();
			if (result.overrideMimeType)
				result.overrideMimeType('text/xml');
			this.enabled = ((result) && (result.onreadystatechange === null));
			if (!this.enabled)
			{
				if (!result) this.insanityReason = 'XmlHttpRequest not created';
				else if (!result.onreadystatechange !== null)
					this.insanityReason = 'XmlHttpRequest onreadystatechange not initialized to null. (Implementation is broken)';
			}
			return result;
	}
	else if (window.ActiveXObject)
	{
		var result;		
		this.enabled = (result = new ActiveXObject('Microsoft.XMLHTTP'));
		if (!this.enabled) this.insanityReason = 'Could not create Microsoft.XMLHTTP';
		return result;
	}
	return null;
}
//example ready state function
XMLRequest3.prototype.readystatechange = function()
{
	
		if (this.readyState !== undefined)  
			var XMLReq = this;
		else
		if (this.___xr___)
			var XMLReq = this.___xr___.xmlHttpReq;

		if ((XMLReq) && (XMLReq.___xr___))
		{
			//make sure we get the XMLRequest3 object 
			var xr = XMLReq.___xr___;
			xr.doReadyState();
		}
		else
		{
			// if we are running safari the only thing we can do is look through all the XMLRequest3 objects 
			// and see if one is in readystate 4 and readyHandled == false. UGLY UGLY UGLY
			for(var count = 0;count < window.___XMLRequest3s.length;count++)
			{
				xr = window.___XMLRequest3s[count];
				if ((xr) && (xr.xmlHttpReq.readyState == 4) && (!xr.readyHandled))
					xr.doReadyState();
			}
		}
		
		
		
}
//ACTUALLY do the readystate thing with <this> as the XMLRequest3 object!
XMLRequest3.prototype.doReadyState = function()
{
	if (this.xmlHttpReq.readyState == 4)
	{
		if (this.readyHandled) return;
		this.readyHandled = true;
		this.busy = false;
		console.debug('doreadystate: ',this);
		if (((this.xmlHttpReq.responseXML == null) || (this.xmlHttpReq.responseXML.childNodes.length == 0)) && (!this.xmlHttpReq.responseText)) 
		{
			this.readyFunc.call(this.readyObj,this.currentQueryRef,'abort');
			return;
		}
		var rDoc = undefined;
		if (this.responseRootTag)//only send a response doc if responseroottag is specified
		{
			try
			{
				rDoc = new XmlDoc(this.responseRootTag);
				rDoc.setXmlDoc(this.xmlHttpReq.responseXML,this.xmlHttpReq.responseText);
			}
			catch (ex)
			{
				rDoc = ex;
			}
		}
		if (this.readyObj)
			this.readyFunc.call(this.readyObj,this.currentQueryRef,rDoc);
		else this.readyFunc(this.curentQueryRef,rDoc);
		if (this.requestQueue.length > 0)//process the queue
		{
			var sendItem = this.requestQueue[0];
			this.requestQueue = this.requestQueue.slice(1);
			this.send(sendItem.querydata,sendItem.queryRef);
		}
	}		
}

XMLRequest3.prototype.cancel = function(clearQueue)
{
	//don't use abort because it doesn't work with IE
	try
	{
		this.xmlHttpReq.abort();
		//unset any references to this so it can be gc'd
		this.xmlHttpReq.onreadystatechange = null;
		this.xmlHttpReq.___xr___ = null;
		this.xmlHttpReq.XMLRequest3 = null;
		
	}
	catch (ex)
	{
		console.warn('abort failed ',ex);
	}
	//who cares if abort worked, just get a new xhr object
	this.xmlHttpReq = this.getNewXHR();
	console.trace('xml request aborted');
	if (this.busy) 
	{
		this.busy = false;
		this.readyHandled = true;
		if (clearQueue)
			this.requestQueue.length = 0;
		this.readyFunc.call(this.readyObj,this.currentQueryRef,'abort');
	}
}

XMLRequest3.prototype.sendDoc = function(xmlDoc)
{
	this.send('value='+escape(Utf8.encode(xmlDoc.serializeToString())),xmlDoc);
}
//NOTE: querydata requires not to be empty for some reason maybe? on IIS
XMLRequest3.prototype.send = function(querydata,queryRef)
{
		//this.debugout(this.src+'&'+querydata);
		if (this.enabled)
		{
			if (this.readyHandled == false)
				this.requestQueue[this.requestQueue.length] = {querydata:querydata,queryRef:queryRef};
			else
			{
				try
				{
					this.xmlHttpReq.open('POST',this.src,true);
				}
				catch (ex)
				{
					if (String(ex.message).match(/0x80004005/))
					{
						console.warn('error with xhr ',ex);
						this.xmlHttpReq = this.getNewXHR(true);
						this.xmlHttpReq.open('POST',this.src,true);
					}
					else throw ex;
				}
				this.xmlHttpReq.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=utf-8');
				this.currentQueryRef = queryRef;
				this.readystatechange.___xr___ = this;
				try
				{
					this.xmlHttpReq.___xr___ = this;
					this.xmlHttpReq.XMLRequest3 = this;
				}
				catch (ex){}
				this.xmlHttpReq.onreadystatechange = this.readystatechange;
				this.readyHandled = false;
				this.busy = true;
				this.lastSrc = this.locationRoot+'/'+String(this.src).replace(/^\//,'')+'&'+querydata;
				this.xmlHttpReq.send(querydata);
			
				
			}	
		}
}

XMLRequest3.prototype.getStatus = function()
{
	return this.xmlHttpReq.readyState;
}
var Next = 0;
XMLRequest3.prototype.resetdebug = function ()
{
	var d = document.getElementById('debugbox');
	if ((d) && (d.firstChild)) 
	{
		while (d.firstChild) 
		{
			d.removeChild(d.firstChild);
		}
	}
}

XMLRequest3.prototype.toString = function()
{
	return 'XMLRequest3 Object: '+this.src+' status: '+this.getStatus();
}

XMLRequest3.prototype.debugout = fdebugout;
function PageExit(elem)
{
	this.href = elem.href;
	this.elem = elem;
}

function PageExits()
{
	this.pexits = new Array();
	this.index = document.ipexits++;
	document.gpexits[this.index] = this;
	var func = new Function('document.gpexits['+this.index+'].getExits();');
	try {window.attachEvent('onload',func)} catch (e) {}
	try {window.addEventListener('load',func,false);}	catch (e){}

}

PageExits.prototype.getExits = function()
{
	links = document.body.getElementsByTagName('A');
	
	for (i = 0;i < links.length;i++)
	{
		if (links[i].href != '')
		{
			//if (!links[i].id)
			//	links[i].id = 'exit'+i;
			this.pexits[i] = new PageExit(links[i]);
		}
	}
}
PageExits.prototype.getExit = function(wrapstring,pexit)
{
	wrapstring = wrapstring.replace(/%id%/g,pexit.elem.id);
	wrapstring = wrapstring.replace(/%href%/g,pexit.href);
	return wrapstring;
}
PageExits.prototype.fixExits = function (wrapstring)
{
	for (i = 0;i<this.pexits.length;i++)
		this.pexits[i].elem.href = this.getExit(wrapstring,this.pexits[i]);
}

