/**
 * @fileoverview
 * 
 * Shared function library
 * 
 */



/***************************************************************************/
/**
 * Vizami base class
 */
Vizami = {};


/***************************************************************************/
/**
 * Utility functions
 */
Vizami.Util = {
	/**
	 * Sorts a an element with li child-elements (ul)
	 * @param {Object} oList the list to sort
	 * @param {Function} fComp a comparator function
	 */	
	sortList : function (oList, fComp) {
		aList = Vizami.DOM.nodeListToArray(oList.getElementsByTagName('li'));
		for(n in aList)
		{
			oList.removeChild(aList[n]);
		}
		aList.sort(fComp);
		for(n in aList)
		{
			oList.appendChild(aList[n]);
		}
	},

	/**
	 * Compares two Strings
	 * @param {String} s1 first String
	 * @param {String} s2 second String
	 * @returns -1, if s1 should be placed before s2; 1 if s2 should be placed before s1; 0 otherwise
	 */	
	stringComparator : function (s1, s2) {
		for(n = 0; n < Math.max(s1.length, s2.length); n++)
		{
			if(s1.charAt(n) > s2.charAt(n))
				return 1;
			if(s1.charAt(n) < s2.charAt(n))
				return -1;		
		}
		if(s1.length < s2.length)
			return 1;
		if(s1.length > s2.length)
			return 1;			
		return 0;
	},
	
	/**
	 * Open a new window
	 * @param {String} sWindowName Name of the window (default = '')
	 * @return the opened window's object
	 */
	newWindow : function (sWindowName) {
		if (sWindowName == null) sWindowName = '';
		var oWindow = window.open("", sWindowName, "height=400,width=600,scrollbars=yes,resizable=yes,status=yes");
		oWindow.document.open();
		oWindow.document.write('\
				<!DOCTYPE html\
					 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\
					 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\
     			<html xmlns="http://www.w3.org/1999/xhtml">\
				<head>\
				<title>Debug object</title>\
				<style type="text/css">\
					body, td, th, pre { font: small Courier New, sans-serif; }\
					th { background: #fee; text-align: left; }\
					td { background: #fdd; }\
				</style>\
				</head>\
				<body></body>\
				</html>\
			');
		oWindow.document.close();
		return oWindow;
	},
	
	/**
	 * Checks if an array a contains the object o
	 * @param {Array} a An array
	 * @param {Object} o An object
	 * @return the position of the object or null
	 */
	hasItem : function (a, o) {
		for(n in a)
		{
			if(a[n] == o)
				return n;
		}
		return false;
	},
	
	/**
	 * Parses smileys and URL's in sMessage
	 * @param {String} s The string you want parsed
	 * @return the parsed string
	 */
	parseExtraContent : function(sMessage) {
		var splitMessage;
		splitMessage = sMessage.split(" ");
		
		var parsed = document.createElement('span');
		var result;
		var splitToken;
		
		for (index in splitMessage) {
			var word2 = splitMessage[index];
			var splitWord = word2.split(",");
			
			for (index2 in splitWord) {
				var word = splitWord[index2];
				
				if ((word.search("http://") == 0) || (word.search("https://") == 0) || (word.search("ftp://") == 0))
				{
					result = document.createElement('a');
					result.setAttribute('target','_blank');
					result.setAttribute('href',word);
					result.appendChild(document.createTextNode(word));
				}
				else if (word.search("www[.]") == 0)
				{
					result = document.createElement('a');
					result.setAttribute('target','_blank');
					result.setAttribute('href','http://'+word);
					result.appendChild(document.createTextNode(word));
				}
				else {
					result = Vizami.Util.parseEmoticons(word);
				}
				parsed.appendChild(result);
				
				if (index2==splitWord.length-1) splitToken = " ";
				else splitToken = ",";
				parsed.appendChild(document.createTextNode(splitToken));
			}

		}
		
		return parsed;
	},
	
	/**
	 * Parses smileys sMessage
	 * @param {String} s The string you want parsed
	 * @return the parsed string
	 */
	parseEmoticons : function(sMessage) {
		var result = document.createElement('span');
		var image;
		var broken=false;
		
		for (var x in Vizami.Util.emoticons)
		{
			if (broken) break;
	
			var splitmessage = sMessage.split(x);
	
			if (splitmessage.length > 1)
			{ 
				result.appendChild(Vizami.Util.parseEmoticons(splitmessage[0]));
				
				for (var y=1;y<splitmessage.length;y++)
				{
					image = document.createElement('img');
					image.setAttribute('src',Vizami.Util.emoticons[x]);
					image.setAttribute('alt',x);
					result.appendChild(image);
					result.appendChild(Vizami.Util.parseEmoticons(splitmessage[y]));
				}
				broken = true;
			}
		}
		if (!broken)
		result.appendChild(document.createTextNode(sMessage));
		
		return result;
	},
	
	emoticons : {
		":-(("      : "_images/smilies/!e!-((.gif",
		":P"		: "_images/smilies/!e!-p.gif",
		":p"		: "_images/smilies/!e!-p.gif",
		";)"        : "_images/smilies/;-%29.gif",
		":)"        : "_images/smilies/!e!-%29.gif",
		":D"        : "_images/smilies/!e!-d.gif",
		":d"        : "_images/smilies/!e!-d.gif",
		":("        : "_images/smilies/!e!-%28.gif",
		"(dracula)" : "_images/smilies/%28dracula%29.gif",
		":-("       : "_images/smilies/!e!-%28.gif",
		"(l)"       : "_images/smilies/%28l%29.gif",
		":-)"       : "_images/smilies/!e!-%29.gif",
		":-o"       : "_images/smilies/!e!-o.gif",
		"(wine)"    : "_images/smilies/%28wine%29.gif",
		":-p"       : "_images/smilies/!e!-p.gif",
		"(f1)"      : "_images/smilies/%28f1%29.gif",
		"(boy1)"    : "_images/smilies/%28boy1%29.gif",
		"(boy3)"    : "_images/smilies/%28boy3%29.gif",
		"(beer)"    : "_images/smilies/%28beer%29.gif",
		"(so)"      : "_images/smilies/%28so%29.gif",
		":-@"       : "_images/smilies/!e!-@.gif",
		"8-|"       : "_images/smilies/8-!o!.gif",
		"b-)"       : "_images/smilies/b-%29.gif",
		"(k)"       : "_images/smilies/%28k%29.gif",
		"(devil)"   : "_images/smilies/%28devil%29.gif",
		":-|"       : "_images/smilies/!e!-!o!.gif",
		"(u)"       : "_images/smilies/%28u%29.gif",
		":-\\"      : "_images/smilies/!e!-!q!.gif",
		"(f2)"      : "_images/smilies/%28f2%29.gif",
		":-<"       : "_images/smilies/!e!-!u!.gif",
		"(boy2)"    : "_images/smilies/%28boy2%29.gif",
		"(mp)"      : "_images/smilies/%28mp%29.gif",
		"(girl1)"   : "_images/smilies/%28girl1%29.gif",
		";-)"       : "_images/smilies/;-%29.gif",
		"(girl3)"   : "_images/smilies/%28girl3%29.gif",
		"8-o"       : "_images/smilies/8-o.gif",
		":-a"       : "_images/smilies/!e!-a.gif",
		":'("       : "_images/smilies/!e!'%28.gif",
		":*("       : "_images/smilies/!e!'%28.gif",	
		":-d"       : "_images/smilies/!e!-d.gif"
	}
}



/***************************************************************************/
/**
 * Debug functions
 */
Vizami.Debug = {
	
	enabled: false,
	
	/**
	 * Pops up a window with all the properties of an object
	 * @param {Object} oObject object to show
	 * @param {Int} iDepth how deep to recursively traverse into the object (optional, default = 0)
	 */
	showObject : function (oObject, iDepth) {
		if (!this.enabled) return;
		if (iDepth == undefined) iDepth = 0;
		var oWindow = Vizami.Util.newWindow();
		var body = oWindow.document.getElementsByTagName('body')[0];
		var p = oWindow.document.createElement('p');
		var now = new Date();
		p.appendChild(oWindow.document.createTextNode('Generated: ' + now.toLocaleString() + ' (' + now.getTime() + ')'));
		body.appendChild(p);
		
		var table = this.createObjectTable(oObject, iDepth, oWindow);
		
		body.appendChild(table);
	},
	
	/**
	 * Create a nested table for the given object, with the given depth
	 * @param {Object} oObject object to show
	 * @param {Int} iDepth how deep to recursively traverse into the object
	 * @param {Object} oWindow reference to the window to create the table with
	 * @returns {Object} reference to the created table
	 * @private
	 */
	createObjectTable : function (oObject, iDepth, oWindow) {
		var table = oWindow.document.createElement('table');
		var tbody = oWindow.document.createElement('tbody');
		for(var i in oObject) {
			var tr = oWindow.document.createElement('tr');
			var th = oWindow.document.createElement('th');
			th.appendChild(oWindow.document.createTextNode(i));
			tr.appendChild(th);
			var td = oWindow.document.createElement('td');
			try {
				switch (typeof oObject[i]) {
					case 'function':
						var tdchild = oWindow.document.createElement('pre');
						tdchild.appendChild(oWindow.document.createTextNode(oObject[i]));
						break;
					case 'object':
						if (iDepth > 0) {
							var tdchild = this.createObjectTable(oObject[i], iDepth-1, oWindow);
						} else {
							var tdchild = oWindow.document.createElement('a');
							var oWrapper = {oObj:oObject[i], oElm:tdchild, oWnd:oWindow};
							tdchild.setAttribute('href','javascript://');
							YAHOO.util.Event.addListener(tdchild, 'click', this.expandObjectTableHandler, oWrapper);
							tdchild.appendChild(oWindow.document.createTextNode(oObject[i]));
						}
						break;
					default:
						var oSubject = oObject[i];
						if (oSubject == '') oSubject = "''";
						if (oSubject == []) oSubject = "[]";
						if (oSubject == null) oSubject = "null";
						if (oSubject == undefined) oSubject = "undefined";
						var tdchild = oWindow.document.createTextNode(oSubject);
						break;
				}
				td.appendChild(tdchild);
			} catch(e) {
				td.appendChild(oWindow.document.createTextNode('(error: '+e.name+')'));
				td.setAttribute('title',e.message);
			}
			tr.appendChild(td);
			tbody.appendChild(tr);
		}
		table.appendChild(tbody);
		return table;
	},
	
	/**
	 * @private
	 */
	expandObjectTableHandler : function (e, oWrapper) {
		var table = Vizami.Debug.createObjectTable(oWrapper.oObj, 0, oWrapper.oWnd);
		oWrapper.oElm.parentNode.replaceChild(table, oWrapper.oElm);
	},
	
	
	
	/**
	 * Put a message in the debug tracer
	 * @param {Object} oMessage message to show
	 * @param {Object} ... any additional messages to show
	 */
	message : function () {
		if (!this.enabled) return;
		var oDebugDiv = this.getTracer();
		var oDebugMsgDiv = document.createElement('div');
		oDebugMsgDiv.style.borderBottom = '1px dotted red';
		oDebugMsgDiv.style.padding = '0.1em 0.3em';
		var oLineNumber = document.createTextNode(this.tracerLineCounter++ + '. ');
		oDebugMsgDiv.appendChild(oLineNumber);
		for (var i=0; i<arguments.length; i++) {
			var oMessage = arguments[i];
			if (typeof oMessage == 'object') {
				var msg = document.createElement('a');
				msg.setAttribute('href','javascript://');
				msg.appendChild(document.createTextNode(oMessage));
				YAHOO.util.Event.addListener(msg, 'click', this.showMessageObjectHandler, oMessage);
			} else {
				var msg = document.createTextNode(oMessage);
			}
			oDebugMsgDiv.appendChild(msg);
		}
		var oDebugMsgDivs = oDebugDiv.getElementsByTagName('div');
		oDebugDiv.insertBefore(oDebugMsgDiv, oDebugMsgDivs.item(0));
	},
	
	tracerLineCounter : 1,
	
	/**
	 * @private
	 */
	showMessageObjectHandler : function (e, oObj) {
		Vizami.Debug.showObject(oObj);
	},
	
	/**
	 * Gets a reference to the debug tracer element, creates it when it doesn't exist.
	 * If /yahoo/util/dragdrop.js was loaded, the tracer is draggable.
	 * @returns {Object} reference to debug tracer element
	 */
	getTracer : function () {
		if (!this.enabled) return;
		var oDebugDiv = document.getElementById('debugdiv');
		if (oDebugDiv) return oDebugDiv;
		var oDebugDiv = document.createElement('div');
		oDebugDiv.setAttribute('id','debugdiv');
		oDebugDiv.setAttribute('title','double-click to expand/collapse');
		var css = 'position:absolute;color:black;border:2px solid red;-moz-border-radius:6px;z-index:999;';
		if ('cssText' in oDebugDiv.style) oDebugDiv.style.cssText = css;
		else oDebugDiv.setAttribute('style',css);
		document.body.insertBefore(oDebugDiv, document.body.firstChild);
		this.toggleTracer();
		YAHOO.util.Event.addListener(oDebugDiv, 'dblclick', this.toggleTracer);
		try {
			dd_debugdiv = new YAHOO.util.DD('debugdiv');
		} catch(e) {}
		return oDebugDiv;
	},
	
	/**
	 * @private
	 */
	toggleTracer : function (e) {
		var oDebugDiv = document.getElementById('debugdiv');
		if (YAHOO.util.Dom.getStyle(oDebugDiv, 'height') != '18px') {
			YAHOO.util.Dom.setStyle(oDebugDiv, 'height', '18px');
			YAHOO.util.Dom.setStyle(oDebugDiv, 'width', '300px');
			YAHOO.util.Dom.setStyle(oDebugDiv, 'overflow', 'hidden');
			YAHOO.util.Dom.setStyle(oDebugDiv, 'background', '#fcc');
			YAHOO.util.Dom.setStyle(oDebugDiv, 'opacity', '0.5');
		} else {
			YAHOO.util.Dom.setStyle(oDebugDiv, 'height', '400px');
			YAHOO.util.Dom.setStyle(oDebugDiv, 'width', '300px');
			YAHOO.util.Dom.setStyle(oDebugDiv, 'overflow', 'auto');
			YAHOO.util.Dom.setStyle(oDebugDiv, 'background', 'white');
			YAHOO.util.Dom.setStyle(oDebugDiv, 'opacity', '1.0');
		}
	}

};



/***************************************************************************/
/**
 * CSS function library
 */
Vizami.CSS = {
	
	/**
	 * Toggle class
	 */
	toggleClass : function(oElm, sClass) {
		if (this.hasClass(oElm, sClass)) {
			this.removeClass(oElm, sClass);
		} else {
			this.addClass(oElm, sClass);
		}
	},
	
	/**
	 * Test whether element has a class
	 * @returns boolean
	 */
	hasClass : function(oElm, sClass) {
		if (oElm.className) {
			var aClasses = oElm.className.split(' ');
			for (var i in aClasses) {
				if (aClasses[i] == sClass) return true;
			}
		}
		return false;
	},
	
	/**
	 * Add a class to an element
	 */
	addClass : function(oElm, sClass) {
		if (oElm.className) {
			var aClasses = oElm.className.split(' ');
		} else {
			var aClasses = new Array();
		}
		aClasses.push(sClass);
		oElm.className = aClasses.join(' ');
	},
	
	/**
	 * Remove a class from an element
	 */
	removeClass : function(oElm, sClass) {
		if (oElm.className) {
			var aClasses = oElm.className.split(' ');
			// bug?
			for (var i in aClasses) {
				if (aClasses[i] == sClass) aClasses.splice(i,1);
			}
			oElm.className = aClasses.join(' ');
		}
	}
	
};


/***************************************************************************/
/**
 * Browser detection
 */
Browser = {};
Browser.UA     = navigator.userAgent.toLowerCase();
Browser.ie     = (document.all && !Browser.UA.match(/Opera/i)) ? true : false;
Browser.ie5    = false;
Browser.opera8 = Browser.UA.match(/Opera[\/ ]8./i) ? true : false;
Browser.opera9 = Browser.UA.match(/Opera[\/ ]9./i) ? true : false;
Browser.moz    = window.controllers ? true : false;
Browser.xhtml  = function() {
		var oElm = document.createElement('div');
		try { oElm.innerHTML = '<div>test'; return false; }
		catch(e) { return true; }
	} ();



/***************************************************************************/
/**
 * DOM function library
 */
Vizami.DOM = {
	// Opera and IE don't have these defined on the nodes themselves :(
	ELEMENT_NODE                : 1,
	ATTRIBUTE_NODE              : 2,
	TEXT_NODE                   : 3,
	CDATA_SECTION_NODE          : 4,
	ENTITY_REFERENCE_NODE       : 5,
	ENTITY_NODE                 : 6,
	PROCESSING_INSTRUCTION_NODE : 7,
	COMMENT_NODE                : 8,
	DOCUMENT_NODE               : 9,
	DOCUMENT_TYPE_NODE          : 10,
	DOCUMENT_FRAGMENT_NODE      : 11,
	NOTATION_NODE               : 12,
	
	// Some namespaces and their prefixes...
	NS : {
		'rdf' : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
		'xml' : 'http://www.w3.org/XML/1998/namespace',
		'vt'  : 'http://vips.studenten.net/rdf-templating'
	},
	// And the other way around
	NS2Prefix : {
		'http://www.w3.org/1999/02/22-rdf-syntax-ns#' : 'rdf:',
		'http://www.w3.org/XML/1998/namespace'        : 'xml:',
		'http://vips.studenten.net/rdf-templating'         : 'vt:'
	},
	
	/**
	 * Returns a NodeList or NamedNodeMap as a JavaScript array
	 * @param {object} oNodeList Source NodeList or NamedNodeMap
	 * @return array
	 */
	nodeListToArray : function(oNodeList) {
		var aNodeListRv = new Array();
		for (var i=0; i<oNodeList.length; i++) {
			aNodeListRv.push(oNodeList.item(i));
		}
		return aNodeListRv;
	},
	
	/**
	 * Returns an array with all elements in a NodeList
	 * @param {object} oNodeList Source NodeList
	 * @return array
	 */
	getNodeListElements : function(oNodeList) {
		var aNodeListRv = new Array();
		for (var i=0; i<oNodeList.length; i++) {
			var oNode = oNodeList.item(i);
			if (oNode.nodeType == Vizami.DOM.ELEMENT_NODE) {
				aNodeListRv.push(oNode);
			}
		}
		return aNodeListRv;
	},
	
	/**
	 * Gets and concatenates all text inside a Node
	 * @param {object} oNode Element Node
	 * @return string
	 */
	textContent : function (oNode) {
		var oNodeList = oNode.childNodes;
		var aNodeListRv = new Array();
		for (var i=0; i<oNodeList.length; i++) {
			var oNode = oNodeList.item(i);
			if (oNode.nodeType == Vizami.DOM.TEXT_NODE) {
				aNodeListRv.push(oNode.nodeValue);
			} else if (oNode.nodeType == Vizami.DOM.ELEMENT_NODE) {
				aNodeListRv.push(getNodeListText(oNode));
			}
		}
		return aNodeListRv.join('');
	},
	
	
	parseXML : function (sXML) {
		if (Browser.ie) {
			var oXMLDocument = new ActiveXObject("Microsoft.XMLDOM");
			oXMLDocument.async = false;
			oXMLDocument.preserveWhiteSpace = true;
			try {
				if (oXMLDocument.loadXML(sXML)) {
					return oXMLDocument;
				} else {
					return null;
				}
			}
			catch (e) { Vizami.Debug.message('parse error'); }
		} else {
			try {
				var oXMLDocument = new DOMParser().parseFromString(sXML, 'text/xml');
				if (oXMLDocument.documentElement.nodeName == "parsererror") {
					return null;
				}
				return oXMLDocument;
			}
			catch (e) { Vizami.Debug.message('parse error'); }
		}
	},
	
	
	/*
	 * Functions to access prefixed elements and attributes in HTML DOM
	 */
	getHTMLElementsByTagName : function (oNode, sTagName) {
		sTagName = Browser.domok ? sTagName : sTagName.substring(sTagName.indexOf(':')+1);
		return oNode.getElementsByTagName(sTagName);
	},
	
	hasHTMLAttribute : function (oNode, sAttrName) {
		if (Browser.opera8) sAttrName = sAttrName.substring(sAttrName.indexOf(':')+1);
		if (oNode.hasAttribute) {
			return oNode.hasAttribute(sAttrName);
		} else {
			if (oNode.attributes[sAttrName]) {
				return true;
			} else {
				return false;
			}
		}
	},
	
	getHTMLAttribute : function (oNode, sAttrName) {
		if (Browser.opera8) sAttrName = sAttrName.substring(sAttrName.indexOf(':')+1);
		if (!Browser.ie) {
			return oNode.getAttribute(sAttrName);
		} else {
			if (oNode.attributes[sAttrName]) {
				return oNode.attributes[sAttrName].nodeValue;
			} else {
				return '';
			}
		}
	},
	
	setHTMLAttribute : function (oNode, sAttrName, sValue) {
		if (Browser.opera8) sAttrName = sAttrName.substring(sAttrName.indexOf(':')+1);
		if (!Browser.ie) {
			return oNode.setAttribute(sAttrName, sValue);
		} else {
			if (oNode.attributes[sAttrName]) {
				oNode.attributes[sAttrName].nodeValue = sValue;
				return true;
			} else {
				return false;
			}
		}
	},	
	
	getElementsByTagNameNS : function (oNode, sNs, sLocalName) {
		if (oNode.getElementsByTagNameNS) {
			return oNode.getElementsByTagNameNS(sNs, sLocalName);
		} else {
			// TODO: look up vt: prefix
			var oElements = oNode.getElementsByTagName('vt:'+sLocalName);
			var aResult = [];
			for (var i=0; i<oElements.length; i++) {
				var oElm = oElements.item(i);
				if (oElm.namespaceURI == sNs && oElm.nodeName == (oElm.prefix + ':' + sLocalName)) {
					aResult.push(oElm);
				}
			}
		}
		return aResult;
	},
	
	isNodeNS : function (oNode, sNs, sLocalName) {
		if (!Browser.ie) {
			return oNode.namespaceURI == sNs && oNode.localName == sLocalName;
		} else {
			return oNode.namespaceURI == sNs && oNode.nodeName == (oNode.prefix ? (oNode.prefix + ':' + sLocalName) : sLocalName);
		}
	},
	
	hasAttribute : function (oNode, sName) {
		if (oNode.hasAttribute) {
			return oNode.hasAttribute(sName);
		} else {
			for (var i=0; i<oNode.attributes.length; i++) {
				if (oNode.attributes.item(i).nodeName == sName) return true;
			}
			return false;
		}
	},
	
	hasAttributeNS : function (oNode, sNs, sLocalName) {
		if (typeof oNode == 'undefined' || !oNode) {
			//alert('IE vindt dat oNode geen object is: ' + oNode + ', ' + sNs + ', ' + sLocalName);
			return false; // XXX
		} else if (oNode.hasAttributeNS) {
			return oNode.hasAttributeNS(sNs, sLocalName);
		} else {
			for (var i=0; i<oNode.attributes.length; i++) {
				var oAttr = oNode.attributes.item(i);
				if (oAttr.namespaceURI == sNs && oAttr.nodeName == (oAttr.prefix + ':' + sLocalName)) return true;
			}
			return false;
		}
	},
	
	getAttributeNS : function (oNode, sNs, sLocalName) {
		if (oNode.getAttributeNS) {
			return oNode.getAttributeNS(sNs, sLocalName);
		} else {
			for (var i=0; i<oNode.attributes.length; i++) {
				var oAttr = oNode.attributes.item(i);
				if (oAttr.namespaceURI == sNs && oAttr.nodeName == (oAttr.prefix + ':' + sLocalName)) return oAttr.nodeValue;
			}
			return '';
		}
	},
	
	getBody : function() {
		return document.getElementsByTagName('body').item(0);
	}
	
};


// make document.importNode work in IE...
if (!document.importNode) {
	document.importNode = function(oNode, deep) {
		var oNew;
		switch (oNode.nodeType) {
			case Vizami.DOM.ELEMENT_NODE:
				if (oNode.prefix) {
					sNodeName = oNode.nodeName.substring(oNode.nodeName.indexOf(':')+1);
				} else {
					sNodeName = oNode.nodeName;
				}
				oNew = document.createElement(sNodeName);
				for(var i = 0; i < oNode.attributes.length; i++){
					oNew.setAttribute(oNode.attributes[i].name, oNode.attributes[i].value);
				}
				if (oNode.style) oNew.style.cssText = oNode.style.cssText;
				if (oNode.getAttribute('style') != null) oNew.style.cssText = oNode.getAttribute('style');
				if (oNode.getAttribute('class') != null) oNew.className = oNode.getAttribute('class');
				
				if(deep && oNode.hasChildNodes()){
					for(var oChild = oNode.firstChild; oChild; oChild = oChild.nextSibling){
						var oImportedChildNode = document.importNode(oChild, true);
						if (oImportedChildNode) oNew.appendChild(oImportedChildNode);
					}
				}
				break;
			case Vizami.DOM.TEXT_NODE:
				oNew = document.createTextNode(oNode.nodeValue);
				break;
		}
		return oNew;
	};
};



/***************************************************************************/
/**
 * RDF templating function library
 */
Vizami.RDF = {
	NS_RDF : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
	NS_XML : 'http://www.w3.org/XML/1998/namespace',
	NS_VT  : 'http://vips.studenten.net/rdf-templating',
	aTemplates : new Array(),
	aTemplateAppliedCallback : new Array(),
	aTemplateResourceAppliedCallback : new Array(),
	
	/**
	 * Applies all RDF templates for a document or inside a certain Node.
	 * @param {Object} oNode Optional, default: document
	 */
	applyTemplates : function(oNode) {
		if (oNode == null) oNode = document;
		var oTemplates = Vizami.DOM.getHTMLElementsByTagName(oNode, 'vt:template');
		for (var i=0; i<oTemplates.length; i++) {
			this.applyTemplate(oTemplates.item(i));
		}
	},
	
	loadTemplates : function(oNode) {
		var aTemplates = Vizami.DOM.getElementsByTagNameNS(oNode, this.NS_VT, 'template');
		Vizami.Debug.message('loadTemplates: '+aTemplates.length + ' templates found');
		for (var i=0; i<aTemplates.length; i++) {
			this.loadTemplate(aTemplates[i]);
		}
	},

	/**
	 * Sets a callback function to call when a template is applied after loading
	 * Callback function will get the following parameters:
	 * - {Object} oDestination The destination of the template
	 * - {Object} oRDFDocument The RDF document that was applied
	 * @param {Object} oFunc Callback function to call when finished loading
	 * @param {Object} oParam Parameters for callback function
	 */
	setTemplateAppliedCallback : function(oFunc) {
		this.aTemplateAppliedCallback.push(oFunc);
	},
	
	/**
	 * Sets a callback function to call when a node tree was added to the destination
	 * Callback function will get the following parameters:
	 * - {Object} oNode The node (tree) that was added
	 * @param {Object} oFunc Callback function to call when node tree is added
	 * @param {Object} oParam Parameters for callback function
	 */
	setTemplateResourceAppliedCallback : function(oFunc) {
		this.aTemplateResourceAppliedCallback.push(oFunc);
	},
	
	/**
	 * Loads an RDF template
	 * @param {Object} oTemplate Node of the template
	 */
	loadTemplate : function(oXMLTemplate) {
		// compile template
		var oTemplate = new Vizami.RDF.Template(oXMLTemplate);
		var oDestination = oXMLTemplate.parentNode;
		
		// add to hash array (by ID)
		this.aTemplates[oXMLTemplate.getAttribute('id')] = oTemplate;
	},
	
	/**
	 * Gets a template by its ID
	 */
	getTemplate : function(sTemplateID) {
		return this.aTemplates[sTemplateID];
	},
	
	/**
	 * Applies a template to a node
	 * @param {Object} oDestination Destination node
	 * @param {String} sDatasource Path to the datasource (optional)
	 *                 If not given, use vt:datasource attribute on destination
	 * @param {String} sTempalateID ID of the template (optional)
	 *                 If not given, use vt:template attribute on destination
	 */
	applyTemplate : function(oDestination, sDatasource, sTemplateID) {
		var oRDFDocument = new Vizami.RDF.Document();

		if (sDatasource == undefined) {
			if (!Vizami.DOM.hasHTMLAttribute(oDestination,'vt:datasource')) return;
			sDatasource = Vizami.DOM.getHTMLAttribute(oDestination,'vt:datasource');
		}
		if (sTemplateID == undefined) {
			if (!Vizami.DOM.hasHTMLAttribute(oDestination,'vt:template')) return;
			sTemplateID = Vizami.DOM.getHTMLAttribute(oDestination,'vt:template');
		}
		if (!this.getTemplate(sTemplateID)) {
			Vizami.Debug.message("Template not found: ", sTemplateID);
		}

		// load datasources
		if (sDatasource != 'rdf:null') {
			this.applyPending = true;
			var oParam = {
				oTemplate: this.getTemplate(sTemplateID),
				oDestination: oDestination
			};
			oRDFDocument.loadWithCallback(sDatasource, this.applyTemplateCallback, oParam);
		}
	},
	
	/*
	 *
	 * We need to replace, but we do not want a blinking effect, so we are gonna remove the old data
	 * after the a-synchronic calback has been done.
	 *
	 */
	replaceTemplate : function(oDestination, sDatasource, sTemplateID) {
		var oRDFDocument = new Vizami.RDF.Document();

		if (sDatasource == undefined) {
			if (!Vizami.DOM.hasHTMLAttribute(oDestination,'vt:datasource')) return;
			sDatasource = Vizami.DOM.getHTMLAttribute(oDestination,'vt:datasource');
		}
		if (sTemplateID == undefined) {
			if (!Vizami.DOM.hasHTMLAttribute(oDestination,'vt:template')) return;
			sTemplateID = Vizami.DOM.getHTMLAttribute(oDestination,'vt:template');
		}
		if (!this.getTemplate(sTemplateID)) {
			Vizami.Debug.message("Template not found: ", sTemplateID);
		}

		// load datasources
		if (sDatasource != 'rdf:null') {
			this.applyPending = true;
			var oParam = {
				oTemplate: this.getTemplate(sTemplateID),
				oDestination: oDestination
			};
			oRDFDocument.loadWithCallback(sDatasource, this.replaceTemplateCallback, oParam);
		}
	},

	/**
	 *
	 * @private
	 */
	applyTemplateCallback : function(oRDFDocument, oParam) {
		var oTemplate = oParam.oTemplate;
		var oDestination = oParam.oDestination;
		var sAboutRef = Vizami.DOM.getHTMLAttribute(oDestination,'vt:ref');
		if (oTemplate.oTemplate.hasChildNodes) {
			var oDuplicate = document.importNode(oTemplate.oTemplate, true);
			var aDuplicateChildren = Vizami.DOM.nodeListToArray(oDuplicate.childNodes);
			for (var j in aDuplicateChildren) {
				oDestination.appendChild(aDuplicateChildren[j]);
			}
			var oReplace = document.getElementsByTagName('menu')[0];
			// set new destination in parameter for callback
			oParam.oDestination = oReplace.parentNode;
			oParam.oDestination.removeChild(oReplace);
		}
		oRDFDocument.enumerateCallback(sAboutRef, Vizami.RDF.applyTemplateResourceCallback, oParam);
		for (var i in Vizami.RDF.aTemplateAppliedCallback) {
			Vizami.RDF.aTemplateAppliedCallback[i](oDestination, oRDFDocument);
		}
	},
	
	/**
	 *
	 * Replaces nodes, instead of adding.
	 *
	 */
	replaceTemplateCallback : function(oRDFDocument, oParam) {
		var oDestination = oParam.oDestination;

		if(oDestination.hasChildNodes())
		{
			while(oDestination.hasChildNodes())
			{
				oDestination.removeChild(oDestination.firstChild);
   			}
  		}
  		Vizami.RDF.applyTemplateCallback(oRDFDocument, oParam);
	},

	/**
	 *
	 * @private
	 */
	applyTemplateResourceCallback : function(oRDFResource, oParam) {
		Vizami.RDF.applyTemplateResource(oRDFResource, oParam.oTemplate, oParam.oDestination);
	},

	/**
	 * Applies a template to a resource, outputs to oDestination
	 * @param {Object} oRDFResource the RDF resource
	 * @param {Object} oTemplate the template to apply
	 * @param {Object} oDestination the destination for the output
	 */
	applyTemplateResource : function(oRDFResource, oTemplate, oDestination) {
		// transform duplicate's contents
		oTemplate.apply(oRDFResource);
		// import & duplicate template
		var oDuplicate = document.importNode(oTemplate.oRepeatNode, true);
		// copy duplicate into HTML document
		oDestination.appendChild(oDuplicate);
		for (var i in Vizami.RDF.aTemplateResourceAppliedCallback) {
			Vizami.RDF.aTemplateResourceAppliedCallback[i](oDuplicate);
		}
	}


};



/***************************************************************************/
/**
 * Template class, 'compiles' a template and stores direct references
 * to the dynamic nodes to avoid walking the tree every instance.
 */

/**
 * @constructor
 */
Vizami.RDF.Template = function(oTemplate) {
	this.oTemplate = oTemplate;
	this.compile(this.oTemplate);
}

Vizami.RDF.Template.prototype = {
	oTemplate : null,
//	aRDFNodes : new Array(),
//	aRDFSubstitute : new Array(),

	/**
	 * Walks over template recursively, and takes action where necessary.
	 * @public
	 */
	compile : function(oTemplate) {
		this.aRDFNodes = new Array();
		this.aRDFSubstitute = new Array();
		this.oRepeatNode = oTemplate;
		var aChildNodes = Vizami.DOM.nodeListToArray(oTemplate.childNodes);
		for (var i in aChildNodes) {
			this.compileNode(aChildNodes[i], oTemplate);
		}
	},

	/**
	 * Walks over template recursively, and takes action where necessary.
	 * @private
	 */
	compileNode : function(oNode, oTemplate, oOwnerElement) {
		switch (oNode.nodeType) {
			case Vizami.DOM.ELEMENT_NODE:
				switch (oNode.nodeName.toLowerCase()) {
					case 'vt:textnode':
						this.compileTextNodeTag(oNode)
						break;
					case 'vt:attribute':
						this.compileAttributeTag(oNode)
						break;
					default:
						var aAttributes = Vizami.DOM.nodeListToArray(oNode.attributes);
						for (var i in aAttributes) {
							this.compileNode(aAttributes[i], oTemplate, oNode);
						}
						var aChildNodes = Vizami.DOM.nodeListToArray(oNode.childNodes);
						for (var i in aChildNodes) {
							this.compileNode(aChildNodes[i], oTemplate);
						}
				}
				break;
			case Vizami.DOM.ATTRIBUTE_NODE:
				switch (oNode.nodeName.toLowerCase()) {
					case 'vt:uri':
						this.oRepeatNode = oOwnerElement;
						oOwnerElement.removeAttribute('vt:uri');
						var oReplace = oOwnerElement.ownerDocument.createElement('menu');
						oOwnerElement.parentNode.replaceChild(oReplace, oOwnerElement);
						break;
					default:
						if (oNode.nodeValue && oNode.nodeValue.indexOf('rdf:') != -1) {
							this.compileAttribute(oNode);
						}
				}
				break;
		}
	},
	
	/**
	 * Registers a text node for templating.
	 * @private
	 */
	compileTextNodeTag : function(oElmTextNode) {
		var oParent = oElmTextNode.parentNode;
		var oTargetNode = oElmTextNode.ownerDocument.createTextNode('');
		this.aRDFNodes.push({
			sProperty: oElmTextNode.getAttribute('value'),
			oTargetNode: oTargetNode,
			sProcessor: Vizami.DOM.hasAttribute(oElmTextNode, 'processor') ? oElmTextNode.getAttribute('processor') : null
		});
		oParent.replaceChild(oTargetNode, oElmTextNode);
	},
	
	/**
	 * Registers an attribute node for templating.
	 * @private
	 */
	compileAttributeTag : function(oElmAttribute) {
		var sAttributeName = oElmAttribute.getAttribute('name');
		var oTargetNode = oElmAttribute.parentNode.setAttribute(sAttributeName, '');
		this.aRDFNodes.push({
			sProperty: oElmAttribute.getAttribute('value'),
			oTargetNode: oTargetNode,
			sProcessor: Vizami.DOM.hasAttribute(oElmTextNode, 'processor') ? oElmTextNode.getAttribute('processor') : null
		});
		oElmAttribute.parentNode.removeChild(oElmAttribute);
	},
	
	/**
	 * Registers a substitution attribute node for templating.
	 * @private
	 */
	compileAttribute : function(oAttribute) {
		this.aRDFSubstitute.push({
			sProperty: new String(oAttribute.nodeValue),
			oTargetNode: oAttribute
		});
	},
	
	
	
	/**
	 * Populate a template from an RDF resource
	 * @param 
	 */
	apply : function(oResource) {
		for (var i in this.aRDFNodes) {
			this.applyTemplateNode(this.aRDFNodes[i], oResource);
		}
		for (var i in this.aRDFSubstitute) {
			this.applyTemplateSubstitution(this.aRDFSubstitute[i], oResource);
		}
	},
	
	/**
	 * 
	 * @private
	 */
	applyTemplateNode : function(oRDFNode, oResource) {
		var sValue = Vizami.RDF.Document.getValue(oRDFNode.sProperty, oResource);
		if (oRDFNode.sProcessor != null)
			sValue = eval(oRDFNode.sProcessor + '(sValue);');
		oRDFNode.oTargetNode.nodeValue = sValue;
	},
	
	/**
	 * Resolves an attribute value containing RDF property references
	 * See: http://xulplanet.com/tutorials/xultu/templateex.html
	 * @param {String} sAttrValue Property URI
	 * @param {Object} oResource    Resource RDF XML node
	 * @private
	 */
	applyTemplateSubstitution : function(oRDFSubstitute, oResource) {
		var sValue = new String(oRDFSubstitute.sProperty);
		var aMatch = sValue.match(this.attributeTemplateRegexp);
		while (aMatch != null) {
			var sSubValue = Vizami.RDF.Document.getValue(aMatch[1], oResource);
			sValue = sValue.replace(aMatch[0], sSubValue);
			aMatch = sValue.match(this.attributeTemplateRegexp);
		}
		oRDFSubstitute.oTargetNode.nodeValue = sValue;
	},
	
	attributeTemplateRegexp: /[\^]?rdf:([^\s\^]*)\^?/
	
}



/***************************************************************************/
/**
 * RDF Document class, has functions that apply to RDF documents.
 * Supports xml:base
 */

/**
 * @constructor
 */
Vizami.RDF.Document = function() {
	Vizami.Debug.message('new Vizami.RDF.Document()');
}

Vizami.RDF.Document.prototype = {
	sURI : null,
	sURIHash : null,
	oRDFDocument : null,
	
	/**
	 * Load an RDF document, and call a callback function when it's done.
	 * The callback function will be done as follows: fFunction(this, oParam).
	 * @param {String} sURI URI of RDF document
	 * @param {Function} fFunction function to callback
	 * @param {Object} oParam parameters passed to callback function
	 */
	loadWithCallback : function(sURI, fFunction, oParam) {
		Vizami.Debug.message('Loading (RDF): ',sURI);
		this.sURI = sURI;
		this.sURIHash = sURI+'#';
		var oCallback =
		{
			success: this.loadSuccess,
			failure: this.loadFailure,
			argument: {
					fFunction: fFunction,
					oParam: oParam
				},
			scope: this
		}
		YAHOO.util.Connect.asyncRequest('GET', sURI, oCallback, null);
	},
	
	/**
	 *
	 * @private
	 */
	loadFailure : function(oFailure) {
		Vizami.Debug.message("Loading failed (RDF): " + oFailure.status + " - " + oFailure.statusText);
	},
	
	/**
	 *
	 * @private
	 */
	loadSuccess : function(oSuccess) {
		Vizami.Debug.message("Loading succeeded (RDF): " + oSuccess.status + " - " + oSuccess.statusText);
		this.oRDFDocument = oSuccess.responseXML;
		oSuccess.argument.fFunction(this, oSuccess.argument.oParam);
	},
	
	
	
	/**
	 * Enumerate all resources in an RDF container, with a callback function
	 * The callback function will be done as follows: fFunction(oResource, oParam).
	 * @param {String} sAboutURI rdf:about URI of the container
	 * @param {Function} fFunction function to callback
	 * @param {Object} oParam parameters passed to callback function
	 */
	enumerateCallback : function(sAboutURI, fFunction, oParam) {
		var oContainer = this.getResource(sAboutURI);
		if (oContainer) {
			var aResources = Vizami.DOM.getNodeListElements(oContainer.childNodes);
			for (var i in aResources) {
				fFunction(aResources[i], oParam);
			}
		}
	},
	
	
	
	/**
	 * Get an RDF resource node by about URI
	 * @param {String} sAboutURI About URI of resource
	 * @return object
	 */
	getResource : function(sAboutURI) {
		if (!this.oRDFDocument) {
			alert('this.oRDFDocument is null.. not good: ' + sAboutURI);
			return null;
		}
		if (sAboutURI == this.sURI || sAboutURI == this.sURIHash) {
			// if equals document URI, then return root element
			return this.oRDFDocument.documentElement;
		} else {
			var rv = this.checkAboutElm(this.oRDFDocument.documentElement, sAboutURI);
			if (rv) return rv;
			return this.findAboutElm(this.oRDFDocument.documentElement, sAboutURI);
		}
	},
	
	/**
	 * Find 
	 * Note: don't use standard recursive method, this is faster unless about is deep in tree
	 * @param {oElm} element currently checking
	 * @param {String} sAboutURI About URI of resource
	 * @return object
	 * @private
	 */
	findAboutElm : function(oElm, sAboutURI) {
		var aChildElms = Vizami.DOM.getNodeListElements(oElm.childNodes);
		for (var i in aChildElms) {
			var rv = this.checkAboutElm(aChildElms[i], sAboutURI);
			if (rv) return rv;
		}
		for (var i in aChildElms) {
			return this.findAboutElm(aChildElms[i], sAboutURI);
		}
	},
	
	checkAboutElm : function(oElm, sAboutURI) {
		if (Vizami.DOM.hasAttributeNS(oElm, Vizami.RDF.NS_XML, 'base')) {
			// TODO: implement
			// pass along sBase (based on sURI and xml:base)
		}
		if (Vizami.DOM.hasAttributeNS(oElm, Vizami.RDF.NS_RDF, 'about')) {
			if (sAboutURI == this.resolveURI(Vizami.DOM.getAttributeNS(oElm, Vizami.RDF.NS_RDF, 'about')))
				return oElm;
		} else if (Vizami.DOM.hasAttributeNS(oElm, Vizami.RDF.NS_RDF,'ID')) {
			// TODO: implement
		}
		return null;
	},
	
	resolveURI : function(sURI, sBase) {
		return sURI;
		// TODO: implement
		if (sURI.charAt(0) == '#') {
			// fragment
			return sBase + sURI;
		} else if (sURI.charAt(0) == '/') {
			// absolute
			return sBase.match(URLPrefixMatch)[1] + sURI;
		} else if (sURI.match(protocolPrefixMatch)) {
			// fully qualified URI
			return sURI;
		} else {
			// relative
			return sBase.match(fragmentPrefixMatch)[1] + sURI;
		}
		return;
	},
	
	protocolPrefixMatch: /[\d\+\-\.]+:/,
	URLPrefixMatch:      /$(.*?)\/[^\/]/,
	fragmentPrefixMatch: /$(.*\/).*?#/
	
};

/**
 * Get a property value of an RDF resource
 * @param {String} sPropertyURI Property URI
 * @param {Object} oResource    Resource RDF XML node
 * @return string
 */
Vizami.RDF.Document.getValue = function(sPropertyURI, oResource) {
	var aPropertyURISplit = sPropertyURI.split('#');
	var sPropertyNS   = aPropertyURISplit[0];
	var sPropertyName = aPropertyURISplit[1];
	if (Vizami.DOM.hasAttributeNS(oResource, sPropertyNS, sPropertyName)) {
		return Vizami.DOM.getAttributeNS(oResource, sPropertyNS, sPropertyName);
	}
	var aPropertyElements = Vizami.DOM.getNodeListElements(oResource.childNodes);
	for (var i in aPropertyElements) {
		var oPropertyElement = aPropertyElements[i];
		if (Vizami.DOM.isNodeNS(oPropertyElement, sPropertyNS, sPropertyName)) {
			if (Vizami.DOM.hasAttributeNS(oPropertyElement, Vizami.RDF.NS_RDF, 'resource')) {
				return Vizami.DOM.getAttributeNS(oPropertyElement, Vizami.RDF.NS_RDF, 'resource');
			} else {
				return Vizami.DOM.textContent(oPropertyElement);
			}
		}
	}
	return '';
}

/***************************************************************************/
/**
 * Init functions
 */
Vizami.Init = {
	initList : new Array(),
	init : false,
	
	/*
	* @param {e} 
	*/
	onLoad : function(e) { //e is een variabele (event) welke yahoo graag wil meegeven.
		if(Vizami.Init.init) return;
		Vizami.Init.init = true;
		for(var n in Vizami.Init.initList) {
			Vizami.Init.initList[n]();
		}
	},
	
	addInitCallback: function(func) {
		Vizami.Init.initList.push(func);				
	}
}

//This line is needed to call the global variables in Vizami.Init
YAHOO.util.Event.addListener(window, 'load', Vizami.Init.onLoad);
YAHOO.util.Event.addListener(document, 'DOMContentLoaded', Vizami.Init.onLoad);

Vizami.Init.addInitCallback(function() {
		Vizami.CSS.addClass(document.body, "jsenabled");
} );

/***************************************************************************/
/**
 * Change status function
 */
 
function changeStatus(sNew,id){
       	var callback = {
		success: this.statuschanged,
		failure: this.changestatusfail,
		argument: sNew,
		scope: this
	}
        status_url = ("_api/user/" + id + "/status?status=" + sNew);
	YAHOO.util.Connect.asyncRequest('POST', status_url, callback);
}
function changestatusfail(obj) {
	alert("ERROR: Status kon niet worden gewijzigd");
}
function statuschanged(obj) {

	cleanStatus();
	Vizami.Debug.message("status: " + obj.argument)
	var image = document.getElementById("chat"+obj.argument);
	image.setAttribute("src", "_images/chatstatus/s" + obj.argument + ".png");
	image.setAttribute("alt", obj.argument);
//	alert("De status is succesvol gewijzigd");
}

function cleanStatus()
{
	document.getElementById("chatfree").setAttribute("src", "_images/chatstatus/cfree.png");
	document.getElementById("chataway").setAttribute("src", "_images/chatstatus/caway.png");
	document.getElementById("chatbusy").setAttribute("src", "_images/chatstatus/cbusy.png");
}
