/**
 * Basic utility classes.
 * @package benignware.utils
 */
(function(){
	
	var Class = benignware.core.Class;
	
	function Delegate(){
	}
	
	Class.extend(Delegate);
	
	Class.registerClass("benignware.utils.Delegate", Delegate);
	/**
	 * Creates the delegate method.
	 * @static
	 * @method create
	 * @param {Object} scope
	 * @param {function} func
	 */
	Delegate.create = function(scope, func) {
		function delegate() {
			return func.apply(scope, arguments);
		}
		delegate.toString = function() {
			return func.toString();
		}
		return delegate;
	}
	
	return Delegate;
	
})();
(function() {
	
	var Class = benignware.core.Class;
	
	function StringUtils() {
	}
	
	Class.registerClass("benignware.utils.StringUtils", StringUtils);
	
	/* String */
	
	StringUtils.contains = function(str, cnt) {
		return str.indexOf(cnt) >= 0 ? true : false;
	}
	
	StringUtils.trim = function(str) {
		return str ? str.replace(/^\s+|\s+$/g, '') : '';
	}
	
	StringUtils.trimSlashes = function(str) {
		return str ? str.replace(/^\/+|\/+$/g, '') : '';
	}
	
	StringUtils.startsWith = function(str, startStr) {
		return (str.indexOf(startStr) == 0);
	}
	
	StringUtils.getNumbers = function(str) {
		var pattern = /(\d+)/;
		var regex = new RegExp( pattern );
		var match;
		var result = [];
		while (match = regex.exec(str)) {
			match = regex.exec(str);
			result.push(match[1]);
			str = str.substring(match.index + match[0].length);
		}
		return result;
	}
	
	/* Conversion */
	StringUtils.getBoolean = function (str) {
		if (typeof(str)=="string") {
			return (str == "true" || str == "1") ? true : false;
		}
		if (typeof(str)=="boolean") {
			return str;
		}
		return false;
	}
	
	StringUtils.toHex = function(num) {
		if (num == null) return "00";
		num = parseInt(num);
		if (num == 0 || isNaN(num)) return "00";
		num = Math.max(0, num); 
		num = Math.min(num, 255);
		num = Math.round(num);
		return "0123456789ABCDEF".charAt((num - num%16) / 16)
		     + "0123456789ABCDEF".charAt(num%16);
	}
	
	StringUtils.getHexColor = function(r, g, b) {
		return "#" + StringUtils.toHex(r) + StringUtils.toHex(g) + StringUtils.toHex(b);
	}
	
	StringUtils.getRGB = function(hexColor) {
		var r, g, b;
		hexColor = StringUtils.trim(hexColor);
		if (StringUtils.startsWith(hexColor, 'rgb')) {
			var p = /rgb\(([0-9]*)\s*,\s*([0-9]*)\s*,\s*([0-9]*)\)/;
			var reg = new RegExp( p );
			var m = reg.exec(hexColor);
			if (m) {
				r = parseInt(m[1]);
				g = parseInt(m[2]);
				b = parseInt(m[3]);
				return {r: r, g: g, b: b}
			}
		}
		var h = hexColor.charAt(0) == "#" ? hexColor.substring(1) : hexColor;
		r = parseInt(h.substring(0, 2), 16);
		g = parseInt(h.substring(2, 4), 16);
		b = parseInt(h.substring(4, 6), 16);
		return {r: r, g: g, b: b}
	}
	
	
	/*
	StringUtils.stripTags = function(str) {
	   var newString = "";
	   var inTag = false;
	   for(var i = 0; i < str.length; i++) {
	        if(str.charAt(i) == '<') inTag = true;
	        if(str.charAt(i) == '>') {
	              inTag = false;
	              i++;
	        }
	        if(!inTag) newString += str.charAt(i);
	   }
	   return newString;
	}
	*/
	
	StringUtils.stripComments = function (strMod) {
		strMod = strMod.replace(/<!--.*-->/gi, '');
		return strMod;
	}
	
	StringUtils.stripTags = function (strMod) {
		
		//CREATOR: http://www.georgehernandez.com
		 strMod = strMod.replace(/<br\/?>/gi, ' ');
	    if(arguments.length < 3) strMod = strMod.replace(/<\/?(?!\!)[^>]*>/gi, '');
	    else{
	        var IsAllowed=arguments[1];
	        var Specified=eval("["+arguments[2]+"]");
	        if(IsAllowed){
	            var strRegExp='</?(?!(' + Specified.join('|') + '))\b[^>]*>';
	            strMod=strMod.replace(new RegExp(strRegExp, 'gi'), '');
	        }else{
	            var strRegExp='</?(' + Specified.join('|') + ')\b[^>]*>';
	            strMod=strMod.replace(new RegExp(strRegExp, 'gi'), '');
	        }
	    }
	    return strMod;
	}
	
	StringUtils.br2nl = function (str) {
		return str.replace(/<\s*br\s*\/?\s*>/gi, "\n");
	}
	
	StringUtils.nl2br = function (str) {
		return str.replace(/\n/gi, "<br/>");
	}
	
	StringUtils.stripCommentSGML = function(strMod){
		//CREATOR: http://www.georgehernandez.com
	    //Thanks to: http://www.faqts.com/knowledge_base/view.phtml/aid/21761
	    strMod=strMod.replace(/<!(?:--[sS]*?--s*)?>s*/g,''); //double dashes escaped cuz of SGML comment
	    return strMod;
	}
	
	StringUtils.stripHTML = function(str) {
		var matchTag = /<(?:.|\s)*?>/g;
        return str.replace(matchTag, "");
	}
	
	StringUtils.jsonDecode = function(str) {
		var trim = StringUtils.trim(str);
		if (trim) {
			if ((trim.indexOf("{") == 0 || trim.indexOf("[") == 0) && (trim.lastIndexOf("}") == trim.length - 1 || trim.lastIndexOf("]") == str.length - 1)) {
				var jsObject = eval ("(" + trim + ")");
				if (jsObject) {
					return jsObject;
				}
			}
		}
		return str;
	}
	
	StringUtils.capitalize = function(name) {
		return name.substring(0, 1).toUpperCase()+name.substring(1);
	}
	StringUtils.uncapitalize = function(name) {
	 	return name.substring(0, 1).toLowerCase()+name.substring(1);
	}
	StringUtils.leftPad = function(str, len, character) {
		if (typeof(character)=="undefined") character = "0";
		if (str.length>len) return str;
		var chStr = "";
		for (var i=0;i<len-str.length;i++) {
			chStr += character;
		}
		return chStr+str;
	}
	
	StringUtils.findNext = function(str, findArray, offset) {
		match = null;
		for (i = 0;i<findArray.length; i++) {
			find = findArray[i];
			if( (index = str.indexOf(find, offset)) > -1 ) {
				if (match == null || index < match[0]) {
					match = [index, find];
				}
			}
		}
		return match;
	}
	
	StringUtils.parseDate = function(string) {  
		var date = new Date();  
		var parts = String(string).split(/[- :]/);  
		date.setFullYear(parts[0]);  
		date.setMonth(parts[1] - 1);  
		date.setDate(parts[2]);  
		date.setHours(parts[3]);  
		date.setMinutes(parts[4]);  
		date.setSeconds(parts[5]);  
		date.setMilliseconds(0);  
		return date;  
	}  
	
	StringUtils.xmlToString = function(xmlNode) {
		if (typeof(XMLSerializer) != "undefined") {
			return (new XMLSerializer()).serializeToString(xmlNode);
		} else if (xmlNode.xml) {
			return xmlNode.xml;
		}  else if (xmlNode.outerHTML) {
			return xmlNode.outerHTML;
		} else {
			if (xmlNode.nodeType) {
				if (xmlNode.cloneNode) {
					xmlNode = xmlNode.cloneNode(true);
				}
				var str = "<" + xmlNode.nodeName;
				for (var i = 0; i < xmlNode.attributes.length; i++) {
					var attrNode = xmlNode.attributes[i];
					str += " " + attrNode.nodeName + "=\"" + attrNode.nodeValue + "\"";
				}
				if (xmlNode.childNodes.length) {
					str += ">"; 
					for (var i = 0; i < xmlNode.childNodes.length; i++) {
						var child = xmlNode.childNodes[i];
						if (child.nodeType == 1) {
							str += StringUtils.xmlToString(child);
						} else if (child.nodeType == 3) {
							str += child.nodeValue;
						}
					}
					str += "</" + xmlNode.nodeName + ">";
				} else {
					str += "/>";
				}
				return str;
			} else if (xmlNode.length) {
				var str = "";
				for (var i = 0; i < xmlNode.length; i++) {
					var child = xmlNode[i];
					str+= StringUtils.xmlToString(child);
				}
				return str;
			}
		}
		return null;
	}
	
	StringUtils.stringToXML = function(xmlstring) {
	 	if(typeof(DOMParser) != "undefined") {
	    	return new DOMParser().parseFromString(xmlstring, "text/xml");
	  	} else if(typeof(ActiveXObject) != 'undefined') {
			var xmldata = new ActiveXObject('MSXML.DomDocument');
			xmldata.async = false;
			xmldata.loadXML(xmlstring);
		 	return xmldata;
		} else if(typeof(XMLHttpRequest) != 'undefined') {
			var xmldata = new XMLHttpRequest;
			if(!contentType) {
				contentType = 'application/xml';
			}
			xmldata.open('GET', 'data:' + contentType + ';charset=utf-8,' + encodeURIComponent(str), false);
			if(xmldata.overrideMimeType) {
				xmldata.overrideMimeType(contentType);
			}
			xmldata.send(null);
			return xmldata.responseXML;
		}
		return null;
	}
	
	StringUtils.fragmentToString = function(frag) {
		var temp = document.createElement("span");
		temp.appendChild(frag);
		return temp.innerHTML;
	}
	
	StringUtils.stringToFragment = function(str) {
		var temp = document.createElement("div");
		temp.innerHTML = str;
		var frag = document.createDocumentFragment();
	  	for (var i = 0; i<temp.childNodes.length; i++) {
	     	frag.appendChild(temp.childNodes[i].cloneNode(true));
		}
		return frag;
	}

	StringUtils.sprintf = function(format){
		for (var i = 1;i<arguments.length; i++) {
			format = format.replace(/%[A-Za-z]/, arguments[i]);
		}
		return format;
	}
	
	StringUtils.sscanf = function(str, format) {
		if (str == null) return "";
		var searches = format.split(/%[A-Za-z]/);
		var values = [], value;
		for (var i = 0;i < searches.length; i++) {
			if (searches[i]) {
				var index = str.indexOf(searches[i]);
				if (index >= 0) {
					value = str.substring(0, index);
					if (value) values.push(str.substring(0, index));
					str = str.substring(index + searches[i].length);
				}
			} else {
				values.push(str);
			}
		}
		if (values.length) return searches.length > 2 ? values : values[0];
		return "";
	}
	
	/* VALIDATION */
	
	StringUtils.isEmail = function(str) {
		return str.match(/(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/);
	}
	
	
	/* FILE STRINGS */
	
	
	StringUtils.getFileName = function(str) {
		var arr = str.split("/");
		return arr.pop();
	}
	
	StringUtils.getFileExtension = function(str) {
		str = StringUtils.getFileName(str);
		var arr = str.split(".");
		return arr.pop();
	}
	
	StringUtils.stripFileExtension = function(str) {
		str = StringUtils.getFileName(str);
		var arr = str.split(".");
		arr.pop();
		return arr.join(".");
	}
	
	// deprecated
	StringUtils.isFilename = function(str) {
		return this.isFile(str);
	}
	
	StringUtils.isFile = function(str) {
		var v = new RegExp();
		//v.compile(/^/?(?:[A-Za-z0-9_-]+/)*[A-Za-z0-9_-]+\\.[A-Za-z0-9]+$/gi);
		v.compile(/[^,{}]*\.[\w]+(?:\?.*)?$/gi);
		//v.compile(/^[a-zA-Z0-9]+\.[a-zA-Z]+$/i);
		return v.test(str);
	}
	
	StringUtils.getDirectoryName = function(str) {
		var arr = str.split("/");
		arr.pop();
		str = arr.join("/");
		return str;
	}
	
	/* URL */
	
	StringUtils.isLocalDomain = function(url) {
		var hostname = StringUtils.getHostname(url);
		if (!hostname || hostname == window.location.hostname) {
			return true;
		}
		return false;
	}
	
	StringUtils.isLocalPath = function(url) {
		if (!StringUtils.isRootPath(url) && !StringUtils.isURL(url)) {
			return true;
		}
		return false;
	}
	
//	StringUtils.getLocalPath = function(file1, file2) {
//		if (StringUtils.isLocalPath(file2)) {
//			var dir = StringUtils.getDirectoryName(file1);
//			var url = dir ? dir + "/" + file2 : file2;
//			return url;
//		}
//		return file2;
//	}


	StringUtils.isRelativePath = function (str) {
		var bool = (!StringUtils.isURL(str) && !StringUtils.isRootPath(str));
		return bool;
	}
	
	StringUtils.isURL = function (str) {
		var v = new RegExp();
		v.compile("^[A-Za-z]+://[A-Za-z0-9-_]+\\.[A-Za-z0-9-_%&\?\/.=]+$"); 
    	return v.test(str);
	}

	StringUtils.isRootPath = function (str) {
		var v = new RegExp();
		v.compile("^/");
		//[A-Za-z0-9-_]+\\.[A-Za-z0-9-_%&\?\/.=]+
    	return v.test(str);
	}
	
	StringUtils.getHostname = function(str) {
		if (!str) return null;
		var re = new RegExp('^(?:f|ht)tp(?:s)?\://([^/]+)', 'im');
		var match = str.match(re);
		return match ? str.match(re)[1].toString() : null;
	}
	
	/**
	 * Extracts the specified parameter from an url.
	 * @method getURLParameter
	 * @param {String} str
	 * @param {String} name
	 */
	
	StringUtils.parseURL = function(url) {
		var result = {protocol: "", host: "", pathname:"", queryString:"", params:[]};
		var pattern = /^\s*(?:([a-z]*\:)\/\/([^\\\/\.]*))?([^\?]*)?(.*)/i;
		var regex = new RegExp( pattern );
		var match = regex.exec(url);
		if (match) {
			result.protocol = match[1];
			result.host = match[2];
			result.pathname = match[3];
			result.queryString = match[4];
		}
		if (result.queryString) {
			var params = [];
			var queryStr = StringUtils.startsWith(result.queryString, "?") ? result.queryString.substring(1) : result.queryString; 
			if (queryStr) {
				var pairs = queryStr.split("&");
				for (var i = 0; i < pairs.length; i++) {
					var pair = pairs[i].split("=");
					params[pair[0]] = pair[1];
				}
			}
			result.params = params;
		}
		var base = result.protocol ? result.protocol + "//" + result.host : "";
		base+= result.pathname;
		result.base = base;
		return result;
	}
	
	StringUtils.buildURL = function(base, params){
		var url = base;
		var first = false;
		for (var x in params) {
			if (params[x] != null) {
				url += !first ? "?" : "&";
				first = true;
				url += x + "=" + params[x];
			}
		}
		return url;
	}
	
	StringUtils.getURLParameter = function(str, name) {
		if (arguments.length==1) {
			str = window.location.href;
			name = arguments[0];
		}
		try {
			  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
			  var regexS = "[\\?&]"+name+"=([^&#]*)";
			  var regex = new RegExp( regexS );
			  var results = regex.exec( str );
			  if( results == null )
			    return "";
			  else
			    return unescape(results[1]);
		} catch (e) {
		}
		return null;
	}
	
	StringUtils.concatPaths = function (p1, p2) {
		if (StringUtils.startsWith(p2, "/")) {
			return p2;
		}
		
		
		var dir = StringUtils.isFilename(p1) ? StringUtils.getDirectoryName(p1) : p1;
		// trim slashes
		
		if (StringUtils.startsWith(p2, dir)) {
			p2 = p2.substr(dir.length);
		}
		p2 = p2.replace(/^\/+|\/+$/g, '');
		
		var str = dir + "/" + p2;
		str = StringUtils.getCanonicalPath(str);
		return str;
	}
	
	StringUtils.getCanonicalPath = function (url) {
		var url = url.replace("\\", "/");
		if (StringUtils.contains(url, "..")) {
			var urlInfo = StringUtils.parseURL(url);
			var split = urlInfo.pathname.split("/");
			for (var i = 0; i < split.length; i++) {
				var part = split[i];
				if (part && !StringUtils.isFilename(part)) {
					if (part == "..") {
						if (i > 1) {
							split.splice(i - 1, 2);
							i+=2;
						}
					}
				}
			}
			var host = urlInfo.protocol ? urlInfo.protocol + "//" + urlInfo.host : "";
			url = host + urlInfo.pathname + urlInfo.queryString;
		}
		return url;
	}
	
	return StringUtils;
})();
(function() {
	
	var Class = benignware.core.Class;
	
	function ArrayUtils() {
	}
	
	Class.registerClass("benignware.utils.ArrayUtils", ArrayUtils);
	
	ArrayUtils.intersect = function(array1, array2) {
		var a1 = array1.slice(0);
		var a2 = array2.slice(0);
		var result = [];
		for (var i=0;i<a1.length;i++){ 
	        for (var j=0;j<a2.length;j++) if (a1[i]==a2[j]) {
				a2.splice(j--, 1);
				result.push(a1[i]);
				break;
			}
	    }
		return result;
	}
	
	ArrayUtils.subtract = function(array1, array2) {
		var a1 = array1.slice(0);
		var a2 = array2.slice(0);
		for (var i=0;i<a1.length;i++){ 
			var found = false;
	        for (var j=0;j<a2.length;j++) if (a1[i]==a2[j]) {
				//a2.splice(j--, 1);
				found = true;
				break;
			}
			if (found) {
				a1.splice(i--, 1);
			}
	    }
		return a1;
	}
	
	ArrayUtils.contains = function(array, value) {
		for (var i=0;i<array.length;i++) if (array[i]==value) return true;
		return false;
	}
	
	ArrayUtils.indexOf = function(array, value) {
		for (var i=0;i<array.length;i++) if (array[i]==value) return i;
		return -1;
	}
	
	ArrayUtils.remove = function(a, value) {
		for (var i=0;i<a.length;i++) if (a[i]==value) a.splice(i--, 1);
		return a;
	}
	
	ArrayUtils.unique = function(a) {
		var result = [];
		for (var i = 0; i < a.length; i++) {
			if (!ArrayUtils.contains(result, a[i])) result.push(a[i]);
		}
		return result;
	}
	
	ArrayUtils.merge = function(a1, a2) {
		var result = a1.concat(a2);
		return ArrayUtils.unique(result);
	}
	
	ArrayUtils.concat = function(a1, a2) {
		return a1.concat(a2);
	}
	
	ArrayUtils.equals = function(a1, a2) {
		var result = [];
		if (typeof(a1) != typeof(a2)) return false;
		if (typeof(a1) == "object" || typeof(a1) == "array") {
			for (var x in a1) {
				var res = ArrayUtils.equals(a1[x], a2[x]);
				if (!res) return false;
			}
			for (var x in a2) {
				var res = ArrayUtils.equals(a2[x], a1[x]);
				if (!res) return false;
			}
			return true;
		}
		return (a1 == a2);
	}
	
	ArrayUtils.clone = function(a) {
		var result = [];
		for (var x in a) {
			var child = a[x];
			if (typeof(child) == "object" || typeof(child) == "array") {
				result[x] = ArrayUtils.clone(child);
			} else {
				result[x] = child;
			}
		}
		return result;
	}
	
	return ArrayUtils;
	
})();
	
(function(){
	/*
	 * private class StyleSheet
	 */
	 
	var Class = benignware.core.Class;
	var Element = Class.require("benignware.core.Element");
	//
	function StyleSheet(ref) {
		this.__nativeStyleSheet = ref;
		this.cssRules = this.getCSSRules();
	}
	 
	Class.registerClass("benignware.utils.StyleSheet", StyleSheet);
	 
	StyleSheet.prototype.getCSSRules = function () {
		try {
			if (this.__nativeStyleSheet.rules) {
				return this.__nativeStyleSheet.rules;
			} else if (this.__nativeStyleSheet.cssRules) {
				return this.__nativeStyleSheet.cssRules;
			}
		} catch (e) {}
		return null;
	}
	
	StyleSheet.prototype.addRule = function (cssSelector, cssText) {
		try {
			if (this.__nativeStyleSheet.addRule) {
				this.__nativeStyleSheet.addRule(cssSelector, cssText);
			} else if (this.__nativeStyleSheet.insertRule) {
				this.__nativeStyleSheet.insertRule(cssSelector+" {"+cssText+"}", this.__nativeStyleSheet.cssRules.length);
			}
		} catch (e) {
			
		}
		return this.getRule(cssSelector);
	}
	
	StyleSheet.prototype.getRule = function (cssSelector) {
		if (this.cssRules) {
			for (var i = 0; i < this.cssRules.length; i++) {
				var rule = this.cssRules[i];
				if (rule.selectorText == cssSelector) {
					return rule;
				}
			}
		}
		return null;
	}
	
	StyleSheet.prototype.setRule = function (cssSelector, cssText) {
		for (var i = 0; i < this.cssRules.length; i++) {
			var rule = this.cssRules[i];
			if (rule.selectorText == cssSelector) {
				return rule;
			}
		}
		return null;
	}
	
	StyleSheet.prototype.setStyle = function (cssSelector, styleName, styleValue) {
		var cssRule = this.getRule(cssSelector);
		if (!cssRule) return;
		cssRule.style[styleName] = styleValue;
	}
	
	
	
	return StyleSheet;
})();
/**
 * Cross-browser implementation for stylesheet access in an html-document. 
 * Use this class to set a component's default styles.
 * @class benignware.utils.CSS
 * @extends benignware.core.Object
 */
(function() {
	
	var Class = benignware.core.Class;
	var Element = Class.require("benignware.core.Element");
	var StyleSheet = Class.require("benignware.utils.StyleSheet");
	//
	function camelCase(str) {
		var a = str.split("-");
		var result = "";
		for (var i=0;i<a.length;i++) {
			var s = a[i];
			if (i>0) {
				s = s.charAt(0).toUpperCase()+s.substring(1);
			}
			result+=s;
		}
		return result;
	}
	
	function minusSeparated(str) {
		var a = [], u = 0;
		for (var i=0;i<str.length;i++) {
			var c = str.charAt(i);
			if (c.toLowerCase()!=c) {
				a.push(str.substring(u, i).toLowerCase());
				u = i;
			}
		}
		a.push(str.substring(u, str.length).toLowerCase());
		return a.join("-");
	}

	var cssInstances = [];
	var defaultStyles = [];
	/**
	 * Retrieves a css-object for the specified document
	 * @constructor
	 * @param {HTMLDocument} doc
	 */
	function CSS(doc) {
		
		doc = doc && doc.ownerDocument ? doc.ownerDocument : doc ? doc : document;
		for (var i = 0; i < cssInstances.length; i++) if (cssInstances[i].document == doc) return cssInstances[i];
		cssInstances.push(this);
		//
		this.document = doc;
		
		for (var i = 0;i < defaultStyles.length; i++) {
			var s = defaultStyles[i];
			this.setStyle(s.selector, s.name, s.value);
		}
		
		return this;
	}
	
	Class.registerClass("benignware.utils.CSS", CSS);
	
	CSS = Class.extend(CSS);
	
	
	// deprecated
	CSS.get = function(doc) {
		return new CSS(doc);
	}
	
	
	/**
	 * Retrieves a css-object for the specified document
	 * @static
	 * @method initialize
	 * @param {HTMLDocument} doc
	 * @return {benignware.utils.CSS} the CSS-object for the specified document.
	 */
	CSS.getInstance = function(doc) {
		return new CSS(doc);
	}

	// deprecated
	CSS.initialize = function(doc) {
		return new CSS(doc);
	}
	
	/**
	 * Retrieves the stylesheet object at the specified index.
	 * @method get
	 * @return {Stylesheet}
	 */
	/*
	CSS.prototype.getStyleSheet = function (index) {
		if (!this.document.styleSheets.length) return null;
		return new StyleSheet(this.document.styleSheets[index]);
	}
	*/
	 
	CSS.prototype.getStyleSheets = function() {
		var result = [];
		for (var i = 0; i < document.styleSheets.length; i++) {
			var styleSheet = new StyleSheet(document.styleSheets[i]);
			result.push(styleSheet);
		}
		return result;
	 }
	 
	CSS.prototype.getStyleSheetAt = function(index) {
		if (this.__styleSheet == null) {
			var styleElement = this.document.createElement("style");
			var headElement = this.document.getElementsByTagName("head")[0];
			if (headElement != null) {
				if (headElement.childNodes.length) {
					headElement.insertBefore(styleElement, headElement.firstChild);
				} else {
					headElement.appendChild(styleElement);
				}
			}
			this.__styleSheet = new StyleSheet(this.document.styleSheets[this.document.styleSheets.length - 1]);
		} 
		return this.__styleSheet;
	}
	
	
	// deprecated
	CSS.prototype.getStyleSheet = function(index) {
		return this.getStyleSheetAt(index);
	}
	
	CSS.prototype.__styleSheet = null;
	/**
	 * Inserts a style for the specified css selector as a default style rule.
	 * @method setDefaultStyle
	 * @param {Object} cssSelector
	 * @param {Object} styleName
	 * @param {Object} styleValue
	 */
	CSS.prototype.setStyle = function(cssSelector, styleName, styleValue) {
		var camelCaseName = camelCase(styleName);
		var minusSeparatedName = minusSeparated(styleName);
		var stylesheet = this.getStyleSheet();
		var cssRule = stylesheet.getRule(cssSelector);
		if (!cssRule) {
			var cssText = "" + minusSeparatedName + ": " + styleValue + ";";
			//if (camelCaseName!=minusSeparatedName) cssText+=" "+minusSeparatedName+": "+styleValue+";";
			cssText+= "";
			cssRule = stylesheet.addRule(cssSelector, cssText);
		}
		if (!cssRule) return;
		if (!cssRule.style[minusSeparatedName]) {
			stylesheet.setStyle(cssSelector, minusSeparatedName, styleValue);
		}
		if (!cssRule.style[camelCaseName]) {	
			stylesheet.setStyle(cssSelector, camelCaseName, styleValue);
		}
	}
	
	// TODO implement
	CSS.prototype.getStyle = function(cssSelector, styleName) {
		
	}
	
	CSS.prototype.getElementStyle = function(elem, styleName) {
		
	}
	
	CSS.prototype.processStyle = function(styleName, callback) {
		var styleSheets = this.getStyleSheets();
		for (var i = 0; i < styleSheets.length; i++) {
			var styleSheet = styleSheets[i];
			var cssRules = styleSheet.getCSSRules();
			
			for (var x in cssRules) {
				var rule = cssRules[x];
				
				if (rule.style) {
					var value = rule.style[styleName];
					if (typeof(value) != "undefined") {
						// MATCH
						var cssSelector = rule.selectorText;
						var affectedElements = this.cssQuery(cssSelector);
					}
				}
			}
		}
	}
	
	
	// deprecated
	CSS.prototype.setDefaultStyle = function(cssSelector, styleName, styleValue){
		this.setStyle(cssSelector, styleName, styleValue);
	}
	
	CSS.setDefaultStyle = function(cssSelector, styleName, styleValue) {
		for (var i = 0; i < cssInstances.length; i++) {
			var css = cssInstances[i];
			css.setStyle(cssSelector, styleName, styleValue);
		}
		defaultStyles.push({selector: cssSelector, name: styleName, value:styleValue});
	}
	
	CSS.measureTextWidth = function(element, text) {
		var testElem = document.createElement("div");
		testElem.style.position = "absolute";
		testElem.style.fontSize = Element.getComputedStyle(element, "font-size");
		testElem.style.fontFamily = Element.getComputedStyle(element, "font-family");
		testElem.style.padding = "0px";
		testElem.style.margin = "0px";
		testElem.style.border = "1px solid black";
		document.body.appendChild(testElem);
		//testElem.appendChild(document.createTextNode(text));
		testElem.innerHTML = text;
		var offsetWidth = testElem.offsetWidth - 2;
		document.body.removeChild(testElem);
		return offsetWidth > 0 ? offsetWidth : 0;
	}
	
	CSS.initialize();



// querying of a DOM document using CSS selectors (a getElementsByTagName on steroids)
// see http://dean.edwards.name/my/cssQuery.js.html
/*
    License: http://creativecommons.org/licenses/by/1.0/
    Author:  Dean Edwards/2004
    Web:     http://dean.edwards.name/
*/
CSS.prototype.cssQuery = function() { 

    var version = "1.0.1"; // timestamp: 2004/05/25

    // constants
    var STANDARD_SELECT = /^[^>\+~\s]/;
    var STREAM = /[\s>\+~:@#\.]|[^\s>\+~:@#\.]+/g;
    var NAMESPACE = /\|/;
    var IMPLIED_SELECTOR = /([\s>\+~\,]|^)([\.:#@])/g;
    var ASTERISK ="$1*$2";
    var WHITESPACE = /^\s+|\s*([\+\,>\s;:])\s*|\s+$/g;
    var TRIM = "$1";
    var NODE_ELEMENT = 1;
    var NODE_TEXT = 3;
    var NODE_DOCUMENT = 9;

    // sniff for explorer (cos of one little bug)
    var isMSIE = /MSIE/.test(navigator.appVersion), isXML;

    // cache results for faster processing
    var cssCache = {};

    // this is the query function
    function cssQuery(selector, from) {
        if (!selector) return [];
        var useCache = arguments.callee.caching && !from;
        from = (from) ? (from.constructor == Array) ? from : [from] : [document];
        isXML = false;//checkXML(from[0]);
        // process comma separated selectors
        var selectors = parseSelector(selector).split(",");
        var match = [];
        for (var i in selectors) {
            // convert the selector to a stream
            selector = toStream(selectors[i]);
            // process the stream
            var j = 0, token, filter, cacheSelector = "", filtered = from;
            while (j < selector.length) {
                token = selector[j++];
                filter = selector[j++];
                cacheSelector += token + filter;
                // process a token/filter pair
                filtered = (useCache && cssCache[cacheSelector]) ? cssCache[cacheSelector] : select(filtered, token, filter);
                if (useCache) cssCache[cacheSelector] = filtered;
            }
            match = match.concat(filtered);
        }
        // return the filtered selection
        return match;
    };
    cssQuery.caching = false;
    cssQuery.reset = function() {
        cssCache = {};
    };
    cssQuery.toString = function () {
        return "function cssQuery() {\n  [version " + version + "]\n}";
    };

    var checkXML = (isMSIE) ? function(node) {
        if (node.nodeType != NODE_DOCUMENT) node = node.document;
        return node.mimeType == "XML Document";
    } : function(node) {
        if (node.nodeType == NODE_DOCUMENT) node = node.documentElement;
        return node.localName != "HTML";
    };

    function parseSelector(selector) {
        return selector
        // trim whitespace
        .replace(WHITESPACE, TRIM)
        // encode attribute selectors
        .replace(attributeSelector.ALL, attributeSelector.ID)
        // e.g. ".class1" --> "*.class1"
        .replace(IMPLIED_SELECTOR, ASTERISK);
    };

    // convert css selectors to a stream of tokens and filters
    //  it's not a real stream. it's just an array of strings.
    function toStream(selector) {
        if (STANDARD_SELECT.test(selector)) selector = " " + selector;
        return selector.match(STREAM) || [];
    };

    var pseudoClasses = { // static
        // CSS1
        "link": function(element) {
            for (var i = 0; i < document.links; i++) {
                if (document.links[i] == element) return true;
            }
        },
        "visited": function(element) {
            // can't do this without jiggery-pokery
        },
        // CSS2
        "first-child": function(element) {
            return !previousElement(element);
        },
        // CSS3
        "last-child": function(element) {
            return !nextElement(element);
        },
        "root": function(element) {
            var document = element.ownerDocument || element.document;
            return Boolean(element == document.documentElement);
        },
        "empty": function(element) {
            for (var i = 0; i < element.childNodes.length; i++) {
                if (isElement(element.childNodes[i]) || element.childNodes[i].nodeType == NODE_TEXT) return false;
            }
            return true;
        }
        // add your own...
    };

    var QUOTED = /([\'\"])[^\1]*\1/;
    function quote(value) {return (QUOTED.test(value)) ? value : "'" + value + "'"};
    function unquote(value) {return (QUOTED.test(value)) ? value.slice(1, -1) : value};

    var attributeSelectors = [];

    function attributeSelector(attribute, compare, value) {
        // properties
        this.id = attributeSelectors.length;
        // build the test expression
        var test = "element.";
        switch (attribute.toLowerCase()) {
            case "id":
                test += "id";
                break;
            case "class":
                test += "className";
                break;
            default:
                test += "getAttribute('" + attribute + "')";
        }
        // continue building the test expression
        switch (compare) {
            case "=":
                test += "==" + quote(value);
                break;
            case "~=":
                test = "/(^|\\s)" + unquote(value) + "(\\s|$)/.test(" + test + ")";
                break;
            case "|=":
                test = "/(^|-)" + unquote(value) + "(-|$)/.test(" + test + ")";
                break;
        }
        push(attributeSelectors, new Function("element", "return " + test));
    };
    attributeSelector.prototype.toString = function() {
        return attributeSelector.PREFIX + this.id;
    };
    // constants
    attributeSelector.PREFIX = "@";
    attributeSelector.ALL = /\[([^~|=\]]+)([~|]?=?)([^\]]+)?\]/g;
    // class methods
    attributeSelector.ID = function(match, attribute, compare, value) {
        return new attributeSelector(attribute, compare, value);
    };

    // select a set of matching elements.
    // "from" is an array of elements.
    // "token" is a character representing the type of filter
    //  e.g. ">" means child selector
    // "filter" represents the tag name, id or class name that is being selected
    // the function returns an array of matching elements
    function select(from, token, filter) {
        //alert("token="+token+",filter="+filter);
        var namespace = "";
        if (NAMESPACE.test(filter)) {
            filter = filter.split("|");
            namespace = filter[0];
            filter = filter[1];
        }
        var filtered = [], i;
        switch (token) {
            case " ": // descendant
                for (i in from) {
					if(typeof from[i]=='function') continue;
                    var subset = getElementsByTagNameNS(from[i], filter, namespace);
                    for (var j = 0; j < subset.length; j++) {
                        if (isElement(subset[j]) && (!namespace || compareNamespace(subset[j], namespace)))
                            push(filtered, subset[j]);
                    }
                }
                break;
            case ">": // child
                for (i in from) {
                    var subset = from[i].childNodes;
                    for (var j = 0; j < subset.length; j++)
                        if (compareTagName(subset[j], filter, namespace)) push(filtered, subset[j]);
                }
                break;
            case "+": // adjacent (direct)
                for (i in from) {
                    var adjacent = nextElement(from[i]);
                    if (adjacent && compareTagName(adjacent, filter, namespace)) push(filtered, adjacent);
                }
                break;
            case "~": // adjacent (indirect)
                for (i in from) {
                    var adjacent = from[i];
                    while (adjacent = nextElement(adjacent)) {
                        if (adjacent && compareTagName(adjacent, filter, namespace)) push(filtered, adjacent);
                    }
                }
                break;
            case ".": // class
                filter = new RegExp("(^|\\s)" + filter + "(\\s|$)");
                for (i in from) if (filter.test(from[i].className)) push(filtered, from[i]);
                break;
            case "#": // id
                for (i in from) if (from[i].id == filter) push(filtered, from[i]);
                break;
            case "@": // attribute selector
                filter = attributeSelectors[filter];
                for (i in from) if (filter(from[i])) push(filtered, from[i]);
                break;
            case ":": // pseudo-class (static)
                filter = pseudoClasses[filter];
                for (i in from) if (filter(from[i])) push(filtered, from[i]);
                break;
        }
        return filtered;
    };

    var getElementsByTagNameNS = (isMSIE) ? function(from, tagName) {
        return (tagName == "*" && from.all) ? from.all : from.getElementsByTagName(tagName);
    } : function(from, tagName, namespace) {
        return (namespace) ? from.getElementsByTagNameNS("*", tagName) : from.getElementsByTagName(tagName);
    };

    function compareTagName(element, tagName, namespace) {
        if (namespace && !compareNamespace(element, namespace)) return false;
        return (tagName == "*") ? isElement(element) : (isXML) ? (element.tagName == tagName) : (element.tagName == tagName.toUpperCase());
    };

    var PREFIX = (isMSIE) ? "scopeName" : "prefix";
    function compareNamespace(element, namespace) {
        return element[PREFIX] == namespace;
    };

    // return the previous element to the supplied element
    //  previousSibling is not good enough as it might return a text or comment node
    function previousElement(element) {
        while ((element = element.previousSibling) && !isElement(element)) continue;
        return element;
    };

    // return the next element to the supplied element
    function nextElement(element) {
        while ((element = element.nextSibling) && !isElement(element)) continue;
        return element;
    };

    function isElement(node) {
        return Boolean(node.nodeType == NODE_ELEMENT && node.tagName != "!");
    };

    // use a baby push function because IE5.0 doesn't support Array.push
    function push(array, item) {
        array[array.length] = item;
    };

    // fix IE5.0 String.replace
    if ("i".replace(/i/,function(){return""})) {
        // preserve String.replace
        var string_replace = String.prototype.replace;
        // create String.replace for handling functions
        var function_replace = function(regexp, replacement) {
            var match, newString = "", string = this;
            while ((match = regexp.exec(string))) {
                // five string replacement arguments is sufficent for cssQuery
                newString += string.slice(0, match.index) + replacement(match[0], match[1], match[2], match[3], match[4]);
                string = string.slice(match.lastIndex);
            }
            return newString + string;
        };
        // replace String.replace
        String.prototype.replace = function (regexp, replacement) {
            this.replace = (typeof replacement == "function") ? function_replace : string_replace;
            return this.replace(regexp, replacement);
        };
    }

    return cssQuery;
}();

	
	return CSS;
})();


/**
 * AJAX Loader class 
 * @class benignware.utils.Loader
 * @extends benignware.events.EventDispatcher
 */
(function() {
	
	var Class = benignware.core.Class;
	var Event = Class.require("benignware.events.Event");
	var EventDispatcher = Class.require("benignware.events.EventDispatcher");
	var Delegate = Class.require("benignware.utils.Delegate");
	var StringUtils = Class.require("benignware.utils.StringUtils");
	
	var __super;
	
	function getXMLHttpRequest() {
		var httpRequest;
		if (typeof XMLHttpRequest != undefined) {
	   		httpRequest = new XMLHttpRequest();
	  	} else if (typeof ActiveXObject != undefined) {
	      	httpRequest = new ActiveXObject('Microsoft.XMLHTTP');
		}
		return httpRequest;
	}
	
	/**
	 * Constructs a new loader.
	 * @constructor
	 * @param {String} method
	 * @param {Boolean} async
	 */
	function Loader(method, async) {
		__super.call(this);
		this.requestHeaders = [];
		this.loadQueue = [];
		this.method = (method!=undefined) ? method : "GET";
		this.async = (async!=undefined) ? async : true;
		this.variables = [];
		this.responseText;
		this.responseXML;
		this.httpRequest = getXMLHttpRequest();
		//
		this.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		this.setRequestHeader('Cache-Control', 'no-cache');
	}
	 
	Class.registerClass("benignware.utils.Loader", Loader);
	
	Class.extend(EventDispatcher, Loader);
	
	__super = Class.getSuper(Loader);
	
	/**
	 * An object containing variables that are posted to the server.
	 * @property variables
	 * @return {Object}
	 */
	Loader.prototype.isLoading = false;
	
	Loader.prototype.url = null;
	
	Loader.prototype.variables = null;
	
	Loader.prototype.preventCaching = false;
	
	Loader.prototype.responseText = null;
	
	Loader.prototype.responseXML = null;
	
	Loader.prototype.loadQueue = null;
	
	Loader.prototype.requestHeaders = null;
	
	Loader.prototype.getQueue = function() {
		return this.loadQueue;
	}
	
	Loader.prototype.setRequestHeader = function(name, value) {
		this.requestHeaders[name] = value;
	}
	
	Loader.prototype.getRequestHeader = function(name) {
		return this.requestHeaders[name];
	}
	
	Loader.prototype.getURL = function() {
		return this.url;
	}
	
	/**
	 * Sends a http request to the server
	 * @method load
	 * @param {String} filename
	 */
	
	function loaded(req, filename) {
		var loadEvent = Event.create("load", false, false);
		loadEvent.filename = filename;
		loadEvent.responseText = req.responseText;
		loadEvent.responseXML = req.responseXML;
		this.responseXML = req.responseXML;
		this.responseText = req.responseText;
		this.dispatchEvent(loadEvent);
		this.loaded();
	}
	
	function load() {
		this.isLoading = true;
		if (!this.loadQueue.length) return;
		var filename = this.loadQueue.shift();
		
		var req = getXMLHttpRequest.call(this);
		this.url = filename;
		if (this.preventCaching) {
			var params = [];
			params['__preventCaching'] = new Date().getTime();
			filename = StringUtils.buildURL(filename, params);
		}
		req.open(this.method, filename, this.async);
		for (var x in this.requestHeaders) {
			req.setRequestHeader(x, this.requestHeaders[x]);
		}
		var target = this;
		req.onreadystatechange = function() {
			if (req.readyState == 4) {
				target.isLoading = false;
				if (req.status == 200) {
					loaded.call(target, req, filename);
				} else {
					var ioErrorEvent = Event.create("error", false, false);
					ioErrorEvent.statusCode = req.status;
					target.dispatchEvent(ioErrorEvent);
				}
				if (target.loadQueue.length == 0) {
					var completeEvent = Event.create("complete", false, false);
					completeEvent.responseText = req.responseText;
					completeEvent.responseXML = req.responseXML;
					target.dispatchEvent(completeEvent);
					target.complete();
				} else {
					load.call(target, filename);
				}
			}
		}
		// variables
		var urlQuery = "";
		for (var x in this.variables) {
			urlQuery+= (!urlQuery.length) ? "" : "&";
			urlQuery+= x+"="+this.variables[x];
		}
		
		var loadStartEvent = Event.create("loadstart", false, false);
		loadStartEvent.url = filename;
		this.dispatchEvent(loadStartEvent);
		req.send(urlQuery ? urlQuery : null);
		if (this.async == false) {
			loaded.call(this, req, filename);
		}
		
	}
	
	Loader.prototype.setParam = function(name, value) {
		this.variables[name] = value;
	}
	
	Loader.prototype.load = function(filename) {
		if (filename) {
			this.loadQueue.push(filename);
			if (!this.isLoading) {
				load.call(this);
			}
		}
	}
	
	Loader.prototype.loaded = function() {
	}
	
	Loader.prototype.complete = function() {
		
	}
	
	return Loader;
})();

/**
 * AJAX PropertyFile class 
 * @class benignware.utils.PropertyFile
 * @extends benignware.events.EventDispatcher
 */
(function() {
	
	var Class = benignware.core.Class;
	var Event = Class.require("benignware.events.Event");
	var EventDispatcher = Class.require("benignware.events.EventDispatcher");
	var Delegate = Class.require("benignware.utils.Delegate");
	var StringUtils = Class.require("benignware.utils.StringUtils");
	var Loader = Class.require("benignware.utils.Loader");
	
	var __super;
	
	function PropertyFile() {
		__super.call(this);
		this.__properties = [];
		this.addEventListener("load", Delegate.create(this, loadHandler));
	}
	 
	Class.registerClass("benignware.utils.PropertyFile", PropertyFile);
	
	Class.extend(Loader, PropertyFile);
	
	__super = Class.getSuper(PropertyFile);
	
	PropertyFile.prototype.__properties = [];
	
	function loadHandler(event) {
		this.parse(event.responseText);
	}
	
	PropertyFile.prototype.parse = function(str) {
		var pattern = /(\"|')|(?:\s*([a-z0-9_\.]*)\s*=\s*)|(\r|\n|;)/gi;
		var regex = new RegExp( pattern);
		var match;
		var literal = null;
		var offset = -1;
		var substr = null;
		var name = null;
		while (offset < str.length) {
			match = regex.exec(str);
			if (match == null) {
				break;
			} else {
				substr = str.substring(offset, match.index);
				offset = match.index + match[0].length;
				if (match[1]) {
					if (!literal) {
						literal = match[1];
					} else {
						if (str.substr(match.index - 1, 1) == "\\") {
							substr+= match[0];
						} else if (literal == match[1]) {
							literal = null;
						}
					}
				} else if (match[2]) {
					if (!literal) {
						name = match[2];
						if (typeof(this.__properties[name]) == "undefined") {
							this.__properties[name] = "";
						}
					} else {
						substr+= match[0];
					}
				} else if (!literal && match[3]) {
					name = null;
				}
				if (name) {
					this.__properties[name]+= substr;
				}
			}
		}
	}
	
	PropertyFile.prototype.setProperty = function(name, value) {
		this.__properties[name] = value;
	}
	
	PropertyFile.prototype.getProperty = function(name) {
		return this.__properties[name];
	}
	
	PropertyFile.prototype.getProperties = function() {
		return this.__properties;
	}
	
	PropertyFile.prototype.setProperties = function(properties) {
		this.__properties = properties;
	}
	
	return PropertyFile;
})();
/**
 * Provides methods for handling the DOM.
 * Use this class to register a cross-browser DOMContentLoaded-Event.
 * @class benignware.utils.DOM
 * @extends benignware.events.EventDispatcher
 */
benignware.core.Class.registerClass("benignware.utils.DOM", (function(){
	
	var Class = benignware.core.Class;
	var EventDispatcher = Class.require("benignware.events.EventDispatcher");
	var Event = Class.require("benignware.events.Event");
	var Element = Class.require("benignware.core.Element");
	var Delegate = Class.require("benignware.utils.Delegate");
	var StringUtils = Class.require("benignware.utils.StringUtils");
	var ArrayUtils = Class.require("benignware.utils.ArrayUtils");
	
	var __super;
	/**
	 * Retrieves the dom-object for the specified html-document.
	 * @constructor
	 * @param {HTMLDocument} doc
	 */
	
	var domObjects = [];
	
	function DOM(doc) {
		doc = typeof(doc) == "undefined" ? document : doc; 
		for (var i=0;i<domObjects.length;i++) if (domObjects[i].document == doc) return domObjects[i];
		domObjects.push(this);
		initialize.call(this, doc);
		return this;
	}
	
	Class.extend(EventDispatcher, DOM);
	__super = Class.getSuper(DOM);
	
	
	// STATIC METHODS
	
	DOM.getInstance = function(doc) {
		return new DOM(doc);
	}
	
	DOM.getDocument = function(xml) {
		return xml.documentElement ? xml : xml.ownerDocument;
	}
	
	DOM.getDocumentElement = function(xml) {
		var doc = xml.ownerDocument ? xml.ownerDocument : xml;
		if (doc) return doc.documentElement;
		return null;
	}
	
	DOM.isDocumentElement = function(xml) {
		return (xml && xml.ownerDocument && xml.ownerDocument.documentElement == xml);
	}
	
	DOM.removeChildren = function(xml) {
		if (xml) {
			for (var i = 0; i < xml.childNodes.length; i++) {
				var child = xml.childNodes[i];
				xml.removeChild(child);
				i--;
			}
		}
	}
	
	DOM.prepend = function(parent, elem) {
		if (parent.length && typeof(parent) != "string") {
			for (var i = 0; i < parent.length; i++) {
				DOM.prepend(parent[i], elem);
			}
			return;
		}
		if (parent.insertBefore) {
			if (typeof(elem) == "string") {
				var doc = parent.ownerDocument;
				elem = doc.createElement(elem);
			}
			parent.insertBefore(elem, parent.firstChild);
		}
	}
	
	/**
	 * Checks if the specified element is a childnode of the specified parent.
	 * @static
	 * @method isChildOf
	 * @param {benignware.core.Element} child
	 * @param {benignware.core.Element} parent
	 * @return {Boolean} true if the specified element is a childnode of the specified parent
	 */
	DOM.isChildOf = function(child, parent) {
		if (parent == child) return false;
		var c = child;
		try {
			while (c) {
				//if (c==child.ownerDocument.body) return false;
				if (child.ownerDocument != null && c == child.ownerDocument.documentElement) return false;
				if (c.parentNode == parent) return true;
				if (c.parentNode == null) return false; 
				c = c.parentNode;
				
			}
		} catch (e) {
			//console.error(e);
		}
		return false;
	}
	 
	 
	// html element finder
	DOM.$ = function(expr) {
		var doc = document;
		// id:
		var idElement = DOM.getElementById(doc, expr);
		if (idElement) {
			return idElement;
		}
	}
	
	DOM.getElementById = function(element, attrValue){
		return DOM.getElementsByAttribute(element, "id", attrValue)[0];
	}
	
	DOM.getElementsByTagName = function(element, tagName, recursive){
		return DOM.getElementsByTagNameNS(element, null, tagName, recursive);
	}
	
	/**
	 * Cross-browser getElementsByAttribute method.
	 * @static
	 * @method getElementsByAttribute
	 * @param {Element} xml
	 * @param {benignware.core.Element} element
	 * @param {StringUtils} attrName
	 * @return {Boolean} true if the element contains the specified attribute node
	 */
	DOM.getElementsByAttribute = function(element, attrName, attrValue) {
		var result = [];
		if (!element) return result;
		for (var i=0;i<element.childNodes.length;i++) {
			var node = element.childNodes[i];
			if (node.nodeType == 1) {
				if (DOM.hasAttribute(node, attrName)) {
					var value = node.getAttribute(attrName);
					if (typeof(attrValue) == "undefined") {
						result.push(node);
					} else if (value == attrValue) {
						result.push(node);
					}
				}
				var r = DOM.getElementsByAttribute(node, attrName, attrValue);
				if (r.length) result = result.concat(r);
			}
		}
		return result;
	}
	 
	
	/**
	 * converts xml to an object
	 */
	DOM.getObject = function(elem, listTags, attributesAsProperties, preserveXML, callback, obj) {
		
		elem = elem.documentElement ? elem.documentElement : elem;
		listTags = listTags && listTags.length ? listTags : []; 
		attributesAsProperties = typeof(attributesAsProperties) == "boolean" ? attributesAsProperties : true; 
		preserveXML = typeof(preserveXML) == "boolean" ? preserveXML : false; 
		var isListTag = (ArrayUtils.contains(listTags, elem.nodeName));
		
		obj = obj ? obj : isListTag ? [] : {}
		if (preserveXML) {
			obj['@type'] = elem.nodeName;
		}
		var children = isListTag ? obj : [];
		var attributes = [];
		for (var i = 0; i < elem.attributes.length; i++) {
			var attrNode = elem.attributes[i];
			if (attributesAsProperties) {
				obj[attrNode.name] = attrNode.value;
			} else {
				attributes[attrNode.name] = attrNode.value;
			}
		}
		if (!attributesAsProperties && elem.attributes.length) {
			obj['@attributes'] = attributes;
		}
		
		for (var i = 0; i < elem.childNodes.length; i++) {
			var childElem = elem.childNodes[i];
			if (childElem.nodeType == 1) {
				var name = childElem.nodeName;
				if (childElem.childNodes.length == 1 && childElem.firstChild.nodeType == 3) {
					var value = childElem.firstChild.nodeValue;
					if (isListTag) {
						children.push(value);
					} else {
						obj[name] = value;
					}
				} else {
					child = DOM.getObject(childElem, listTags, attributesAsProperties, preserveXML, callback);
					if (isListTag) {
						children.push(child);
					} else {
						obj[name] = child;
					}
				}
			}
		}
		if (!isListTag && children.length) {
			obj['@children'] = children;
		}
		if (callback) {
			var retObj = callback(elem, obj);
			obj = retObj ? retObj : obj;
		}
		return obj;
	}
	
	DOM.getElements = function(elem){
		var result = [];
		if (elem) {
			for (var i = 0; i < elem.childNodes.length; i++) {
				var child = elem.childNodes[i];
				if (child.nodeType == 1) result.push(child);
			}
		}
		return result;
	}
	
	DOM.stripWhitespace = function(elem){
		var result = [];
		if (elem) {
			for (var i = 0; i < elem.childNodes.length; i++) {
				var child = elem.childNodes[i];
				if (child.nodeType == 3 && StringUtils.trim(child.nodeValue) == "") {
					elem.removeChild(child);
					i--;
				} else {
					result.push(child);
				}
			}
		}
		return result;
	}
	
	DOM.getTextNodes = function(elem){
		var result = [];
		if (elem) {
			for (var i = 0; i < elem.childNodes.length; i++) {
				var child = elem.childNodes[i];
				if (child.nodeType == 3) result.push(child);
			}
		}
		return result;
	}
	
	DOM.getText = function(elem) {
		var result = "";
		if (elem) {
			for (var i = 0; i < elem.childNodes.length; i++) {
				var child = elem.childNodes[i];
				if (child.nodeType == 3) result+= child.nodeValue;
			}
		}
		return result;
	}
	
	DOM.getAncestors = function(elem){
		var result = [];
		while (elem.parentNode) {
			result.push(elem.parentNode);
			elem = elem.parentNode;
		}
		return result;
	}
	
	DOM.getCommonAncestor = function(node1, node2) {
		
		var node;
		if (node1.length) {
			var nodes = node1;
			var commonAncestor = null;
			for (var i = 0; i < nodes.length; i++) {
				node = nodes[i];
				if (!commonAncestor) {
					commonAncestor = node.parentNode;
				} else {
					commonAncestor = DOM.getCommonAncestor(node, commonAncestor);
				}
				
			}
			return commonAncestor;
		} else {
			node = node1;
			while (node != null) {
				if (DOM.isChildOf(node2, node)) return node;
				node = node.parentNode;
			}
		}
		return null;
	}
	
	DOM.getDeepestElement = function (element) {
		while (element) {
			var nodes = DOM.stripWhitespace(element);
			if (nodes.length == 1 && nodes[0].nodeType == 1) {
				element = nodes[0];
			} else {
				break;
			}
		}
		return element;
	}
	
	DOM.getWrapperElement = function (child) {
		var parent = child.parentNode;
		var result = null;
		while(parent && parent.childNodes.length == 1) {
			result = parent;
			parent = parent.parentNode;
		}
		return result;
	}
	
	DOM.getEmptyContainer = function (elem) {
		var emptyElement;
		while (elem) {
			if (DOM.isEmptyElement(elem)) {
				emptyElement = elem;
				elem = elem.parentNode;
			} else break;
		}
		return emptyElement;
	}
	
	DOM.isEmptyElement = function(elem) {
		if (elem.nodeType == 3) {
			if (!elem.nodeValue.length) {
				return true;
			}
			return false;
		}
		if (elem.nodeType == 1) {
			if (!elem.childNodes.length || elem.childNodes.length == 0 || elem.childNodes.length == 1 && elem.firstChild.nodeType == 1 && elem.firstChild.nodeName.toLowerCase() == "br") {
				return true;
			}
			var emptyChildren = true;
			for (var i = 0; i < elem.childNodes.length; i++) {
				var child = elem.childNodes[i];
				if (!DOM.isEmptyElement(child)) {
					emptyChildren = false;
					break;
				}
			}
			if (emptyChildren) {
				return true;
			}
		}
		return false;
	}
	
	DOM.getPreviousElement = function (elem) {
		var prevSib = elem.previousSibling;
		if (prevSib) {
			if (prevSib.childNodes.length > 0) {
				return prevSib.childNodes[prevSib.childNodes.length - 1];
			}
			return prevSib;
		}
		return null;
	}
	
	DOM.getChildIndex = function(child) {
		var parent = child.parentNode;
		if (parent) {
			for (var i = 0;i<parent.childNodes.length; i++) {
				if (parent.childNodes[i] == child) {
					return i;
				}
			}
			return -1;
		}
	}
	
	
	/**
	 * Cross-browser hasAttribute method.
	 * @static
	 * @method hasAttribute
	 * @param {benignware.core.Element} element
	 * @param {StringUtils} attrName
	 * @return {Boolean} true if the element contains the specified attribute node
	 */
	DOM.hasAttribute = function(element, attrName) {
		 var attrNode = element.getAttributeNode(attrName);
		 if (attrNode && attrNode.specified) return true;
		 return false;
	}
	
	DOM.getValue = function(element) {
		if (element && element.nodeType == 1 && element.firstChild && element.firstChild.nodeType == 3) {
			return element.firstChild.nodeValue;
		}
		return null;
	}
	
	DOM.getLocalName = function(elem) {
		var localName = null;
		if (elem.baseName) {
			localName = elem.baseName;
		} else if (elem.localName) {
			localName = elem.localName;
		} else if (elem.nodeName) {
			localName = elem.nodeName;
		}
		return localName;
	}
	
	DOM.XML_NAMESPACE = "http://www.w3.org/2000/xmlns/";
	
	DOM.getBaseURL = function(xml) {
		var doc = DOM.getDocument(xml);
		if (doc) {
			var baseURI = doc.baseURI;
			if (baseURI) {
				var urlInfo = StringUtils.parseURL(baseURI);
				var baseURL = urlInfo.base;
				return baseURL;
			}
		}
		return null;
	}
	
	DOM.getNamespaceByPrefix = function (xml, prefix) {
		if (!xml || !prefix) {
			return null;
		}
		var docElem = DOM.getDocumentElement(xml);
		for (var i = 0; i < docElem.attributes.length; i++) {
			var attrNode = docElem.attributes[i];
			var localName = DOM.getLocalName(attrNode);
			if (attrNode.namespaceURI == DOM.XML_NAMESPACE && localName == prefix) {
				return attrNode.value;
			}
		}
		return null;
	}
	
	DOM.getNamespacePrefix = function (xml, namespaceURI) {
		var docElem = DOM.getDocumentElement(xml);
		for (var i = 0; i < docElem.attributes.length; i++) {
			var attrNode = docElem.attributes[i];
			var localName = DOM.getLocalName(attrNode);
			if (attrNode.namespaceURI == DOM.XML_NAMESPACE && attrNode.value == namespaceURI) {
				return attrNode.localName;
			}
		}
		return "";
	}
	
	DOM.getLocalNameByQName = function(qName) {
		var nameArr = qName.split(":");
		var localName = nameArr.pop();
		return localName;
	}
	
	DOM.getPrefixByQName = function(qName) {
		var nameArr = qName.split(":");
		var localName = nameArr.pop();
		var nsPrefix =  nameArr[0];
		return nsPrefix;
	}
	
	DOM.getNamespaceByQName = function(xml, qName) {
		var nsPrefix = DOM.getPrefixByQName(qName);
		var nsURI = DOM.getNamespaceByPrefix(xml, nsPrefix);
		return nsURI;
	}
	
	DOM.addNamespace = function(xml, prefix, namespaceURI) {
		var nsPrefix = DOM.getNamespacePrefix(xml, namespaceURI);
		if (!nsPrefix) {
			var doc = xml.ownerDocument;
			var attr = doc.createAttributeNS(DOM.XML_NAMESPACE, "xmlns:" + prefix);
			attr.nodeValue = namespaceURI;
			doc.documentElement.setAttributeNodeNS(attr);
		}
	}
	
	DOM.getElementsByTagNameNS = function(element, namespaceURI, tagName, recursive) {
		recursive = (typeof(recursive) != "undefined") ? recursive : true;
		var result = [];
		if (!element) return result;
		tagName = tagName.toLowerCase();
		element = (typeof(element.ownerDocument) == "undefined") ? element.documentElement : element;
		for (var i=0;i<element.childNodes.length;i++) {
			var node = element.childNodes[i];
			if (node.nodeType == 1) {
				if (namespaceURI && node.namespaceURI == namespaceURI || !namespaceURI) {
					var localName = DOM.getLocalName(node);
					
					if (localName != null && localName.toLowerCase() == tagName) { 
						result.push(node);
					}
				}
				if (recursive) {
					var r = DOM.getElementsByTagNameNS(node, namespaceURI, tagName, recursive);
					if (r.length) result = result.concat(r);
				}
			}
		}
		return result;
	}
	
	DOM.getElementsByClass = function(element, classObj){
		if (typeof(classObj) == "string") {
			var win = Element.getDefaultView(element);
			var C = win && win.benignware && win.benignware.core && win.benignware.core.Class ? win.benignware.core.Class : Class;
			classObj = C.getClass(classObj);
		}
		var result = [];
		for (var i = 0; i < element.childNodes.length; i++) {
			var node = element.childNodes[i];
			if (node.nodeType == 1) {
				if (Class.instanceOf(node, classObj)) {
					result.push(node);
				}
				var r = DOM.getElementsByClass(node, classObj);
				if (r.length > 0) result = result.concat(r);
			}
		}
		return result;
	}
	
	DOM.getParentByClass = function(element, classObj) {
		if (element) {
			var node = element.parentNode;
			while(node) {
				if (node && Class.instanceOf(node, classObj)) return node;
				node = node.parentNode;
			}
		}
		
		return null;
	}
	
	DOM.getParentByTagName = function(element , nodeName) {
		if (element == null) return false;
		var node = element;
		while(node != null && node.parentNode != null) {
			node = node.parentNode;
			if (node.nodeName.toLowerCase() == nodeName.toLowerCase()) return node;
			if (node == element.ownerDocument) break;
		}
		return null;
	}
	
	DOM.isAdded = function(element) {
		return isAdded(element);
	}

	/**
	 * Checks whether the specified element is added to the DOM. 
	 * @static
	 * @method isAddedToDOM
	 * @param {benignware.core.Element} element
	 * @return {Boolean} true if the specified element is added to the DOM
	 */
	DOM.isAddedToDOM = function(element) {
		return isAddedToDOM(element);
	}

	
	DOM.generateId = function(doc, name, attribute, count){
		var c = (typeof(count) != "undefined") ? count : 1;
		var attrName = (typeof(attribute) != "undefined" || attribute != null) ? attribute : "id";
		var uniqueName = name+c;
			while (DOM.getElementsByAttribute(doc, attrName, uniqueName).length > 0) {
			c++;
			uniqueName = name+c;
			if (c > 20) {
				break;
			}
		}
		return uniqueName;
	}
	
	DOM.isAddedToDOM = function(element) {
		if (element == null) return false;
		var node = element;
		while(node != null && node.parentNode != null) {
			node = node.parentNode;
			if (node == element.ownerDocument) return true;
		}
		return false;
	}
	
	
	function dispatchEventRec(element, event, rec) {
		if (rec) {
			for (var i=0;i<element.childNodes.length;i++) {
				var child = element.childNodes[i];
				if (child.nodeType == 1) {
					dispatchEventRec(child, event, rec);
				}
			}
		}
		Element.dispatchEvent(element, event);
	}
	
	
	// initialize DOMEvents added, addedToDOM, removed

	function initializeDOMElement(element, rec) {
		var doc = element.ownerDocument;
		var nativeAppChild = element.appendChild;
		element.appendChild = function(child) {
			var removeFlag = child.parentNode ? true : false;
			var result = callNative(this, nativeAppChild, arguments);
			if (removeFlag) {
				dispatchEventRec(child, Event.create(doc, "removed", true, false), false);
			}
			dispatchEventRec(child, Event.create(doc, "added", true, false), false);
			
			if (DOM.isChildOf(element, doc.documentElement)) {
				dispatchEventRec(child, Event.create(doc, "addedToDOM", false, false), true);
				
			}
			return result;
		}
		
		var nativeInsBefore = element.insertBefore;
		element.insertBefore = function(child, beforeNode) {
			var removeFlag = child.parentNode ? true : false;
			var result = callNative(this, nativeInsBefore, arguments);
			if (removeFlag) {
				dispatchEventRec(child, Event.create(doc, "removed", true, false), false);
			}
			dispatchEventRec(child, Event.create(doc, "added", true, false), false);
			if (DOM.isChildOf(element, doc.documentElement)) {
				dispatchEventRec(child, Event.create(doc, "addedToDOM", false, false), true);
			}
			return result;
		}
		
		
		var nativeRemChild = element.removeChild;
		element.removeChild = function(child) {
			var result = callNative(this, nativeRemChild, arguments);
			dispatchEventRec(child, Event.create(doc, "removed", true, false), false);
			return result;
		}

		var nativeCloneNode = element.cloneNode;
		element.cloneNode = function() {
			var result = callNative(this, nativeCloneNode, arguments);
			initializeDOMElement(result);
			return result;
		}

		if (rec) {
			for (var i = 0;i<element.childNodes.length;i++) {
				var child = element.childNodes[i];
				if (child.nodeType == 1) initializeDOMElement(child, rec)
			}
		}
	}
	
	function initializeDOM(doc) {
		var nativeCreateElem = doc.createElement;
		doc.createElement = function() {
			var element = callNative(doc, nativeCreateElem, arguments);
			initializeDOMElement(element);
			return element;
		}
		initializeDOMElement(doc.body, true);
	}
	
		// initialize doc instance
	
	function initialize(doc) {
		this.document = doc;
		this.__domContentLoaded = false;
		var target = this;
		
		function callback() {
			if (target.__domContentLoaded) return;
			target.__domContentLoaded = true;
			var element = doc.documentElement;
			var domContentLoadedEvent = Event.create(doc, "DOMContentLoaded", false, false);
			target.dispatchEvent(domContentLoadedEvent);
		}
		
		var win = doc.defaultView ? doc.defaultView : doc.parentWindow ? doc.parentWindow : null;

		// http://www.kryogenix.org/days/2007/09/26/shortloaded
		(function(i) {var u =navigator.userAgent;var e=/*@cc_on!@*/false; var st = 
			setTimeout;if(/webkit/i.test(u)){st(function(){var dr=document.readyState;
			if(dr=="loaded"||dr=="complete"){i()}else{st(arguments.callee,10);}},10);}
			else if((/mozilla/i.test(u)&&!/(compati)/.test(u)) || (/opera/i.test(u))){
			document.addEventListener("DOMContentLoaded",i,false); } else if(e){     (
			function(){var t=document.createElement('doc:rdy');try{t.doScroll('left');
			i();t=null;}catch(e){st(arguments.callee,0);}})();}else{window.onload=i;}})(callback);
		
	}
	
	DOM.prototype.isDOMContentLoaded = function() {
		this.__domContentLoaded;
	}
	
	// INITIALIZE DOCUMENT
	var dom = DOM.getInstance(document);
	dom.addEventListener("DOMContentLoaded", function(event) {
		//initializeDOM(document);
	});
	
	return DOM;
})());
(function(){
	
	var Class = benignware.core.Class;
	var EventDispatcher = Class.require("benignware.events.EventDispatcher");
	var Event = Class.require("benignware.events.Event");
	var Element = Class.require("benignware.core.Element");
	var Delegate = Class.require("benignware.utils.Delegate");
	var StringUtils = Class.require("benignware.utils.StringUtils");
	var DOM = Class.require("benignware.utils.DOM");
	
	var __super;
	
	/**
	 * Retrieves the dom-object for the specified html-document.
	 * @constructor
	 * @param {HTMLDocument} doc
	 */
	
	var docInstances = [];
	
	function HTMLAnnotationsParser(doc) {
		doc = typeof(doc) == "undefined" ? document : doc; 
		for (var i = 0; i < docInstances.length; i++) {
			if (docInstances[i].document == doc) {
				return docInstances[i];
			}
		}
		objects.push(this);
		initialize.call(this, doc);
		return this;
	}
	
	HTMLAnnotationsParser.allowScripting = true;
	HTMLAnnotationsParser.autoParseComments = true;
	
	HTMLAnnotationsParser.prototype.__allowScripting = undefined;
	HTMLAnnotationsParser.prototype.__autoParseComments = undefined;
	
	Class.registerClass("benignware.utils.HTMLAnnotationsParser", HTMLAnnotationsParser);

	function initialize() {
		// INITIALIZE DOCUMENT
	}
	
	HTMLAnnotationsParser.prototype.setAutoParseComments = function(bool) {
		this.__autoParseComments = bool;
	}
	
	HTMLAnnotationsParser.prototype.getAutoParseComments = function() {
		return this.__autoParseComments != undefined ? this.__autoParseComments : HTMLAnnotationsParser.autoParseComments;
	}
	
	HTMLAnnotationsParser.prototype.setAllowScripting = function(bool) {
		this.__allowScripting = bool;
	}
	
	HTMLAnnotationsParser.prototype.getAllowScripting = function() {
		return this.__allowScripting != undefined ? this.__allowScripting : HTMLAnnotationsParser.allowScripting;
	}

	HTMLAnnotationsParser.parseComments = function(doc, callback, useDefault) {
		if (typeof (callback) == "function") {
			callback = [callback];
		} else if (typeof(callback) == "undefined") {
			callback = [];
		}
		if (typeof(useDefault) == "undefined" || typeof(useDefault) != "undefined" && useDefault) {
			callback.push(parseComment);
		}
		parseComments(doc.documentElement, callback);
	}
	
	function parseComments(element, callback) {
		var children = DOM.stripWhitespace(element);
		
		for (var i = 0; i < children.length; i++) {
			var child = children[i];
			
			if (child.nodeType == 8) {
				var comment = child.nodeValue;
				if (comment) {
					var nextSibling = children[i + 1];
					var elem = null;
					if (nextSibling && nextSibling.nodeType == 1) {
						if (nextSibling.nodeName.toLowerCase() != "iframe") {
							parseComments(nextSibling, callback);
							elem = DOM.isChildOf(nextSibling, nextSibling.ownerDocument.body) ? nextSibling : nextSibling.ownerDocument.body;
							i++;
						}
					}
					elem = elem && elem.nodeType == 1 ? elem : null;
					for (var a = 0;a < callback.length; a++) {
						if (typeof(callback[a]) == "function") callback[a](comment, elem);
					}
				}
			} else if (child.nodeType == 1 && child.nodeName.toLowerCase() != "iframe") {
				parseComments(child, callback);
			}
			
		}
	}
	
	function parseComment(comment, element) {
		
		if (!element || element.nodeType != 1) return;
		var str = comment;
		var tags = [];
		var pattern = /(\"|')|(?:@(script|class|documentClass|attribute|set|event))/;
		var regex = new RegExp( pattern );
		var match;
		var literal = null;
		var currentIndex = 0;
		var className = null;
		var lastTag = null;
		while (str.length > 0) {
			match = regex.exec(str);
			if (match == null) {
				break;
			} else {
				if (match[1]) {
					literal = (literal != null && literal==match[1]) ? null : match[1];
				} else {
					if (literal == null) {
						var tagName = match[2];
						if (match[2]) {
							if (tags.length > 0) {
								var lastTag = tags[tags.length-1];
								if (lastTag != null) {
									tags[tags.length-1].value = comment.substring(lastTag.index+lastTag.name.length+1, currentIndex+match.index);
								}
							}
						}
						tags.push({name: tagName, value: "", index:currentIndex+match.index});
					}
				}
				str = str.substring(match.index+match[0].length);
				currentIndex+= match.index+match[0].length;
			}
		}
		
		if (tags.length > 0) {
			var lastTag = tags[tags.length-1];
			if (lastTag != null) {
				tags[tags.length-1].value += comment.substring(lastTag.index+lastTag.name.length+1);
			}
		}
		
		for (var i = 0; i < tags.length; i++) {
			if (tags[i].name == "class" || tags[i].name == "documentClass") {
				className = StringUtils.trim(tags[i].value);
				if (tags[i].name == "documentClass") {
					element = Element.prototype.getDocument.call(element).body;
				}
			}
		}
		
		if (className != null) {
			var classObj = Class.getClass(className);
			if (classObj != null) {
				element = Element.initialize(element, classObj);
			}
		}
		
		for (var i = 0;i<tags.length;i++) {
			var tagValue = tags[i].value;
			var tagName = tags[i].name;
			switch (tagName) {
				case "class":
				case "documentClass":
					break;
					
				case "script":
					if (this.getAllowScripting()) {
						var f = function(){ eval(tagValue); }
						f.call( element );
					}
					break;
					
				case "set":
				case "attribute":
				case "event":
					var p = /\s*([a-zA-Z]*)\s*(.*)/gi;
					var reg = new RegExp( p );
					var m = reg.exec(tagValue);
					var name = m[1];
					var value = StringUtils.trim(tagValue.substring(m.index + m[1].length+1));
					if (tagName == "set") {
						callSetter(element, name, value);
					} else if (tagName == "attribute"){
						element.setAttribute(name, value);
					} else if (tagName == "event") {
						if (this.getAllowScripting()) {
							var ef = function(event) {
								eval(value);
							}
							Element.addEventListener(element, name, Delegate.create(element, function(event) {
								ef.call(element, event); 
							}), false);
						}
					}
					break;
			}
		}
	}
	
	// Helper Methods
	
	function callSetter(obj, name, value) {
		if (!obj) {
			console.error("SETTER OBJECT MISSING", obj, name, value);
			return;
		}
		var setterName = "set" + StringUtils.capitalize(name);
		if (typeof(obj[setterName]) == "function") {
			obj[setterName].call(obj, value);
			return;
		}
		if (typeof(obj[name]) != "undefined" && typeof(obj[name]) != "function") {
			switch (typeof(obj[name])) {
				case "boolean":
					obj[name] = StringUtils.getBoolean(value);
					return;
				default: 
					obj[name] = value;
					return;
			}
		}
	}
	
	function callNative(target, method, args) {
		if (typeof(args) == "undefined") args = [];
		
		try {
			return method.apply(target, args);
		} catch (e) {
			try {
				return method(args[0], args[1], args[2], args[3], args[4], args[5]);
			} catch (e) {
			}
		}
		
		
		return null;
	}
	
	
	/* CURRENT DOCUMENT */
	var dom = DOM.getInstance(document);
	dom.addEventListener("DOMContentLoaded", function(event) {
		HTMLAnnotationsParser.parseComments(document);
	});
	
	return HTMLAnnotationsParser;
	
})();
(function() {
	
	var Class = benignware.core.Class;
	var StringUtils = Class.require("benignware.utils.StringUtils");
	var Event = Class.require("benignware.events.Event");
	var Element = Class.require("benignware.core.Element");
	var DOM = Class.require("benignware.utils.DOM");
	var ArrayUtils = Class.require("benignware.utils.ArrayUtils");
	
	var instances = [];
	var instance = null;
	var offsetX = 10;
	var offsetY = 10;
	
	function DragManager(doc) {
		doc = Element.prototype.getDocument.call(doc) || document;
		for (var i=0;i<instances.length;i++) if (instances[i].__document == doc) return instances[i];
		this.__document = doc;
		instances.push(this);
	}
	
	DragManager.getInstance = function(doc) {
		return new DragManager(doc);
	}
	
	Class.registerClass("benignware.utils.DragManager", DragManager);
	
	DragManager.prototype.__isDragging = false;
	DragManager.prototype.__document = null;
	DragManager.prototype.__dragInstance = null;
	DragManager.prototype.__feedbackNode = null;
	DragManager.prototype.__dragOverElement = null;
	DragManager.prototype.__dragOverAncestors = [];
	DragManager.prototype.__dragObjects = [];
	
	var mouseDownInstance = null;
	
	DragManager.prototype.initialize = function(dragInstance, dragArea){
		
		if (ArrayUtils.contains(this.__dragObjects, dragInstance)) return;
		this.__dragObjects.push(dragInstance);
		
		//
		var dragManager = this;
		
		function mouseDownHandler(event){
			event = Event.getEvent(event);
			
			var isEditable = Element.isEditable(event.target);
			if (!isEditable && !mouseDownInstance) {
				Element.addEventListener(dragManager.__document, "mousemove", mouseMoveHandler, false);
				Element.addEventListener(dragManager.__document, "mouseup", mouseUpHandler, false);
				mouseDownInstance = dragInstance;
			}
			event.target.focus();
			if (!Element.isEditable(event.target)) {
				event.preventDefault();
			}
			
			//return false;
			
		}
		dragArea = dragArea || dragInstance;
		
		dragArea.style.cursor = "pointer";
		
		Element.addEventListener(dragArea, "mousedown", mouseDownHandler, false);
		
		function mouseMoveHandler(event) {
			event = Event.getEvent(event);
			var isEditable = Element.isEditable(mouseDownInstance);
			mouseDownInstance = null;
			if (isEditable) return;
			var clone;
			if (!dragManager.__isDragging) {
				
				dragManager.__isDragging = true;
				clone = dragInstance.cloneNode(true);
				dragManager.__document.body.appendChild(clone); 
				clone.style.position = "absolute";
				clone.style.opacity = "0.5";
				clone.style.filter = "alpha (opacity = 50)";
				clone.style.zIndex = "10000";
				//dragInstance.ownerDocument.body.style.cursor = "move";
				dragManager.__feedbackNode = clone;
				dragManager.__dragInstance = dragInstance;
				var dragStartEvent = Event.create(this.__document, "dragstart", false, false);
				Element.dispatchEvent(dragInstance, dragStartEvent);
				if (!dragManager.__isDragging) {
					return;
				}
				
			} else {
				clone = dragManager.__feedbackNode;
			}
			
			event.preventDefault();
			var doc = clone.ownerDocument;
			if (clone != null) {
				var mouseX = event.pageX ? event.pageX : window.event && window.event.x ? window.event.x + doc.documentElement.scrollLeft : 0;
				var mouseY = event.pageY ? event.pageY : window.event && window.event.y ? window.event.y + doc.documentElement.scrollTop : 0;
				clone.style.left = (mouseX + offsetX) + "px";
				clone.style.top = (mouseY + offsetY) + "px";
			}
			

			if (event.target != dragManager.__dragOverElement) {
				var ancestors = DOM.getAncestors(event.target);
				ancestors.push(event.target);
				var enter = ArrayUtils.subtract(ancestors, dragManager.__dragOverAncestors);
				var leave = ArrayUtils.subtract(dragManager.__dragOverAncestors, ancestors);
				for (var i = 0;i<leave.length;i++) {
					var dragLeaveEvent = Event.create(this.__document, "dragleave", false, false);
					dragLeaveEvent.dragInstance = dragInstance;
					Element.dispatchEvent(leave[i], dragLeaveEvent);
				}
				for (var i = 0;i<enter.length;i++) {
					var dragEnterEvent = Event.create(this.__document, "dragenter", false, false);
					dragEnterEvent.dragInstance = dragInstance;
					Element.dispatchEvent(enter[i], dragEnterEvent);
				}
			
			
				var dragOutEvent = Event.create(this.__document, "dragout", true, false);
				dragOutEvent.relatedTarget = event.target;
				dragOutEvent.dragInstance = dragInstance;
				dragOutEvent.dragX = event.clientX;
				dragOutEvent.dragY = event.clientY;
				Element.dispatchEvent(dragManager.__dragOverElement, dragOutEvent);
			
			}
	
			if (event.target != dragManager.__dragOverElement) {	
				var dragOverEvent = Event.create(this.__document, "dragover", true, false);
				dragOverEvent.relatedTarget = dragManager.__dragOverElement;
				dragOverEvent.dragInstance = dragInstance;
				dragOverEvent.dragX = event.clientX;
				dragOverEvent.dragY = event.clientY;
				Element.dispatchEvent(event.target, dragOverEvent);
				
			}
			
			var dragMoveEvent = Event.create(this.__document, "dragmove", true, false);
			dragMoveEvent.dragInstance = dragInstance;
			dragMoveEvent.dragX = event.clientX;
			dragMoveEvent.dragY = event.clientY;
			Element.dispatchEvent(event.target, dragMoveEvent);

			
			
			var dragEvent = Event.create(this.__document, "drag", false, false);
			dragEvent.dragInstance = dragInstance;
			dragEvent.dragOverElement = dragManager.__dragOverElement;
			dragEvent.dragX = event.clientX;
			dragEvent.dragY = event.clientY;
			Element.dispatchEvent(dragInstance, dragEvent);
			
			if (event.target != dragManager.__dragOverElement) {
				dragManager.__dragOverElement = event.target;
				dragManager.__dragOverAncestors = ancestors;
			}
			//
			//event.preventDefault();
		}
		
		function mouseUpHandler(event) {
			mouseDownInstance = null;
			
			event = Event.getEvent(event);
			Element.removeEventListener(dragManager.__document, "mousemove", mouseMoveHandler, false);
			Element.removeEventListener(dragManager.__document, "mouseup", mouseUpHandler, false);
			
			if (!dragManager.__isDragging) return;
			if (dragManager.__dragOverElement) {
				var dropEvent = Event.create(this.__document, "drop", true, false);
				dropEvent.dragInstance = dragInstance;
				dropEvent.dragX = event.clientX;
				dropEvent.dragY = event.clientY;
				Element.dispatchEvent(dragManager.__dragOverElement, dropEvent);
			}

			dragManager.stop();
			var dragEndEvent = Event.create(this.__document, "dragend", false, false);
			Element.dispatchEvent(dragInstance, dragEndEvent);
			//event.preventDefault();
		}
		
		Element.addEventListener(dragInstance, "dragstop", function(event) {
			Element.removeEventListener(dragManager.__document, "mousemove", mouseMoveHandler, false);
			Element.removeEventListener(dragManager.__document, "mouseup", mouseUpHandler, false);
			
		}, false);
		
		Element.addEventListener(dragInstance, "dragcancel", function(event) {
			Element.removeEventListener(dragArea, "mousedown", mouseDownHandler);
		}, false);
		
		var dragInitEvent = Event.create(this.__document, "draginit", false, false);
		Element.dispatchEvent(dragInstance, dragInitEvent);
	}
	
	DragManager.prototype.stop = function() {
		
		if (this.__feedbackNode) {
			this.__feedbackNode.parentNode.removeChild(this.__feedbackNode);
			this.__feedbackNode = null;
		}
		var dragStopEvent = Event.create(this.__document, "dragstop", false, false);
		Element.dispatchEvent(this.__dragInstance, dragStopEvent);
		//
		this.__isDragging = false;
		this.__dragInstance = null;
		this.__dragOverAncestors = [];
		this.__dragOverElement = null;
	}
	
	DragManager.prototype.cancel = function(dragInstance) {
		this.stop();
		if (ArrayUtils.contains(this.__dragObjects, dragInstance)) {
			ArrayUtils.remove(this.__dragObjects, dragInstance);
			var dragCancelEvent = Event.create(dragInstance.ownerDocument, "dragcancel", false, false);
			dragInstance.style.cursor = "";
			Element.dispatchEvent(dragInstance, dragCancelEvent);
		}
	}
	
	DragManager.prototype.isDragging = function() {
		return this.__isDragging;
	}
	
	DragManager.prototype.getDragInstance = function() {
		return this.__dragInstance;
	}
	
	return DragManager;
})();



(function() {
	
	var Class = benignware.core.Class;
	var StringUtils = Class.require("benignware.utils.StringUtils");
	
	var cookieVars;
	
	function Cookie() {}
		
	Class.registerClass("benignware.utils.Cookie", Cookie);
	
	Cookie.get = function(name) {
		if (!cookieVars) cookieVars = parseCookie(document.cookie);
		if (typeof(cookieVars[name]) != "undefined") return cookieVars[name];
		return null;
	}
	
	Cookie.set = function(name, value, expirationDate, path, domain) {
		var str = name + "=" + value+";";
		if (path) {
			str+= " path=" + path + ";";
		}
		if (domain) {
			str+= " domain=" + domain + ";";
		}
		if (expirationDate) str+= " expires=" + expirationDate.toGMTString() + ";";
		document.cookie = str;
		if (!cookieVars) cookieVars = [];
		cookieVars[name] = value;
	}

	function parseCookie(str) {
		var pattern = /([a-zA-Z0-9_\.]*)\s*=\s*([^;]*)/;
		var regex = new RegExp( pattern );
		var match;
		var properties = [];
		while (str.length > 0) {
			match = regex.exec(str);
			
			if (match == null) {
				break;
			} else {
				var name = match[1];
				var value = match[2];
				properties[name] = value;
				str = str.substring(match.index + match[0].length);
			}
		}
		return properties;
	}

	return Cookie;
})();

/**
 * AJAX PropertyFile class 
 * @class benignware.utils.PropertyFile
 * @extends benignware.events.EventDispatcher
 */
(function() {
	
	var Class = benignware.core.Class;
	var Event = Class.require("benignware.events.Event");
	var EventDispatcher = Class.require("benignware.events.EventDispatcher");
	var Delegate = Class.require("benignware.utils.Delegate");
	var StringUtils = Class.require("benignware.utils.StringUtils");
	var Loader = Class.require("benignware.utils.Loader");
	var PropertyFile = Class.require("benignware.utils.PropertyFile");
	
	var instance;
	
	function Locale() {
		__super.call(this);
		this.__text = [];
		this.__langId = "en_US";
	}
	 
	Class.registerClass("benignware.utils.Locale", Locale);
	Class.extend(EventDispatcher, Locale);
	__super = Class.getSuper(Locale);
	
	Locale.prototype.__langId = null;
	Locale.prototype.__text = null;
	
	Locale.getInstance = function() {
		if (!instance) {
			instance = new Locale();
		}
		return instance;
	}
	
	Locale.prototype.load = function(langId, src) {
		var propFile = new PropertyFile();
		propFile.onload = Delegate.create(this, function(event) {
			this.setStrings(langId, propFile.getProperties());
		});
		propFile.load(src);
	}
	
	Locale.prototype.setDefaultLocale = function(langId) {
		if (langId != this.__defaultLangId) {
			this.__defaultLangId = langId;
		}
	}
	
	Locale.prototype.getDefaultLocale = function() {
		return this.__defaultLangId ? this.__defaultLangId : this.getSystemLocale();
	}
	
	Locale.prototype.setLocale = function(langId) {
		if (langId != this.__langId) {
			this.__langId = langId;
			var localeChangedEvent = Event.create('localeChanged', false, false);
			this.dispatchEvent(localeChangedEvent);
		}
	}
	
	Locale.prototype.getLocale = function() {
		return this.__langId ? this.__langId : this.getSystemLocale();
	}
	
	Locale.prototype.setProperties = function(langId, properties) {
		if (typeof(properties) == "string") {
			properties = eval(properties);
		}
		if (!this.__text[langId]) {
			this.__text[langId] = [];
		}
		for (var x in properties) {
			this.__text[langId][x] = properties[x];
		}
	}
	
	Locale.prototype.getProperties = function() {
		var langId = this.getLocale();
		if (this.__text[langId]) return this.__text[langId];
		return [];
	}
	
	Locale.prototype.setString = function(langId, id, text) {
		if (!this.__text[langId]) {
			this.__text[langId] = [];
		}
		this.__text[langId][id] = text;
	}
	
	Locale.prototype.getString = function(langId, id) {
		if (this.__text[langId] && this.__text[langId][id]) {
			return this.__text[langId][id];
		}
		var defaultLangId = this.__defaultLangId;
		if (defaultLangId != langId) {
			if (this.__text[defaultLangId] && this.__text[defaultLangId][id]) {
				return this.__text[defaultLangId][id];
			}
		}
		return id;
	}
	
	Locale.prototype.setText = function(id, text) {
		var langId = this.getLocale();
		this.setString(langId, id, text);
	}
	
	Locale.prototype.getText = function(id) {
		var langId = this.getLocale();
		var string = this.getString(langId, id);
		return string;
	}
	
	
	Locale.prototype.getSystemLocale = function() {
		if ( navigator ) {
		    if ( navigator.language ) {
		        return navigator.language;
		    }
		    else if ( navigator.browserLanguage ) {
		        return navigator.browserLanguage;
		    }
		    else if ( navigator.systemLanguage ) {
		        return navigator.systemLanguage;
		    }
		    else if ( navigator.userLanguage ) {
		        return navigator.userLanguage;
		    }
		}
	}
	
	return Locale;
})();
(function() {

	var Class = benignware.core.Class;
	
	var instance;
	
	function UserAgent() {
		for (var x in UserAgent.__userAgents) {
			var detectionMethod = UserAgent.__userAgents[x];
			detectionMethod.call(this);
			if (this.name == x) {
				break;
			}
		}
	}
	
	UserAgent.getInstance = function() {
		if (!instance) {
			instance = new UserAgent();
		}
		return instance;
	}
	
	UserAgent.__userAgents = [];
	
	UserAgent.FIREFOX = "Firefox";
	UserAgent.INTERNET_EXPLORER = "Internet Explorer";
	UserAgent.SAFARI = "Safari";
	UserAgent.CHROME = "Chrome";
	UserAgent.OPERA = "Opera";

	UserAgent.prototype.id = "";
	UserAgent.prototype.name = "";
	UserAgent.prototype.vendor = "";
	UserAgent.prototype.version = "";
	UserAgent.prototype.language = "";
	
	UserAgent.prototype.toString = function() {
		return this.name + " " + this.version;
	}
	
	UserAgent.addUserAgent = function(name, detectionMethod) {
		UserAgent.__userAgents[name] = detectionMethod;
	}
	
	UserAgent.removeUserAgent = function(name) {
		UserAgent.__userAgents[name] = undefined;
	}
	
	Class.registerClass('benignware.utils.UserAgent', UserAgent);

	UserAgent.addUserAgent(UserAgent.FIREFOX, function(){
		var vendor = navigator.vendor;
		if (!vendor || /Mozilla/i.test(vendor)) {
			var appCodeName = navigator.appCodeName;
			if (/Mozilla/i.test(appCodeName)) {
				var id = navigator.userAgent;
				var regex = new RegExp(/Firefox\/([0-9\.]+)*/i);
				var match = regex.exec(id);
				if (match) {
					this.id = id;
					this.name = UserAgent.FIREFOX;
					this.vendor = appCodeName;
					this.version = match[1];
				}
			}
		}
	});
	
	UserAgent.addUserAgent(UserAgent.SAFARI, function() {
		var id = navigator.userAgent;
		var vendor = navigator.vendor;
		if (/Apple/i.test(vendor)) {
			var regex = new RegExp(/Version\/([0-9\.]+)*/i);
			var match = regex.exec(id);
			if (match) {
				this.id = id;
				this.name = UserAgent.SAFARI;
				this.version = match[1];
				this.vendor = vendor;
			}
		}
	});
	
	UserAgent.addUserAgent(UserAgent.INTERNET_EXPLORER, function() {
		var id = navigator.userAgent;
		if (/Microsoft/i.test(navigator.appName)) {
			var regex = new RegExp(/MSIE\s([0-9\.]+)*/i);
			var match = regex.exec(id);
			if (match) {
				this.id = id;
				this.name = UserAgent.INTERNET_EXPLORER;
				this.version = match[1];
				this.vendor = "Microsoft";
			}
		}
	});
	
	
	UserAgent.addUserAgent(UserAgent.CHROME, function() {
		var id = navigator.userAgent;
		var vendor = navigator.vendor;
		if (/Google/i.test(vendor)) {
			var regex = new RegExp(/Chrome\/([0-9\.]+)*/i);
			var match = regex.exec(id);
			if (match) {
				this.id = id;
				this.name = UserAgent.CHROME;
				this.version = match[1];
				this.vendor = vendor;
			}
		}
	});
	
	UserAgent.addUserAgent(UserAgent.OPERA, function() {
		var id = navigator.userAgent;
		if (window.opera) {
			var vendor = navigator.vendor;
			var regex = new RegExp(/Version\/([0-9\.]+)*/i);
			var match = regex.exec(id);
			if (match) {
				this.id = id;
				this.name = UserAgent.OPERA;
				this.version = match[1];
				this.vendor = vendor;
			}
		}
	});
	
	function compareVersions(version1, version2) {
		var version1Arr = version1.split(".");
		var version2Arr = version2.split(".");
		var l = version1Arr.length > version2Arr.length ? version1Arr.length : version2Arr.length;
		for (var i = 0; i < l; i++) {
			var v1 = Number(version1Arr[i]);
			var v2 = Number(version2Arr[i]);
			v1 = !isNaN(v1) ? v1 : 0;
			v2 = !isNaN(v2) ? v2 : 0;
//			if (i > 1) {
//				v1 = v1 ? v1.replace(/0+$/g, '') : 0;
//				v2 = v2 ? v2.replace(/0+$/g, '') : 0;
//			}
			if (v1 > v2) {
				return 1;
			} else if (v1 < v2) {
				return -1;
			} else if (v1 == v2 && i == l - 1) {
				return 0;
			}
		}
		return -1;
	}
	
	UserAgent.detect = function(requirements) {
		requirements = typeof(requirements) == "string" ? [requirements] : requirements;
		var userAgent = UserAgent.getInstance();
		var userAgents = [];
		for (var x in UserAgent.__userAgents) {
			userAgents.push(x);
		}
		var userAgentsPattern = userAgents.join("|");
		for (var i = 0; i < requirements.length; i++) {
			var requirement = requirements[i];
			var regex = new RegExp("(" + userAgentsPattern + ")\\s*(?:([0-9\\.]+)*\\s*(?:(\\+)|\\-\\s*([0-9\\.]+)*)?)?", 'i');
			var match = regex.exec(requirement);
			if (!match) {
				continue;
			}
			var reqName = match[1];
			var reqVersion = match[2];
			var andAbove = match[3];
			var toVersion = match[4];
			
			if (userAgent.name == reqName) {
				
				// match
				if (reqVersion) {
					if (compareVersions(userAgent.version, reqVersion) == 0) {
						return true;
					} 
					if (andAbove && compareVersions(userAgent.version, reqVersion) >= 0) {
						return true;
					}
					if (toVersion && compareVersions(userAgent.version, reqVersion) >= 0 && compareVersions(userAgent.version, toVersion) <= 0) {
						return true;
					}
				} else {
					return true;
				}
			}
		}
		return false;
	}
	
	return UserAgent;
})();

