/* Benignware Javascript Framework */
// namespace
//console
(function() {
	
	// global namespace
	if (!window.console) window.console = {log: function(){}, error: function(){}}
	if (!window.benignware) window.benignware = {}
	if (!window.benignware.core) window.benignware.core = {}
	
	// private utility methods
	function trim(str) {
		return str.replace(/^\s+/,"").replace(/\s+$/,"");
	}

	// cross browser http request
	function getXMLHttpRequest() {
		return typeof XMLHttpRequest != undefined ? new XMLHttpRequest() : 
			typeof ActiveXObject != undefined ? new ActiveXObject('Microsoft.XMLHTTP') : null; 
	}
	
	function getScriptLocations() {
		var result = [];
		var scriptNodes = document.getElementsByTagName("script");
		for (var i = scriptNodes.length - 1; i >= 0; i--) {
			var child = scriptNodes[i];
			var src = child.getAttribute("src");
			if (src != null) {
				result.push(src);
			}
		}
		return result;
	}

	// extracts filename from path
	function extractFileName(path) {
	  	return path.split("/").pop();
	}

	// extracts directory from path
	function extractDirectory(path) {
	  	return unescape(path.substring(0, (path.lastIndexOf("/"))));
	}

	// converts qualified classname to filename
	function getFileName(qualifiedName) {
		return qualifiedName.replace(/\./g, "/") + ".js";
	}
	
	/**
	 * The Class Object provides the oop-model of the library
	 * @class benignware.core.Class
	 */
	function Class(superclass, constructor) {
		return Class.extend.apply(this, arguments);
	}
	 
	window.benignware.core.Class = Class;
	
	// private static
	var classMap = {coreClass: Class}
	
	// initialize auto-loading dependencies
	
	var classFiles = {};
	var classPath = [(function() {
		// find default classpath from head scripts
		var coreClassName = 'benignware.core.Class';
		var coreDistName = 'benignjs-core';
		var scriptLocations = getScriptLocations();
		var qualifiedName = coreClassName;
		var classFile = getFileName(qualifiedName);
		var classDir = extractDirectory(classFile);
		
		for (var i = 0; i < scriptLocations.length; i++) {
			
			var file = scriptLocations[i];
			var fileName = extractFileName(file);
			var dir = extractDirectory(file);

			
			// assume single file
			if (file.length >= classFile.length && file.substring(file.length - classFile.length) == classFile) {
				classFiles[qualifiedName] = file;
				return file.substring(0, file.length - classFile.length);
			}
			
			// assume package parts
			var classDir = extractDirectory(classFile);
			var arr = classDir.split("/");
			while (arr.length > 0) {
				var strDir = arr.join("/");
				if (dir.length >= strDir.length && dir.substring(dir.length - strDir.length) == strDir) {
					return dir.substring(0, dir.length - strDir.length);
				}
				var last = arr.pop();
			} 
			
			
			// assume core distribution in top level
			if (fileName.indexOf(coreDistName) == 0) {
				classFiles[qualifiedName] = file;
				return extractDirectory(file);
			}
			
		}
		return "./";
	})()];
	
	
	/**
	 * Creates the specified namespace on the native global window scope.
	 * @static
	 * @method namespace
	 * @param {String} namespace
	 */
	Class.namespace = function(namespace) {
		var nsArray = trim(namespace).split(".");
		var obj = window;
		for (var i = 0; i < nsArray.length; i++) {
			if (i == nsArray.length - 1 && nsArray[i] == "*") continue;
			if (typeof(obj[nsArray[i]]) == "undefined") obj[nsArray[i]] = {}
			obj = obj[nsArray[i]];
		}
		return obj;
	}
	
	/**
	 * Creates a new class by extending from another.
	 * @static
	 * @method extend
	 * @param {benignware.core.Class} superClass 
	 * @param {Prototype} constructor
	 * @return {benignware.core.Class} the new class object
	 */
	Class.extend = function(superclass, constructor) {
		if (arguments.length == 1) {
			if (!arguments[0].prototype.__super__) {
				constructor = arguments[0];
				// native object
				superclass = Object;
			}
		}
		if (!superclass) {
			superclass = Object;
		}
		for (var prop in superclass.prototype) {
			if (constructor.prototype[prop] == null) {
				constructor.prototype[prop] = superclass.prototype[prop];
			}
		}
		constructor.prototype.__super__ = superclass;
		constructor.prototype.__proto__ = superclass.prototype;
		constructor.prototype.getClass = function() {
			return constructor;
		}
		return constructor;
	} 
	 
	Class.mustImplement = function(constructor, interfaceObj) {
		if (!constructor.prototype.__interfaces__) {
			constructor.prototype.__interfaces__ = [];
		}
		constructor.prototype.__interfaces__.push(interfaceObj);
		return constructor;
	}
	
	Class.instanceOf = function(obj, classObject) {
		if (!obj || !classObject) return false;
		if (typeof(classObject) == "string") {
			classObject = Class.getClass(classObject);
		}
		if (obj.constructor == classObject) {
			return true;
		}
		if (typeof(obj.__class__) == "function") {
			var proto = obj.__class__.prototype;
			while (proto) {
				if (proto == classObject.prototype) return true;
				proto = proto.__proto__;
			}
		}
		if (obj.__interfaces__) {
			for (var i = 0; i < obj.__interfaces__.length; i++) {
				if (classObject == obj.__interfaces__[i]) {
					return true;
				}
			}
		}
		return false;
	}
	

	/**
	 * Returns a super object for the specified class. 
	 * The super object contains all inherited methods of the superclass.
	 * @static
	 * @method getSuper
	 * @param {benignware.core.Class} classObject
	 * @return {Object} the superclass object
	 */
	Class.getSuper = function(__class) {
		if (__class && __class.prototype && __class.prototype.__super__) {
			var superclass = __class.prototype.__super__;
			function __super() {
				return superclass.apply(this, arguments);
			}
			for (var x in superclass.prototype) {
				__super[x] = superclass.prototype[x];
			}
			__super.__super = Class.getSuper(superclass);
			return __super;
		}
		return null;
	}
	 
	 
	 Class.getPrivate = function(__class) {
		 
		 function __private() {
		 }
		 
		 __private.set = function(name, value) {
			 if (!this.__private) {
				 this.__private = {};
			 }
			 var qualifiedName = Class.getQualifiedName(__class);
			 if (qualifiedName) {
				 if (!this.__private[qualifiedName]) {
					 this.__private[qualifiedName] = {}
				 }
				 this.__private[qualifiedName].name = value;
			 }
		 }
		 
		 __private.get = function(name) {
			 var qualifiedName = Class.getQualifiedName(__class);
			 if (qualifiedName) {
				 if (this.__private[qualifiedName]) {
					 return this.__private[qualifiedName][name];
				 }
			 }
		 }
		 
		 return __private;
		 
	 }
	
	/**
	 * Adds a class path
	 * @static
	 * @method addClassPath
	 * @param {String} directory
	 */
	Class.addClassPath = function(dir) {	
		dir = dir.length > 0 && dir.charAt(directory.length - 1) != "/" ? dir + "/" : dir;
		classPath.push(dir);
	}
	
	/**
	 * Removes a ClassPath
	 * @static
	 * @method removeClassPath
	 * @param {String} directory
	 * @see benignware.core.Object#require
	 */
	Class.removeClassPath = function(dir) {
		for (var i = 0; i < classPath.length; i++) {
			if (classPath[i] == dir) classPath.splice(i--, 1);
		}
	}

	/**
	 * Returns the class which has been loaded to the specified class id 
	 * @param {String} fullClassID
	 * @return {benignware.core.Class}
	 */
	 Class.getClass = function(qualifiedName) {
		if (classMap[qualifiedName] && classMap[qualifiedName]) return classMap[qualifiedName];
		try {
			var a = qualifiedName.split(".");
			var obj = window;
			for (var i = 0; i < a.length; i++) {
				var n = trim(a[i]);
				if (n && obj && typeof(obj[n]) != "undefined") {
					obj = obj[n];
				}
			}
			if (obj != window && typeof(obj) == "function") return obj;
		} catch (e) {
			console.error(e);
		}
		return null;
	 }
	 
	 /**
	  * retrieves qualified name for specified registered class
	  */
	 Class.getQualifiedName = function(__class) {
		 for (var x in classMap) {
			 if (classMap[x] == __class) return x;
		 }
		 return null;
	 }
	
	/**
	 * Loads the class for the specified full name.
	 * @param {String} className
	 * @return {Class} the loaded class object
	 */
	Class.require = function(qualifiedName) {
		var __class = Class.getClass(qualifiedName);
		if (__class) return __class;
		var fileName = getFileName(qualifiedName);
		for (var i = classPath.length - 1; i >= 0; i--) {
			var file = classPath[i] + fileName;
			var httpRequest = getXMLHttpRequest();
			httpRequest.open("GET", file, false);
			httpRequest.send(null);
			if (httpRequest.status == 200 && httpRequest.responseText) {
				try {
					eval(httpRequest.responseText);
				} catch(e) {
				}
				try {
					__class = eval(className);
				} catch (e) {
					// classname not defined
				}
				if (!__class) {
					var cl = Class.getClass(qualifiedName);
					if (cl) __class = cl;
				}
			}
		}
		
		if (!__class) {
			// error class not found
			console.error("ClassNotFound: " + qualifiedName);
			return null;
		}
		
		// register
		classMap[qualifiedName] = __class;
		classFiles[qualifiedName] = file;
		
		return __class;	 
	}
	
	
	/**
	 * Retrieves a fileName relative to a previously required class file or core library path. 
	 * Use this method to reference resource files.
	 * @param {String} className
	 * @param {String} relativePath
	 * @return {String} fileName
	 */
	 
	Class.getResourceUri = function(qualifiedName, relativePath) {
		var fileName;
		if (typeof(qualifiedName) == "function") {
			qualifiedName = Class.getQualifiedName(qualifiedName);
		}
		if (qualifiedName) {
			
			if (classFiles[qualifiedName] && classFiles[qualifiedName] != null) {
				return extractDirectory(classFiles[qualifiedName]) + "/" + relativePath;
			}
			
			if (classPath.length) {
				fileName = classPath[0];
				fileName+= getFileName(qualifiedName);
				if (fileName) {
					var fileDir = extractDirectory(fileName);
					var result = "";
					result+= fileDir ? fileDir + "/" : "";
					result+= relativePath;
					return result;
				}
			}
		}
		
		return null;
	}
	 
	Class.registerClass = function(qualifiedName, __class) {
		classMap[qualifiedName] = __class;
		// register on global scope
		var s = qualifiedName.split('.');
		var className = s.pop();
		Class.namespace(s.join('.'))[className] = __class;
		return __class;
	}
	
	
	// methods for bean handling
	Class.getSetterName = function (propName) {
		return "set" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
	}
	
	Class.getGetterName = function (propName) {
		return "get" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
	}
	
	Class.callSetter = function (obj, propName, value) {
		var setterName = Class.getSetterName(propName);
		if (typeof(obj[setterName]) == "function") {
			obj[setterName].call(obj, value);
			return;
		}
		if (typeof(obj[propName]) != "undefined" && typeof(obj[propName]) != "function") {
			switch (typeof(obj[propName])) {
				case "boolean":
					obj[propName] = (value == "true" || value == "1") ? true : (value);
					return;
				default:
					obj[propName] = value;
					return;
			}
		}
	}
	
	Class.callGetter = function (obj, propName) {
		var getterName = Class.getGetterName(propName);
		if (typeof(obj[getterName]) == "function") {
			return obj[getterName].call(obj);
		}
		if (typeof(obj[propName]) != "undefined") {
			return obj[propName];
		}
		return null;
	}
	
	Class.hasSetter = function (obj, propName) {
		var setterName = Class.getSetterName(propName);
		return (typeof(obj[setterName]) == "function" || typeof(obj[propName]) != "undefined" && typeof(obj[propName]) != "function")
	}
	
	Class.hasGetter = function (obj, propName) {
		var getterName = Class.getGetterName(propName);
		return (typeof(obj[getterName]) == "function" || typeof(obj[propName]) != "undefined" && typeof(obj[propName]) != "function")
	}

	
	return Class;
})();
(function(){
	
	var Class = benignware.core.Class;
	
	function Event(type, bubbles, cancelable) {
		this.type = type;
		this.bubbles = bubbles;
		this.cancelable = cancelable;
	}
	Class(Event);
	
	benignware.core.Class.registerClass("benignware.events.Event", Event);
	/**
	 * Creates the event of the specified type.
	 * Insert the target document as first argument optionally.
	 * @static
	 * @method create
	 * @param {String} type
	 * @param {boolean} bubbles
	 * @param {boolean} cancelable
	 */
	
	Event.prototype.type = null;
	//Event.prototype.target = null;
	Event.prototype.bubbles = false;
	Event.prototype.cancelable = false;
	Event.prototype.cancelBubble = false;
	Event.prototype.returnValue = true;
	
	Event.prototype.stopPropagation = function() {
		this.cancelBubble = true;
	}
	
	Event.prototype.preventDefault = function() {
		this.returnValue = false;
	}
	
	Event.create = function(type, bubbles, cancelable) {
		var doc;
		if (arguments.length == 4) {
			doc = arguments[0];
			type = arguments[1];
			bubbles = arguments[2];
			cancelable = arguments[3];
		}
		doc = doc || document;
		if (!doc.documentElement) {
			// error
		}
		if (!bubbles) bubbles = false;
		if (!cancelable) cancelable = false;
		
		if (typeof(doc.createEvent) == "function") {
			var event;
			if (typeof(doc.__native__) != "undefined" && typeof(doc.__native__.createElement) == "function") {
				event = doc.__native__.createEvent("Event");
			} else {
				event = doc.createEvent("Event");
			}
			if (type) {
				event.initEvent(type, bubbles, cancelable);		
			}
			return event;
		} else {
			return new Event(type, bubbles, cancelable);
		}
	}
	
	Event.getEvent = function(event) {
		if (typeof(event) == "undefined") {
			// no event
			return event;
		}
		if (typeof(event.preventDefault) == "undefined") {
			event.preventDefault = Event.prototype.preventDefault;
		}
		if (typeof(event.stopPropagation) == "undefined") {
			event.stopPropagation = Event.prototype.stopPropagation;
		}
		
		if (typeof(event.target) == "undefined" && typeof(event.srcElement) != "undefined") {
			event.target = event.srcElement;
		}
		
		if (typeof(event.which) == "undefined" && typeof(event.keyCode) != "undefined") {
			event.which = event.keyCode;
		}
		
		//if (typeof(event.relatedTarget) == "undefined") {
		if (event.fromElement) {
				event.relatedTarget = event.fromElement;
			} else if (event.toElement) {
				event.relatedTarget = event.toElement;
			}
		//}
		
		/*
		if (typeof(event.clientX) != "undefined") {
			event.pageX = event.clientX;
		}
		
		if (typeof(event.clientY) != "undefined") {
			event.pageY = event.clientY;
		}
		*/
		
		return event;
	}
	
	return Event;
})();
/**
 * The EventDispatcher class is the base class for all runtime classes that dispatch events.
 * @package benignware.events
 * @class benignware.events.EventDispatcher
 * @extends benignware.core.Object
 */
benignware.core.Class.registerClass("benignware.events.EventDispatcher", (function(){
	var Class = benignware.core.Class;
	var Event = Class.require("benignware.events.Event");
	var __super;
	
	function EventDispatcher() {
		this.eventListeners = [];
	}
	
	EventDispatcher = Class(EventDispatcher);
	__super = Class.getSuper(EventDispatcher);
	
	EventDispatcher.prototype.eventListeners = null;
	/**
	 * Registers an event listener for the specified event type.
	 * @method addEventListener
	 * @param {String} whichEvent
	 * @param {function} handler
	 * @param {boolean} useCapture
	 */
	EventDispatcher.prototype.addEventListener = function(whichEvent, handler, useCapture) {
		if (this.eventListeners == null) this.eventListeners = [];
		if (typeof(this.eventListeners[whichEvent]) == "undefined") this.eventListeners[whichEvent] = [];
		this.eventListeners[whichEvent].push(handler);
	}
	/**
	 * Removes an event listener for the specified event type.
	 * @method removeEventListener
	 * @param {String} whichEvent
	 * @param {function} handler
	 * @param {boolean} useCapture
	 */
	EventDispatcher.prototype.removeEventListener = function(whichEvent, handler, useCapture) {
		if (this.eventListeners == null || !this.eventListeners[whichEvent]) return;
		for (var i = 0; i < this.eventListeners[whichEvent].length; i++) {
			if (this.eventListeners[whichEvent][i] == handler) {
				this.eventListeners[whichEvent].splice(i--, 1);
				break;
			}
		}
	}
	
	/**
	 * Dispatches the given event.
	 * @method dispatchEvent
	 * @param {benignware.events.Event} event
	 */
	 
	EventDispatcher.prototype.destroy = function() {
		  __super.destroy.call(this);
		 for (var whichEvent in this.eventListeners) {
			 for (var i = 0; i < this.eventListeners[whichEvent].length; i++) {
				 this.eventListeners[whichEvent].splice(i--, 1);
			}
		 }
	}
	
	EventDispatcher.prototype.__nativeEvents = ['mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove', 'mouseenter', 'mouseleave', 'mousedown', 'click', 'dblclick', 'blur', 'submit'];
	 
	EventDispatcher.prototype.dispatchEvent = function(event) {
		
		if (this.__native__ && this.__native__.dispatchEvent) {
			return this.__native__.dispatchEvent(event);
		}
		
		if (typeof(event.target) == "undefined") {
			try {
				event.target = this;
			} catch (e) {}
		}
		
		if (!event) return;
		
		if (this.fireEvent && this.__nativeEvents) {
			for (var i = 0; i < this.__nativeEvents.length; i++) {
				if (event.type == this.__nativeEvents[i]) {
					this.fireEvent("on" + event.type);
					return;
				}
			}
		}
		
		
		if (this.eventListeners != null && this.eventListeners[event.type]) for (var i=0;i<this.eventListeners[event.type].length;i++) {
			this.eventListeners[event.type][i](event);
		}
		var eventProperty = "on" + event.type.toLowerCase();
		try {
			if (this[eventProperty] == undefined) eventProperty = "on" + event.type;
			if (this[eventProperty] && typeof(this[eventProperty])=="function") {
				this[eventProperty](event);
			}
		} catch (e) {
			//alert(e.message+"   "+eventProperty);
			
		}
		if (event.bubbles && !event.cancelBubble) {
			if (this.parentNode) {
				if (this.parentNode.dispatchEvent) {
					this.parentNode.dispatchEvent(event);
				} else {
					EventDispatcher.prototype.dispatchEvent.call(this.parentNode, event);
				}
			}
		}
	}
	
	EventDispatcher.prototype.createEvent = function(type, bubbles, cancelable) {
		var doc = this.ownerDocument ? this.ownerDocument : document;
		if (!doc) return null;
		return Event.create(doc, type, bubbles, cancelable);
	}
	
	return EventDispatcher;
})());
(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.endsWith = function(str, endStr) {
		return (str.lastIndexOf(endStr) == 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;
	}
	
	StringUtils.hyphenatedToCamelCase = function(name) {
		var split = name.split("-");
		var result = "";
		for (var i = 0; i < split.length; i++) {
			var word = split[i];
			if (i == 0 || !word.length) {
				result+= word;
			} else {
				result+= word.substring(0, 1).toUpperCase() + word.substring(1);
			}
		}
		return result;
	}
	
	StringUtils.camelCaseToHyphenated = function(name) {
		var result = "";
		for (var i = 0; i < name.length; i++) {
			var char = name.charAt(i);
			if (char == char.toUpperCase()) {
				result+= "-" + char.toLowerCase();
			}
			result+= char;
		}
		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, values) {
		var args = typeof(values) == "array" || typeof(values) == "object" && values.length ? [null].concat(values) : arguments;
		for (var i = 1; i < args.length; i++) {
			format = format.replace(/%[A-Za-z]/, args[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) {
					var rest = str.substring(index);
					if (i < searches.length - 1) {
						var ni = rest.indexOf(searches[i + 1]);
						var value = rest.substring(index + searches[i].length, ni);
						if (value) {
							values.push(value);
						}
						rest = rest.substring(ni);
						
					}
					
					str = str.substring(ni);
				}
			} else {
				values.push(str);
			}
		}
		return values;
	}
	
	/* 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;
})();
/**
 * Base Class of all DOM Elements in the framework.
 * @class benignware.core.Element
 * @extends benignware.events.EventDispatcher
 */
(function(){
	
	var Class = benignware.core.Class;
	var EventDispatcher = Class.require("benignware.events.EventDispatcher");
	var Event = Class.require("benignware.events.Event");
	var StringUtils = Class.require("benignware.utils.StringUtils");
	
	var __super;
	function Element() {
		__super.call(this);
	}
	Class.registerClass("benignware.core.Element", Element);
	
	Element = Class.extend(EventDispatcher, Element);
	__super = Class.getSuper(Element);
	
	/**
	 * Sets the base element for the Element.create method. 
	 * This value is used at instantiation only. runtime changes have no effect.
	 * @property baseElement
	 * @return {String}
	 */
	Element.prototype.baseElement = "div";
	
	//Element.prototype.__native__ = {};
	
	Element.prototype.appendChild = function(child) {
		if (this.__native__.appendChild != null) return this.__native__.appendChild.call(this, child);
		return null;
	}
	Element.prototype.removeChild = function(child) {
		if (this.__native__.removeChild != null) return this.__native__.removeChild.call(this, child);
		return null;
	}
	Element.prototype.insertBefore = function(child, beforeNode) {
		if (this.__native__ && this.__native__.insertBefore != null) return this.__native__.insertBefore.call(this, child, beforeNode);
		return null;
	}
	
	Element.prototype.setAttribute = function(name, value) {
		if (this.__native__ && this.__native__.setAttribute != null) return this.__native__.setAttribute.call(this, name, value);
		return null;
	}
	Element.prototype.getAttribute = function(name) {
		if (this.__native__ && this.__native__.getAttribute != null) return this.__native__.getAttribute.call(this, name);
		return null;
	}
	
	Element.prototype.addEventListener = function(whichEvent, handler, useCapture) {
		if (typeof(useCapture) == "undefined") useCapture = false;
		
		if (this.__native__ && typeof(this.__native__.addEventListener) == "function") { 
			this.__native__.addEventListener.call(this, whichEvent, handler, useCapture);
		} else if (typeof(this.attachEvent) != "undefined") {
			this.attachEvent("on" + whichEvent, handler);
		} else if (!Class.instanceOf(this, Element) && this.addEventListener) {
			this.addEventListener(whichEvent, handler, useCapture);
		}
		
		if (__super && __super.addEventListener) {
			__super.addEventListener.apply(this, arguments);
		}
		
		
			
	}
	
	//Element.prototype.handleEvent = function()
	
	Element.prototype.removeEventListener = function(whichEvent, handler, useCapture) {
		if (useCapture == undefined) useCapture = false;
		
		if (this.__native__ && typeof(this.__native__.removeEventListener) != "undefined") {
			this.__native__.removeEventListener.call(this, whichEvent, handler, useCapture);
		} else if (typeof(this.detachEvent) != "undefined") {
			this.detachEvent("on" + whichEvent, handler);
		} else if (!Class.instanceOf(this, Element) && this.addEventListener) {
			this.removeEventListener(whichEvent, handler, useCapture);
		}
		
		if (__super && __super.removeEventListener) {
			__super.removeEventListener.apply(this, arguments);
		}
		
		
	}
	
	Element.prototype.dispatchEvent = function(event) {
		if (this.__native__.dispatchEvent) {
			this.__native__.dispatchEvent.call(this, event);
		} else {
			__super.dispatchEvent.apply(this, arguments);
		}
	}
	
	Element.prototype.getDataset = function() {
		var target = this;
		if (this.__dataset) {
			return this.__dataset;
		}
		var dataset = {};
		dataset.set = function(name, value) {
			target.setAttribute("data" + StringUtils.camelCaseToHyphenated(name));
		}
		dataset.get = function(name) {
			return target.getAttribute("data" + StringUtils.camelCaseToHyphenated(name));
		}
		this.__dataset = dataset;
		return dataset;
	}
	
	Element.prototype.destroy = function() {
		__super.destroy.call(this);
	}
	
	Element.prototype.getDocument = function() {
		return (typeof(this.documentElement) != "undefined") ? this : this.ownerDocument;
	}
	
	Element.prototype.getChildIndex = function(child) {
		for (var i = 0;i<this.childNodes.length; i++) {
			if (this.childNodes[i] == child) {
				return i;
			}
		}
		return -1;
	}
	
	Element.prototype.getDefaultView = function() {
		return Element.getDefaultView(this);
	}
	
	Element.getDefaultView = function(el) {
		var win;
		if (typeof(this.alert) != "undefined") return el;
		var doc = Element.prototype.getDocument.call(el);
		if (doc) {
			try {
				win = (typeof(doc.parentWindow) != "undefined") ? doc.parentWindow : typeof(doc.defaultView) != "undefined" ? doc.defaultView : null
				return win;
			} catch (e) {
				
			}
		}
		return null;
		var doc = Element.prototype.getDocument.call(this);
		
		if (doc) {
			try {
				var win = (typeof(doc.parentWindow) != "undefined") ? doc.parentWindow : typeof(doc.defaultView) != "undefined" ? doc.defaultView : null
				return win;
			} catch (e) {
				
			}
		}
		return null;
	}
	
	Element.prototype.getTopLevelView = function() {
		var win = Element.prototype.getDefaultView.call(this);
		var c = 0;
		while (win.parent) {
			win = win.parent;
			if (c > 10) break;
			c++;
		}
		return win;
	}
	
	/**
	 * Creates an element of the specified class on the object's owner document.
	 * @method createElement
	 * @param {benignware.core.Class} classObject
	 * @return {benignware.core.Element} the created instance
	 */
	Element.prototype.createElement = function(classObject, args) {
		var doc = this.getDocument();
		return Element.create(doc, classObject, args);
	}

	Element.prototype.createTextNode = function(text) {
		var doc = this.getDocument();
		return doc.createTextNode(text);
	}
	
	Element.cloneElement = function(elem) {
		var newElem = elem.cloneNode(false);
		if (elem.nodeType == 1) {
			if (elem.getAttribute("src")) {
				newElem.setAttribute("src", elem.getAttribute("src"));
			}
		}
		if (elem.nodeType == 1) {
			if (elem.getClass) {
				var classObj = elem.getClass();
				for (var x in classObj.prototype) {
					if (typeof(classObj.prototype[x]) != "function" && typeof(elem[x]) != "undefined") {
						newElem[x] = elem[x];
					}
				}
				Element.initialize(newElem, classObj);
				
			} else {
				for (var i = 0; i < elem.childNodes.length; i++) {
					var child = elem.childNodes[i];
					var newChild = Element.cloneElement(child);
					newElem.appendChild(newChild);
				}
			}
		}
		return newElem;
	}
	
	/**
	 * Creates an event of the specified type on the object's owner document.
	 * @method createEvent
	 * @param {String} type
	 * @param {Boolean} bubbles
	 * @param {Boolean} cancelable
	 * @return {benignware.events.Event} the created event
	 */
	Element.prototype.createEvent = function(type, bubbles, cancelable) {
		var doc = this.getDocument();
		if (!doc) return null;
		return Event.create(doc, type, bubbles, cancelable);
	}
	
	/**
	 * Adds a css selector to the css classname of the component.
	 * @method addCSSName
	 * @param {String} str a css selector
	 */
	Element.prototype.addCSSName = function(str){
		Element.addCSSName(this, str);
	}
	/**
	 * Removes the specified css selector from the css classname of the component.
	 * @method removeCSSName
	 * @param {String} str a css selector
	 */
	Element.prototype.removeCSSName = function(str) {
		Element.removeCSSName(this, str);
	}
	/**
	 * Sets the specified style on the component.
	 * @method setStyle
	 * @param {String} styleName
	 * @param {Object} styleValue
	 */
	Element.prototype.setStyle = function(styleName, styleValue) {
		if (styleName.toLowerCase()=="float") {
			this.style.cssFloat = styleValue;
			this.style.styleFloat = styleValue;
			return;
		}
		this.style[styleName] = styleValue;
	}
	/**
	 * Retrieves the computed value of the specified style property
	 * @method getStyle
	 * @param {String} styleName
	 * @return {Object} the style value
	 */
	Element.prototype.getStyle = function(styleName) {
		if (styleName=="float") {
			if (this.style.cssFloat!=undefined) return this.style.cssFloat;
			return this.style.styleFloat;
		}
		return Element.getComputedStyle(this, styleName);
	}
	
	/*
	 * static methods
	 */
//	Element.addEventListener = function(object, whichEvent, handler, useCapture) {
//		if (!object) return;
//		if (useCapture==undefined) useCapture = false;
//		if (object.addEventListener) object.addEventListener(whichEvent, handler, useCapture);
//		if (object.attachEvent) object.attachEvent("on"+whichEvent, handler);
//	}
//	Element.removeEventListener = function(object, whichEvent, handler, useCapture) {
//		if (!object) return;
//		if (useCapture==undefined) useCapture = false;
//		if (typeof(object.removeEventListener) != "undefined") object.removeEventListener(whichEvent, handler, useCapture);
//		if (typeof(object.detachEvent) != "undefined") object.detachEvent("on"+whichEvent, handler);
//	}
	
	Element.addEventListener = function(object, whichEvent, handler, useCapture) {
		 Element.prototype.addEventListener.call(object, whichEvent, handler, useCapture);
	}
	 
	Element.removeEventListener = function(object, whichEvent, handler, useCapture) {
		Element.prototype.removeEventListener.call(object, whichEvent, handler, useCapture);
	}
	
	
	Element.dispatchEvent = function(object, event) {
		if (!object) return;
		
		if (typeof(object.dispatchEvent) == "function") {
			object.dispatchEvent(event);
		} else {
				EventDispatcher.prototype.dispatchEvent.call(object, event);
			}
	}
	
	
	/**
	 * Adds a css selector to the css classname of the component.
	 * @method addCSSName
	 * @param {String} str a css selector
	 */
	Element.addCSSName = function(elem, str){
		Element.removeCSSName(elem, str);
		var cssName = elem.className || "";
		elem.className = cssName + " " + str;
	}
	/**
	 * Removes the specified css selector from the css classname of the component.
	 * @method removeCSSName
	 * @param {String} str a css selector
	 */
	Element.removeCSSName = function(elem, str) {
		if (elem.className) elem.className = elem.className.replace(new RegExp("\\s+" + str, ""), "");
	}
	
	/**
	 * Creates a class instance from a given HTML Element.
	 * Use this method with document.getElementById to initialize classes on existing html elements.
	 * @static
	 * @method initialize
	 * @param {benignware.core.Element} element
	 * @param {benignware.core.Class} classObject
	 * @param {Array} args
	 * @return {benignware.core.Element} the instantiated element
	 */
	Element.initialize = function(element, __class, args) {
		if (Class.instanceOf(element, __class)) {
			// element already initialized
			return;
		}
		args = (typeof(args) == "undefined") ? [] : args;
		var nativeObj = {}
		for (var x in __class.prototype) {
			
			if (typeof(__class.prototype[x]) == "function" && typeof(element[x]) == "function") {
				nativeObj[x] = (function(){
					var m = x;
					var nativeMethod = element[x];
					
					function __nativeDelegate() {
						var _args = arguments || [];
						try {
							return nativeMethod.apply(element, _args);
						} catch(e) {
							
							try {
								// IE
								return nativeMethod(_args[0], _args[1], _args[2], _args[3]);
							} catch(e) {
								// ERROR
							}
						}
						return null;
					}
					return __nativeDelegate;
				})();
			}
			
				
			if (typeof(element[x]) == "undefined" || typeof(element[x]) == "function") {
				try {
					element[x] = __class.prototype[x];
				} 
				catch (e) {
				}
			}
		}
		
		try {
			
			element.__class__ = __class;
			
			element.__native__ = nativeObj;
			if (__class.apply) {
				__class.apply(element, args);
			}
		
		} catch (e) {
			//alert("ELEM INIT: "+e);
			var msg = "initialization of class failed. \n\n";
			for (var x in e) {
				msg+=x+": "+e[x]+"\n";
			}
			msg+= "class: " + __class;
			//alert(msg);
			console.error(msg);
		}
		
		return element;
	}
	
	
	/**
	 * Constructs a new instance of the specified class. 
	 * Insert the target document as first argument.
	 * @static
	 * @method create
	 * @param {benignware.core.Class} classObject
	 * @param {Array} args
	 * @return {benignware.core.Element} the created instance
	 */
	Element.create = function(doc, classObject, args) {
		if (arguments.length==1) {
			classObject = arguments[0];
			doc = document;
		}
		if (arguments.length == 0) {
			classObject = Element;
			doc = document;
		}
		if (typeof(classObject) == "string") {
			//__class = Class.require(__class);
		}
		if (classObject) {
			var domElement = (typeof(classObject) == "string") ? classObject : (classObject.prototype && classObject.prototype.baseElement) ? classObject.prototype.baseElement : "div";
			var element;
			if (typeof(doc.__native__) != "undefined" && typeof(doc.__native__.createElement) == "function") {
				element = doc.__native__.createElement(domElement);
			} else {
				element = doc.createElement(domElement);
			}
			
			if (typeof(classObject) != "string") Element.initialize(element, classObject, args);
			return element;
		} else {
			console.error('classObject is not defined');
		}
		return null;
	}
	
	Element.prototype.getOuterHTML = function() {
		if (this.outerHTML) return this.outerHTML;
		var attrs = this.attributes;
		var str = "<" + this.tagName;
		for (var i = 0; i < attrs.length; i++) {
			str += " " + attrs[i].name + "=\"" + attrs[i].value + "\"";
		}
	   	if (this.childNodes.length == 0) {
			return str + "/>";
		}
		return str + ">" + this.innerHTML + "</" + this.tagName + ">";
	}
	
	Element.isEditable = function(elem){
		var cnt = Element.getEditableContainer(elem);
		return cnt ? true : false;
	}
	
	Element.isEditableElement = function(elem) {
		if (elem.nodeType != 1) {
			return false;
		}
		var attrValue = elem.getAttribute("contenteditable");
		return (typeof(attrValue) != "undefined" && attrValue == "true" || (typeof(attrValue) == "undefined") && (elem.contentEditable == "true"));
	}
	
	Element.getEditableContainer = function(elem) {
		while(elem) {
			if (Element.isEditableElement(elem)) {
				return elem;
			}
			if (elem.parentNode) {
				elem = elem.parentNode;
			} else return null;
			
		}
		return null;
	}
	
	 /**
	 * Cross-browser implementation of the getComputedStyle method.
	 * This method retrieves the current value for the specified style property
	 * @param {benignware.core.Element} element
	 * @param {String} styleName
	 * @return {String} the style value
	 */
	Element.getComputedStyle = function(element, styleName) {
		var doc = Element.prototype.getDocument.call(element);
		if (doc != null) {
			if (doc.defaultView && doc.defaultView.getComputedStyle) {
				var result = doc.defaultView.getComputedStyle(element, "");
				if (result) {
					var value = result.getPropertyValue(styleName);
					return value;
				}
			}
			if (element.currentStyle) {
				var a = styleName.split("-");
				if (a.length) {
					for (var i=1;i<a.length;i++) a[i] = a[i].charAt(0).toUpperCase()+a[i].substring(1);
					styleName = a.join("");
				}
				
				return element.currentStyle[styleName];
			}
		}
		
		return null;
	}
	 
	 Element.getWindowSize = function() {
		var win = Element.prototype.getDefaultView.call(this);
		if (win) {
			if (typeof(win.innerWidth) != "undefined") {
				return {
					width:win.innerWidth, 
					height:win.innerHeight
				}
			}
			var doc = win.document;
			if (doc.documentElement.clientWidth) {
				return {
					width: doc.documentElement.clientWidth, 
					height: doc.documentElement.clientHeight
				};
			}
		}
		return null;
	}
	 
	
	/**
	 * Retrieves a metric object with border dimensions.
	 * Add "border", "padding" or "margin" to get the sum of their dimensions
	 * @param {benignware.core.Element} element
	 * @param {String} metricType
	 * @return {Object} an object containing the properties 'top', 'left', 'right' and 'bottom'
	 */
	Element.getBorderMetrics = function (element, metricType) {
		var result = {top:0, left:0, right:0, bottom:0}
		var metricTypes = [];
		for (var i=1;i<arguments.length;i++) {
			if (arguments[i]!=undefined) metricTypes.push(arguments[i]);
		}
		if (metricTypes.length == 0) {
			metricTypes = ["border"];
		}
		for (var i=0;i<metricTypes.length;i++) {
			var metricsType = metricTypes[i];
			if (metricsType) {
				var widthExt = (metricsType == "border") ? "-width" : "";
				for (var x in result) {
					var propName = metricsType + "-" + x + widthExt;
					var s = Element.getComputedStyle(element, propName);
					var v = parseFloat(s);
					v = (v) ? v : 0;
					result[x]+=v;
				}
			}
		}
		return result;
	}
	
	Element.setX = function(element, x){
		element.style.left = x+"px";
	}
	
	Element.setY = function(element, y){
		element.style.top = y+"px";
	}
	
	/**
	 * Sets the specified width and height on the specified element.
	 * @static
	 * @method setSize
	 * @param {benignware.core.Element} element the target element
	 * @param {Number} w width of the component in pixels
	 * @param {Number} h height of the component in pixels
	 */
	Element.setSize = function(element, w, h){
		element.style.width = w+"px";
		element.style.height = h+"px";
	}
	/**
	 * Retrieves width and height of the specified element.
	 * @static
	 * @method getSize
	 * @param {benignware.core.Element} element the target element
	 * @return {Object} An Object containing the width and height of the component as properties x and y.
	 */
	Element.getSize = function(element) {
		var m = Element.getBorderMetrics(element, "border");
		return {x: element.offsetWidth - m.left - m.right, y: element.offsetHeight - m.top - m.bottom}

	}
	/**
	 * Sets the specified width on the specified element.
	 * @static
	 * @method setWidth
	 * @param {benignware.core.Element} element the target element
	 * @param {Number} w width of the component in pixels
	 */
	Element.setWidth = function(element, w){
		element.style.width = w+"px";
	}
	/**
	 * Retrieves width of the specified element.
	 * @static
	 * @method getWidth
	 * @param {benignware.core.Element} element the target element
	 * @return {Number} width of the component in pixels.
	 */
	Element.getWidth = function(element) {
		return Element.getSize(element).x;
	}
	/**
	 * Sets the specified height on the specified element.
	 * @static
	 * @method setHeight
	 * @param {benignware.core.Element} element the target element
	 * @param {Number} h height of the component in pixels
	 */
	Element.setHeight = function(element, h){
		element.style.height = h+"px";
	}
	/**
	 * Retrieves height of the specified element.
	 * @static
	 * @method getHeight
	 * @param {benignware.core.Element} element the target element
	 * @return {Number} height of the component in pixels
	 */
	Element.getHeight = function(element) {
		return Element.getSize(element).y;
	}
	
	/**
	 * Retrieves inner width of the specified element.
	 * @static
	 * @method getInnerWidth
	 * @param {benignware.core.Element} element the target element
	 * @return {Number} inner width of the component in pixels
	 */
	Element.getInnerWidth = function(element) {
		var m = Element.getBorderMetrics(element, "padding");
		return element.offsetWidth - m.left - m.right;
	}
	/**
	 * Retrieves inner height of the specified element.
	 * @static
	 * @method getInnerHeight
	 * @param {benignware.core.Element} element the target element
	 * @return {Number} inner height of the component in pixels
	 */
	Element.getInnerHeight = function(element) {
		var m = Element.getBorderMetrics(element, "padding");
		return element.offsetHeight-m.top-m.bottom;
	}
	
	
	Element.getOffsetWidth = function(element) {
		return element.offsetWidth;
	}
	Element.getOffsetHeight = function(element) {
		return element.offsetHeight;
	}
	/**
	 * Sets the total width of the specified element.
	 * @static
	 * @method setTotalWidth
	 * @param {benignware.core.Element} element the target element
	 * @param {Number} w total width of the component in pixels
	 */
	Element.setTotalWidth = function(element, w){
		var m = Element.getBorderMetrics(element, "border", "padding");
		Element.setWidth(element, w-m.left-m.right);
	}
	/**
	 * Sets the total height of the specified element.
	 * @static
	 * @method setTotalHeight
	 * @param {benignware.core.Element} element the target element
	 * @param {Number} w total height of the component in pixels
	 */
	Element.setTotalHeight = function(element, h){
		var m = Element.getBorderMetrics(element, "border", "padding");
		Element.setHeight(element, h-m.top-m.bottom);
		
		
	}
	/**
	 * Retrieves the total width of the specified element.
	 * @static
	 * @method getTotalWidth
	 * @param {benignware.core.Element} element the target element
	 * @return {Number} total width of the component in pixels
	 */
	Element.getTotalWidth = function(element){
		var m = Element.getBorderMetrics(element, "margin", "padding");
		return Element.getWidth(element)+m.left+m.right;
	}
	/**
	 * Retrieves the total height of the specified element.
	 * @static
	 * @method getTotalHeight
	 * @param {benignware.core.Element} element the target element
	 * @return {Number} total height of the component in pixels
	 */
	Element.getTotalHeight = function(element){
		var m = Element.getBorderMetrics(element, "margin", "padding");
		return Element.getHeight(element)+m.top+m.bottom;
	}
	
	/*
	Element.getPosition = function(element){
		var m = Element.getBorderMetrics(element, "border");
		return {x: element.offsetWidth-m.left-m.right, y: element.offsetHeight-m.top-m.bottom}
		var w = parseInt(element.style.width);
		var h = parseInt(element.style.height);
		return {x:w, y:h}
	}
	*/
	
	Element.setOffsetLeft = function(element){
		var m = Element.getBorderMetrics(element, "margin");
		element.style.left = element.offsetLeft-m.left;
	}
	
	Element.setOffsetTop = function(element){
		var m = Element.getBorderMetrics(element, "margin");
		element.style.top = element.offsetTop-m.top;
	}
	
	
	Element.getPosition = function (element, parent) {
		var pos;
		if (element && element == window) {
			pos = {x: 0, y: 0};
		}
		if (!element || !element.ownerDocument) {
			return null;
		}
		if (!pos) {
			var doc = element.ownerDocument;
			var docElem = doc.documentElement;
			var pos = {x:0, y:0}
			if (docElem) {
				pos = {x: -docElem.scrollLeft, y: -docElem.scrollTop}
			}

			if (element) {
				var elem = element;
				do {
//					
					pos.x += elem.offsetLeft;
					pos.y += elem.offsetTop;
	
					pos.x -= elem.scrollLeft;
					pos.y -= elem.scrollTop;
					
				} while (elem = elem.offsetParent);
			}
			
			
			
			if (!parent) {
				return pos;
			}
		}
		if (parent && parent != element) {
			var ppos = Element.getPosition(parent);
			pos = {x: pos.x - ppos.x, y: pos.y - ppos.y}
		}
		return pos;
	} 
	
	Element.getScrollPosition = function(doc) {
		doc = typeof(doc) != "undefined" ? doc : document;
		var win = Element.prototype.getDefaultView.call(doc);
		var scrollLeft = document.body.scrollLeft;
		if (!scrollLeft) {
			if (win.pageXOffset) {
				scrollLeft = win.pageXOffset;
			} else {
				scrollLeft = doc.body.parentElement ? doc.body.parentElement.scrollLeft : 0;
			}
		}
		var scrollTop = doc.body.scrollTop;
		if (scrollTop == 0) {
			if (win.pageYOffset) {
				scrollTop = win.pageYOffset;
			} else {
				scrollTop = doc.body.parentElement ? doc.body.parentElement.scrollTop : 0;
			}
		}
		return {x: scrollLeft, y: scrollTop} 
	}
	
	Element.getWindowSize = function(doc) {
		doc = typeof(doc) != "undefined" ? doc : document;
		var win = Element.getDefaultView(doc);
		if (win) {
			if (typeof(win.innerWidth) != "undefined") {
				return {
					width: win.innerWidth, 
					height: win.innerHeight
				}
			} else if (doc.documentElement.clientWidth) {
				return {
					width: doc.documentElement.clientWidth, 
					height: doc.documentElement.clientHeight
				};
			} else {
				return {
					width: doc.getElementsByTagName('body')[0].clientWidth,
					height: doc.getElementsByTagName('body')[0].clientHeight
				};
			}
		}
		return {w: 0, h: 0};
	}
	
	
	Element.getScaledSize = function(originalWidth, originalHeight, width, height, scaleMode, maxBounds) {
		scaleMode = typeof(scaleMode) != "undefined" ? scaleMode : 1;
		maxBounds = typeof(maxBounds) != "undefined" ? maxBounds : {width: originalWidth, height: originalHeight}; 
		var r1 = originalWidth / width;
		var r2 = originalHeight / height;
		var w, h;
		if (scaleMode == 1 || scaleMode == 2) {
			var largestRatio;
			if (scaleMode == 1) {
				largestRatio = Math.max(r1, r2);
			} else {
				largestRatio = Math.min(r1, r2);
			}
			w = originalWidth * (1 / largestRatio);
			h = originalHeight * (1 / largestRatio);
		}
		w = w < maxBounds.width || maxBounds.width == 0 ? w : maxBounds.width;
		h = h < maxBounds.height || maxBounds.height == 0 ? h : maxBounds.height;
		return {
			width: w, 
			height: h
		}
	}
	
	
	Element.getContainerSize = function(element) {
		var w, h;
		if (element.offsetParent) {
			return {w: element.offsetParent.offsetWidth, h: element.offsetParent.offsetHeight}; 
		} else {
			return Element.getWindowSize();
		}
	}
	
	return Element
})();
/**
 * dom ready listener
 * @class benignware.utils.DOMContent
 * @extends benignware.events.EventDispatcher
 */
benignware.core.Class.registerClass("benignware.core.DOMContent", (function(){
	
	var Class = benignware.core.Class;
	var EventDispatcher = Class.require("benignware.events.EventDispatcher");
	var Event = Class.require("benignware.events.Event");
	
	var __super;
	/**
	 * Retrieves the dom-object for the specified html-document.
	 * @constructor
	 * @param {HTMLDocument} doc
	 */
	
	var docInstances = [];
	
	function DOMContent(doc) {
		initialize.call(this, doc);
	}
	
	DOMContent.prototype.__document = null;
	
	Class.extend(EventDispatcher, DOMContent);
	__super = Class.getSuper(DOMContent);
	
	DOMContent.parsers = [];
	
	// STATIC METHODS
	DOMContent.getInstance = function(doc) {
		doc = typeof(doc) == "undefined" ? document : doc; 
		for (var i = 0; i < docInstances.length; i++) {
			if (docInstances[i].__document == doc) {
				return docInstances[i];
			}
		}
		var instance = new DOMContent(doc);
		docInstances.push(instance);
		return instance;
	}
	
	
	
	function initialize(doc) {
		this.__document = doc;
		this.__domContentLoaded = false;
		this.__parsers = [];
		// add default parsers

		// init
		var target = this;
		// callback
		function callback() {
			if (target.__domContentLoaded) return;
			target.__domContentLoaded = true;
			// parse before dispatch
			parse.call(target, doc);
			// dispatch
			var domContentLoadedEvent = Event.create(doc, "DOMContentLoaded", false, false);
			target.dispatchEvent(domContentLoadedEvent);
			var readyEvent = Event.create(doc, "ready", false, false);
			target.dispatchEvent(readyEvent);
		}
		// dom content listener
		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);
		
	}
	
	DOMContent.prototype.isDOMContentLoaded = function() {
		this.__domContentLoaded;
	}
	
	function parse(doc) {
		// parse dom
		var parsers = DOMContent.parsers;
		for (var i = 0; i < parsers.length; i++) {
			var parser = parsers[i];
			if (typeof(parser) != "object") {
				var parserClass = null;
				if (typeof(parser) == "string") {
					parserClass = Class.getClass(parser);
				} else if (typeof(parser == "function")) {
					parserClass = parser;
				}
				if (parserClass) {
					parser = new parserClass();
				}
			}
			if (parser && parser.parse) {
				// parse
				parser.parse(doc);
			}
		}
	}
	
	var dom = DOMContent.getInstance(document);
	dom.addEventListener("DOMContentLoaded", function(event) {
//		console.log("DOM IS READY");
	});
	
	return DOMContent;
})());
(function(){
	
	var Class = benignware.core.Class;
	var Element = Class.require("benignware.core.Element");
	var DOMContent = Class.require("benignware.core.DOMContent");
	
	function HTMLAttributesParser() {}
	
	Class(HTMLAttributesParser);
	
	DOMContent.parsers.push(HTMLAttributesParser);
	
	HTMLAttributesParser.prototype.__document = null;
	
	Class.registerClass("benignware.core.HTMLAttributesParser", HTMLAttributesParser);

	HTMLAttributesParser.prototype.parse = function(doc) {
		parseElements.call(this, doc.documentElement, parseElement);
	}
	
	function parseElements(element, callback) {
		var children = element.childNodes;
		for (var i = 0; i < children.length; i++) {
			var child = children[i];
			if (child.nodeType == 1) {
				parseElements(child, callback);
				callback.call(this, child);
			}
		}
	}
	
	function parseElement(element) {
		var props = [];
		var attrs = element.attributes;
		var type = null;
		for (var i = 0; i < attrs.length; i++) {
			var attrNode = attrs[i];
			var name = attrNode.nodeName.toLowerCase();
			var value = attrNode.nodeValue;
			if (name.indexOf("data-") == 0) {
				var dataName = name.substring("data-".length);
				if (dataName == 'type') {
					type = value;
				} else {
					props.push({name: dataName, value: value});
				}
			}
		}
		if (type) {
			var __class = Class.getClass(type);
			if (__class) {
				Element.initialize(element, __class);
			}
			console.log("INIT TYPE: " + type)
		}
		for (var i = 0; i < props.length; i++) {
			var prop = props[i]
			var name = prop.name
			var value = prop.value
			var propName = hyphenatedToCamelCase(name);
			if (Class.hasSetter(element, propName)) {
				Class.callSetter(element, propName, value);
			}
		}
	}
	
	// TODO: StringUtils
	function hyphenatedToCamelCase(name) {
		var split = name.split("-");
		var result = "";
		for (var i = 0; i < split.length; i++) {
			var word = split[i];
			if (i == 0 || !word.length) {
				result+= word;
			} else {
				result+= word.substring(0, 1).toUpperCase() + word.substring(1);
			}
		}
		return result;
	}
	
	return HTMLAttributesParser;
	
})();
(function(){
	
	var Class = benignware.core.Class;
	var Element = Class.require("benignware.core.Element");
	var DOMContent = Class.require("benignware.core.DOMContent");
	
	function HTMLAnnotationsParser() {
	}
	
	HTMLAnnotationsParser = Class(HTMLAnnotationsParser);
	
	Class.registerClass("benignware.core.HTMLAnnotationsParser", HTMLAnnotationsParser);
	
	DOMContent.parsers.push(HTMLAnnotationsParser);
	
	HTMLAnnotationsParser.prototype.parse = function(doc) {
		parseComments.call(this, doc.documentElement, parseComment);
	}
	
	function stripWhitespace(elem) {
		var result = [];
		if (elem) {
			for (var i = 0; i < elem.childNodes.length; i++) {
				var child = elem.childNodes[i];
				if (child.nodeType == 3 && !child.nodeValue.replace(/^\s+|\s+$/g, '')) {
					elem.removeChild(child);
					i--;
				} else {
					result.push(child);
				}
			}
		}
		return result;
	}
	
	function parseComments(element, callback) {
		var children = 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.call(this, nextSibling, callback);
							elem = nextSibling;
							i++;
						}
					}
					elem = elem && elem.nodeType == 1 ? elem : null;
					
					if (elem) {
						callback(comment, elem);
					}
				}
			} else if (child.nodeType == 1 && child.nodeName.toLowerCase() != "iframe") {
				parseComments.call(this, 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") {
				className = tags[i].value.replace(/^\s+|\s+$/g, '');
			}
		}
		
		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":
					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 = tagValue.substring(m.index + m[1].length + 1).replace(/^\s+|\s+$/g, '');
					if (tagName == "set") {
						if (Class.hasSetter(element, name)) {
							Class.callSetter(element, name, value);
							break;
						} else {
							tagName = "attribute";
						}
					}
					if (tagName == "attribute") {
						element.setAttribute(name, value);
					} else if (tagName == "event") {
//						if (this.getAllowScripting()) {
//							var ef = function(event) {
//								eval(value);
//							}
//							Element.addEventListener(element, name, function(event) {
//								ef.call(element, event); 
//							}, false);
//						}
					}
					break;
			}
		}
	}
	
	return HTMLAnnotationsParser;
	
})();
/**
 * Stores the current mouse position
 * @class benignware.utils.Mouse
 */
benignware.core.Class.namespace("benignware.utils.*");
benignware.utils.Mouse = (function(){
	var Class = benignware.core.Class;
	var mouseObjects = [];
	/**
	 * Retrieves a mouse-object for the specified document.
	 * @constructor
	 * @param {HTMLDocument} doc
	 */
	function Mouse(doc) {
		doc = doc || document;
		for (var i=0;i<mouseObjects.length;i++) if (mouseObjects[i].document==doc) return mouseObjects[i];
		mouseObjects.push(this);
		//
		this.document = doc;
		this.x = 0;
		this.y = 0;
		var target = this;
		var mouseMoveHandler = function mouseMoveHandler(event) {
			target.x = event.clientX || event.pageX;
			target.y = event.clientY || event.pageY;
		};
		if (doc.addEventListener) doc.addEventListener("mousemove", mouseMoveHandler, false);
		else if (doc.attachEvent) doc.attachEvent("onmousemove", mouseMoveHandler);
		return this;
	}
	/**
	 * Gets the current mouse position
	 * @method getPosition
	 * @return {Object} an object with the properties 'x' and 'y' 
	 */
	Mouse.prototype.getPosition = function() {
		return {x:this.x, y:this.y}
	}
	/**
	 * Retrieves a mouse-object for the specified document.
	 * @static
	 * @method get
	 * @param {HTMLDocument} doc
	 * @return {benignware.utils.Mouse} the mouse-object for the specified document
	 */
	Mouse.get = function(doc) {
		return new Mouse(doc);
	}
	return Mouse;
})();
/**
 * @package benignware.core
 */

/**
 * The Component class is the base class for all user interface controls.
 * @class benignware.core.Component
 * @extends benignware.core.Element
 */
(function() {
	
	var __super;
	var Class = benignware.core.Class;
	var Element = Class.require("benignware.core.Element");
	var Event = Class.require("benignware.events.Event");
	var Mouse = Class.require("benignware.utils.Mouse");
	
	/**
	 * Constructs a new component.
	 * @constructor
	 */
	
	function isChildOf(child, parent) {
		if (!child || !parent || !parent.firstChild) return false;
		if (parent === child) return false;
		try {
			while (child.parentNode) {
				if (child.parentNode == parent) return true; else {
					child = child.parentNode;
				}
			}
		} catch (e) {
		}
		return false;
	}
	
	function Component() {
		__super.call(this);
		this.addCSSName("benignware");
		
		var doc = this.getDocument();
		var win = this.getDefaultView();
		var target = this;
		//
		if (win != null) {
			Element.addEventListener(win, "blur", function(event) {
				loseFocus(event);
			}, false);
			Element.addEventListener(win, "resize", function(event) {
				target.invalidateSize();
			}, false);
		}
		
		this.addEventListener("blur", function(event) {
			loseFocus(event);
		}, false);
		//
		this.addEventListener("addedToDOM", function() {
			target.invalidateSize();
		}, false);
		
		//
		if (typeof(this.onmouseenter) == "undefined") {
			var mouseOverFlag = undefined;
			var mouseEnter = function(event) {
				var relTarget = event.relatedTarget;
				// handle firefox <3.6 xul / anonymous div bug:
				if (!relTarget) return;
				if (HTMLElement) {
					var s = HTMLElement.prototype.toString.call(relTarget);
					if (s == "[xpconnect wrapped native prototype]" || s == "[object XULElement]") return;
				}
				//
				if (mouseOverFlag != undefined && relTarget && (target == relTarget || isChildOf(relTarget, target))) return;
				
				if (mouseOverFlag == undefined || event.type == "mouseover") {
					mouseOverFlag = true;
					var rollOverEvent = target.createEvent("mouseenter", false);
					rollOverEvent.relatedTarget = event.relatedTarget;
					rollOverEvent.toElement = event.relatedTarget;
					target.dispatchEvent(rollOverEvent);
				} else {
					mouseOverFlag = false;
					var rollOutEvent = target.createEvent("mouseleave", false);
					rollOutEvent.relatedTarget = event.relatedTarget;
					rollOutEvent.fromElement = event.relatedTarget;
					target.dispatchEvent(rollOutEvent);
				}
			};
			this.addEventListener("mouseover", mouseEnter, false);
			this.addEventListener("mouseout", mouseEnter, false);
		}
		
		this.addEventListener("mouseenter", function(){
			target.addCSSName("hover");
		}, false);
		
		this.addEventListener("mouseleave", function(){
			target.removeCSSName("hover");
		}, false);

		var mousedownFlag = false;
		function focus(event) {
			event = Event.getEvent(event);
			if (!target.hasFocus) {
				target.hasFocus = true;
				mousedownFlag = true;
				// use native focus-event with form controls, mousedown on other subclasses
//				target.dispatchEvent(target.createEvent("focus", false, false));
			}
		}
		
		function loseFocus(event) {
			event = Event.getEvent(event);
			if (target.hasFocus) {
				target.hasFocus = false;
				target.dispatchEvent(target.createEvent("losefocus", false, false));
			}
		}
		
		this.hasFocus = false;
		
		this.addEventListener("mousedown", focus, false);
		
		//this.addEventListener("focus", focus, false);

		//this.addEventListener("blur", loseFocus, false);
		
		function docLoseFocusHandler(event) {
			event = Event.getEvent(event);
			if (!mousedownFlag && event.target != target && !isChildOf(target, event.target)) {
				loseFocus(event);
			}
			mousedownFlag = false;
		}
		
		Element.addEventListener(doc, "mousedown", docLoseFocusHandler, false);

		this.addEventListener("selectstart", function(event){
			event = Event.getEvent(event);
			if (!target.textSelectable) {
				event.preventDefault();
			}
		}, false);
		
		
		
		// context menu
		var mouse = Mouse.get(doc);
		
		var contextMenuHandler = function(event) {
			event = Event.getEvent(event);
			
			var menu = target.contextMenu;
			if (typeof(menu) == "undefined") {
				return true;
			}
			if (menu != null) {
				if (Class.instanceOf(menu, Component)) {
					menu.hasFocus = true;
				}
				var body = doc.body;
				body.insertBefore(menu, doc.body.firstChild);
				menu.style.position = "absolute";
				menu.style.display = "block";
				menu.style.zIndex = "1";
				var mousePos = mouse.getPosition();
				var win = target.getDefaultView();
				var pageXOffset = (typeof(doc.documentElement.scrollLeft) != "undefined") ? doc.documentElement.scrollLeft : win.pageXOffset;
				var pageYOffset = (typeof(doc.documentElement.scrollTop) != "undefined") ? doc.documentElement.scrollTop : win.pageYOffset;
				menu.style.left = pageXOffset + mousePos.x + "px";
				menu.style.top = pageYOffset + mousePos.y + "px";
				menu.addEventListener("losefocus", removeContextMenuHandler, false);
				menu.addEventListener("select", removeContextMenuHandler, false);
				event.preventDefault();
				event.stopPropagation();
			}
			
			return false;
		}
		this.addEventListener("contextmenu", contextMenuHandler, false);
		
		
		var removeContextMenuHandler = function(event) {
			var menu = target.contextMenu;
			menu.removeEventListener("losefocus", removeContextMenuHandler, false);
			menu.removeEventListener("select", removeContextMenuHandler, false);
			menu.parentNode.removeChild(menu);
		};
		
		// INIT COMPONENT CREATION
		this.createChildren();
		this.initialize();
		
		if (doc.body && (this == doc.body || isChildOf(this, doc.body))) {
			this.invalidateSize();
		}
		
	}
	Class.registerClass("benignware.core.Component", Component);
	Class.extend(Element, Component);
	__super = Class.getSuper(Component);
	
	
	// constants
	
	// events
	Component.RESIZE = "resize";
	
	
	
	/**
	 * Stores the parent Component instance. 
	 * This value changes when the component is added to the DOM.
	 * @property
	 */
	Component.prototype.parentComponent = null;
	
	Component.prototype.getParentComponent = function() {
		var node = this.parentNode;
		while (node) {
			if (node.instanceOf && node.instanceOf(Component)) return node;
			node = node.parentNode;
		}
		return null;
	}
	
	/**
	 * Indicates if the contained text of this component can be selected. 
	 * The default value is 'true'
	 * @property
	 */
	Component.prototype.textSelectable = true;
	
	Component.prototype.hasFocus = false;
	
	/**
	 * Initializes the component. 
	 * Override this method to initialize subclasses of Component. 
	 * @method initialize
	 */
	Component.prototype.initialize = function() {
		
	}
	
//	Component.prototype.focus = function() {
//		this.hasFocus = true;
//		this.__native__.focus.call(this);
//	}
//	
	Component.prototype.blur = function() {
		this.hasFocus = false;
		this.__native__.blur.call(this);
	}
	
	/**
	 * Override this method to create ui children elements on subclasses of Component.
	 * @method createChildren
	 */
	Component.prototype.createChildren = function() {
	}
	 
	
	
	// layout methods
	Component.prototype.setX = function(x){
		this.style.left = x+"px";
	}
	Component.prototype.getX = function() {
		
	}
	
	Component.prototype.setY = function(y){
		this.style.top = y+"px";
	}
	Component.prototype.getY = function() {
		
	}
	
	/**
	 * Sets the specified width and height on the component.
	 * @method setSize
	 * @param {Number} w width of the component in pixels
	 * @param {Number} h height of the component in pixels
	 */
	Component.prototype.setSize = function(w, h){
		Element.setSize(this, w, h);
		this.invalidateSize();
	}
	/**
	 * Retrieves width and height of the component.
	 * @method getSize
	 * @return {Object} An Object containing the width and height of the component as properties x and y.
	 */
	Component.prototype.getSize = function(){
		return Element.getSize(this);
	}
	/**
	 * Sets the specified width on the component.
	 * @method setWidth
	 * @param {Number} w width of the component in pixels
	 */
	Component.prototype.setWidth = function(w){
		Element.setWidth(this, w);
		this.invalidateSize();
	}
	/**
	 * Retrieves width of the component.
	 * @method getWidth
	 * @return {Number} width of the component in pixels.
	 */
	Component.prototype.getWidth = function() {
		return Element.getWidth(this);
	}
	/**
	 * Sets the specified height on the component.
	 * @method setHeight
	 * @param {Number} h height of the component in pixels
	 */
	Component.prototype.setHeight = function(h){
		Element.setHeight(this, h);
		this.invalidateSize();
	}
	/**
	 * Retrieves height of the component.
	 * @method getHeight
	 * @return {Number} height of the component in pixels
	 */
	Component.prototype.getHeight = function() {
		return Element.getHeight(this);
	}
	
	/**
	 * Retrieves inner width of the component
	 * @method getInnerWidth
	 * @return {Number} inner width of the component in pixels
	 */
	Component.prototype.getInnerWidth = function() {
		return Element.getInnerWidth(this);
	}
	/**
	 * Retrieves inner height of the component
	 * @method getInnerHeight
	 * @return {Number} inner height of the component in pixels
	 */
	Component.prototype.getInnerHeight = function() {
		return Element.getInnerHeight(this);
	}
	/**
	 * Sets the total width of the component.
	 * @method setTotalWidth
	 * @param {Number} w total width of the component in pixels
	 */
	Component.prototype.setTotalWidth = function(w){
		Element.setTotalWidth(this, w);
	}
	/**
	 * Sets the total height of the component.
	 * @method setTotalHeight
	 * @param {Number} w total height of the component in pixels
	 */
	Component.prototype.setTotalHeight = function(h){
		Element.setTotalHeight(this, h);
	}
	/**
	 * Retrieves the total width of the component.
	 * @method getTotalWidth
	 * @return {Number} total width of the component in pixels
	 */
	Component.prototype.getTotalWidth = function(){
		return Element.getTotalWidth(this);
	}
	/**
	 * Retrieves the total height of the component.
	 * @method getTotalHeight
	 * @return {Number} total height of the component in pixels
	 */
	Component.prototype.getTotalHeight = function(){
		return Element.getTotalHeight(this);
	}
	/**
	 * Retrieves border dimensions of the component. 
	 * Use arguments 'border', 'margin' or 'padding' to add up their specific dimensions
	 * @method getBorderMetrics
	 * @return {Object} An Object with the properties 'top', 'left', 'right' and 'bottom'
	 */
	Component.prototype.getBorderMetrics = function() {
		return Element.getBorderMetrics(this, arguments[0], arguments[1], arguments[2]);
	}
	/**
	 * Stores the validated width of the component
	 * @property
	 * @return {Number} validated width of the component in pixels
	 */
	Component.prototype.validatedWidth = null;
	/**
	 * Stores the validated height of the component
	 * @property
	 * @return {Number} validated height of the component in pixels
	 */
	Component.prototype.validatedHeight = null;
	
	/**
	 * Invalidates the Size of the component. 
	 * Call this method from subclasses to check if the components dimensions have changed. 
	 * If so the protected method resizeComponent will then be invoked.
	 * @method invalidateSize 
	 */
	Component.prototype.invalidateSize = function() {
		if (this.parentNode == null) return;
		var w = this.getWidth();
		var h = this.getHeight();
		if (this.parentNode != null && (w != this.validatedWidth || h != this.validatedHeight)) {
			this.validatedWidth = w;
			this.validatedHeight = h;
			this.resizeComponent();
			this.dispatchEvent(this.createEvent(Component.RESIZE));
		}
	}
	/**
	 * Invalidates the the component. 
	 * Call this method from subclasses to check if the components dimensions have changed. 
	 * If so the protected method resizeComponent will then be invoked.
	 * @method invalidateSize 
	 */
	Component.prototype.invalidate = function() {
		this.invalidateSize();
	}
	 /**
	 * Updates the layout of the component's children.
	 * This protected method is called when the size of the component has changed. 
	 * @method layout 
	 */
	Component.prototype.layout = function() {
	}
	 
	// deprecated: use layout instead
	Component.prototype.resizeComponent = function() {
	}
	
	Component.prototype.destroy = function() {
		 __super.destroy.call(this);
		 if (this.stop) {
			 this.stop();
		 }
	 }
	
	 return Component;
})();
/**
 * This class is the base class of all container classes such as Panel, Dialog, Window.
 * @class benignware.core.Container
 * @extends benignware.core.Component
 */
benignware.core.Container = (function(){
	
	var Class = benignware.core.Class;
	var Component = Class.require("benignware.core.Component");
	var Element = Class.require("benignware.core.Element");
	var Event = Class.require("benignware.events.Event");
	var __super;
	/**
	 * Constructs a new container.
	 * @constructor
	 */
	function Container() {
		var children = [];
		if (this.innerHTML) {
			for (var i = 0; i < this.childNodes.length; i++) {
				var child = this.childNodes[i];
				if (child.nodeType == 1) {
					children.push(child);
				}
			}
		}
		__super.call(this);
		for (var i = 0; i < children.length; i++) {
			var child = children[i];
			if (child.nodeType == 1) {
				if (isItem(child)) {
					this.add(child);
				} else {
					this.content.appendChild(child);
				}
			}
		}
	}
	Container = Class(Component, Container);
	__super = Class.getSuper(Container);
	
	Container.prototype.initialize = function() {
		__super.initialize.call(this);
	}
	/**
	 * Holds a reference to the content node of the container. 
	 * Append elements to this node to fill the containers content area.  
	 * @property content
	 */
	Container.prototype.content = null;
	
	Container.prototype.createChildren = function() {
		this.content = this.createElement("div");
		this.content.style.position = "relative";
		this.appendChild(this.content);
	}
	
	function isItem(elem) {
		if (elem.nodeType == 1) {
			var name = elem.nodeName.toLowerCase();
			if (name != "br" && name != "script" && name != "link") {
				return true;
			}
		}
		return false;
	}
	
	function getItems() {
		var items = [];
		var children = this.content.childNodes;
		for (var i = 0; i < children.length; i++) {
			var child = children[i];
			if (isItem(child)) {
				items.push(child);
			}
		}
		return items;
	}

	/* LIST IMPLEMENTATION */
	
	Container.prototype.add = function(child, index) {
		if (typeof(index) == "number") {
			this.content.insertBefore(child, this.get(index));
		} else {
			this.content.appendChild(child);
		}
		this.resizeComponent();
	}
	
	Container.prototype.size = function() {
		return getItems.call(this).length;
	}
	
	Container.prototype.get = function(index) {
		var items = getItems.call(this);
		return items[index];
	}
	
	Container.prototype.indexOf = function(item) {
		var items = getItems.call(this);
		for (var i = 0; i < items.length; i++) {
			var item = items[i];
			if (items[i] == item) {
				return i;
			}
		}
		return -1;
	}
	
	Container.prototype.remove = function(index) {
		var items = getItems.call(this);
		for (var i = 0; i < items.length; i++) {
			var item = items[i];
			if (i == index || item == index) {
				this.removeChild(item);
				break;
			}
		}
	}
	
	Container.prototype.resizeComponent = function() {
	}
	
	
	
	return Container;
})();
(function(){
	
	var Class = benignware.core.Class;
	var Component = Class.require("benignware.core.Component");
	var Element = Class.require("benignware.core.Element");
	var Event = Class.require("benignware.events.Event");
	
	var __super;
	function Image() {
		__super.call(this);
	}
	
	Image = Class.extend(Component, Image);
	
	__super = Class.getSuper(Image);
	
	Class.registerClass("benignware.core.Image", Image);
	
	
	Image.EXACT_FIT = "exactFit"; 
	Image.BEST_FIT = "bestFit"; 
	Image.BEST_FIT_MAX = "bestFitMax";
	
	// default html element
	Image.prototype.baseElement = "img";
	
	Image.prototype.resizeComponent = function() {
	}

	Image.getScaledSize = function(originalWidth, originalHeight, width, height, scaleMode, bounds) {
		
		scaleMode = typeof(scaleMode) != "undefined" ? scaleMode : 1;
		if (typeof(bounds) == "undefined" || bounds == null) {
			bounds = true;
		}
		if (typeof(bounds) == "string") {
			bounds = bounds == "false" || bounds == "0" ? false : true;
		}
		if (typeof(bounds) == "boolean") {
			bounds = bounds ? {width:originalWidth, height: originalHeight} : {width:0, height: 0};
		}
		
		var r1 = originalWidth / width;
		var r2 = originalHeight / height;
		var w, h;
		
		switch (scaleMode) {
			
			case Image.BEST_FIT:
			case Image.BEST_FIT_MAX:
				
				
				var largestRatio;
				if (scaleMode == Image.BEST_FIT) {
					largestRatio = Math.max(r1, r2);
				} else {
					largestRatio = Math.min(r1, r2);
				}
				w = originalWidth * (1 / largestRatio);
				h = originalHeight * (1 / largestRatio);
				
				break;
				
			case Image.EXACT_FIT:
			default:
				w = width;
				h = height;
				break;
				
				
		
		}
		
		w = w < bounds.width || bounds.width == 0 ? w : bounds.width;
		h = h < bounds.height || bounds.height == 0 ? h : bounds.height;
		
		
		
		
		return {
			width: w, 
			height: h
		}
	}
	
	Image.scale = function(elem, width, height, scaleMode, maxBounds) {
//		var inlineStyleWidth = elem.style.width;
//		var inlineStyleHeight = elem.style.height;

		elem.style.width = "auto";
		elem.style.height = "auto";
		var originalWidth = elem.offsetWidth; 
		var originalHeight = elem.offsetHeight; 
		if (originalWidth && originalHeight) {
			var size = Image.getScaledSize(originalWidth, originalHeight, width, height, scaleMode, maxBounds);
			elem.style.width = Math.floor(size.width) + "px";
			elem.style.height = Math.floor(size.height) + "px";
		}
	}
	
	
	return Image;
})();
/**
 * 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 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;
	}

	var css = CSS.getInstance();
	
	// benignware default styles
	
	CSS.setDefaultStyle("*.benignware table", "border", "none");
	//
	CSS.setDefaultStyle("*.benignware td", "padding", "0px");
	CSS.setDefaultStyle("*.benignware td", "margin", "0px");
	//
	CSS.setDefaultStyle("*.benignware th", "padding", "0px");
	CSS.setDefaultStyle("*.benignware th", "margin", "0px");
	//
	CSS.setDefaultStyle("*.benignware input", "margin", "0px");
	CSS.setDefaultStyle("*.benignware select", "margin", "0x");
	CSS.setDefaultStyle("*.benignware textarea", "margin", "0px");
	

// 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();
		//
	}
	 
	Loader.TIMEOUT = 28000;
	 
	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.timeOutEnabled = false;
	
	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;
	}
	
	Loader.prototype.__requestTimer = null;
	
	/**
	 * Sends a http request to the server
	 * @method load
	 * @param {String} filename
	 */
	
	function loaded(req, filename) {
		var loadEvent = new Event("load", false, false);
		loadEvent.url = filename;
		loadEvent.responseText = req.responseText;
		loadEvent.responseXML = req.responseXML;
		this.responseXML = req.responseXML;
		this.responseText = req.responseText;
		this.dispatchEvent(loadEvent);
		this.loaded();
		
	}
	
	function cancelRequest(req) {
		if (typeof(req) === 'object') {
			try {
				req.abort();
			} catch (e) {
				console.error("ERROR WHEN ABORTING HTTP REQUEST " + e);
			}
		}
	}
	
	function load() {
		this.isLoading = true;
		if (!this.loadQueue.length) return;
		var filename = this.loadQueue.shift();
		
		//this.httpRequest = getXMLHttpRequest();
		var req = this.httpRequest;
		this.url = filename;
		if (this.preventCaching) {
			var urlInfo = StringUtils.parseURL(filename);
			var params = urlInfo.params;
			params['__preventCaching'] = new Date().getTime();
			filename = StringUtils.buildURL(filename, params);
		}
		
		for (var x in this.requestHeaders) {
			req.setRequestHeader(x, this.requestHeaders[x]);
		}
		var target = this;
		this.__aborted = false;
		req.onreadystatechange = function() {
			
			if (req.readyState == 4) {
				clearTimeout(target.__requestTimer);
				if (!target.__aborted) {
					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);
					}
				} else {
					// aborted
					target.__aborted = false;
				}
			}
		}
		// variables
		var urlQuery = "";
		for (var x in this.variables) {
			urlQuery+= (!urlQuery.length) ? "" : "&";
			urlQuery+= x+"="+this.variables[x];
		}
		
		if (urlQuery) {
			req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		}
		
		req.open(this.method, filename, this.async);
		
		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);
		}
		
		if (this.__requestTimer) {
			clearTimeout(this.__requestTimer);
		}
		
		// timeout
		if (this.timeOutEnabled) {
			this.__requestTimer = setTimeout(function() {
		       target.abort();
		       var ioErrorEvent = Event.create("error", false, false);
		       target.dispatchEvent(ioErrorEvent);
		    }, Loader.TIMEOUT)
		}
	}
	
	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() {
		
	}
	
	Loader.prototype.abort = function() {
		if (this.isLoading) {
			cancelRequest(this.httpRequest);
			this.isLoading = false;
			this.__aborted = true;
			this.loadQueue = [];
		}
	}
	
	Loader.prototype.skip = function() {
		cancelRequest(this.httpRequest);
		next.call(this);
	}
	
	function next() {
		load.call(this);
	}
	
	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;
})();

/**
 * AJAX JSLoader class 
 * @class benignware.utils.JSLoader
 * @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 __super;
	
	function JSLoader() {
		__super.call(this, "GET");
		//
		this.addEventListener("load", Delegate.create(this, function(event) {
			var url = this.getURL();
			var fileExt = StringUtils.getFileExtension(url);
			var ressource;
			switch (fileExt) {
				case 'properties':
					ressource = new PropertyFile();
					ressource.parse(event.responseText);
					break;
			}
			
			if (!ressource) {
				if (event.responseXML && event.responseXML.documentElement) {
					ressource = event.responseXML;
				} else {
					try {
						if (window.execScript) {
							ressource = window.execScript(event.responseText);
						} else {
							ressource = eval(event.responseText);
						}
					} catch(e) {
						
					}
				}
			}
			if (!ressource) {
				ressource = event.responseText;
			}
			this.__ressources[url] = ressource;
		}), false);
	}
	 
	Class.registerClass("benignware.utils.JSLoader", JSLoader);
	
	Class.extend(Loader, JSLoader);
	
	__super = Class.getSuper(JSLoader);
	
	JSLoader.prototype.__ressources = [];
	
	JSLoader.prototype.complete = function(event) {
		var mainClass = this.getMainClass();
		if (mainClass) {
			var classObj = Class.getClass(mainClass);
			if (classObj) {
				var Element = Class.getClass("benignware.core.Element");
				if (Element) {
					var doc = this.getDocument();
					var element = Element.create(doc, classObj);
					element.loader = this;
					this.__application = element;
				}
			}
		}
		var initEvent = Event.create('init', false, false);
		this.dispatchEvent(initEvent);
	}
	
	JSLoader.prototype.getResource = function(url) {
		return this.__ressources[url];
	}
	
	JSLoader.prototype.setDocument = function(doc) {
		this.__document = doc;
	}
	
	JSLoader.prototype.getDocument = function() {
		return this.__document ? this.__document : document;
	}
	
	JSLoader.prototype.setMainClass = function(mainClass) {
		this.__mainClass = mainClass;
	}
	
	JSLoader.prototype.getMainClass = function() {
		return this.__mainClass;
	}
	
	JSLoader.prototype.getApplication = function() {
		return this.__application;
	}
	
	return JSLoader;
})();
/**
 * DOM Utilities
 * @class benignware.utils.DOM
 * @extends benignware.events.EventDispatcher
 */
benignware.core.Class.registerClass("benignware.utils.DOM", (function(){
	
	var Class = benignware.core.Class;
	var Object = benignware.core.Object;
	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");
	
	function DOM() {}
	Class(DOM);
	__super = Class.getSuper(DOM);
	
	
	DOM.XML_NAMESPACE = "http://www.w3.org/2000/xmlns/";
	
	// STATIC METHODS
	
	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;
	}
	
	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 && !child.nodeValue.replace(/^\s+|\s+$/g, '')) {
					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.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.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;
	}
	
	return DOM;
})());
(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;
})();
/**
 * Tween Class
 * @class benignware.transitions.Tween
 */
(function(){
	// import classes
	var Class = benignware.core.Class;
	var Event = Class.require("benignware.events.Event");
	var EventDispatcher = Class.require("benignware.events.EventDispatcher");
	// define super
	var __super;
	/**
	 * Constructs a new form-element.
	 * @constructor
	 */
	function Tween(duration, easeFunction, begin, finish) {
		__super.call(this);
		this.time = 0;
		this.easeFunction = easeFunction;
		this.begin = typeof(begin) == "number" ? begin : 0;
		this.finish = typeof(finish) == "number" ? finish : 1;
		this.duration = typeof(duration) == "number" ? duration : 1;
	}
	
	Class.registerClass("benignware.transitions.Tween", Tween);

	Tween = Class(EventDispatcher, Tween);
	__super = Class.getSuper(Tween);
	
	// constants
	Tween.INTERVAL_MS = 50;
	Tween.FORWARD = 1;
	Tween.REVERSE = -1;
	
	// events
	Tween.MOTION_CHANGE = "motionChange";
	Tween.MOTION_FINISH = "motionFinish";
	Tween.MOTION_START = "motionStart";
	Tween.MOTION_STOP = "motionStop";
	
	
	Tween.prototype.__isPlaying = false;
	Tween.prototype.__intervalId = null;
	
	Tween.prototype.easeFunction = null;
	Tween.prototype.duration = 0;
	Tween.prototype.begin = 0;
	Tween.prototype.finish = 0;
	
	Tween.prototype.time = 0;
	Tween.prototype.direction = Tween.FORWARD;
	
	Tween.prototype.__interval = Tween.INTERVAL_MS;
	
	function dispatchMotionEvent(type) {
		var motionEvent = new Event(type);
		this.dispatchEvent(motionEvent);
	}
	
	Tween.prototype.play = function() {
		if (!this.__isPlaying) {
			var target = this;
			this.__isPlaying = true;
			this.__intervalId = window.setInterval(function(event){
				// change
				var time = target.time;
				var t = time;
				var diff = (target.__interval / 1000) * target.direction;
				time+= diff;
				
				if (time > target.duration) {
					time = target.duration;
					
				}
				if (time < 0) {
					time = 0;
				}
				target.time = time;
				
				target.update();
				dispatchMotionEvent.call(target, Tween.MOTION_CHANGE);
				if (target.direction == Tween.FORWARD && target.time == target.duration || target.direction == Tween.REVERSE && target.time == 0) {
					dispatchMotionEvent.call(target, Tween.MOTION_FINISH);
					target.stop();
				}
			}, target.__interval);
		}
	}
	
	Tween.prototype.start = function() {
		if (this.__isPlaying) {
			this.stop();
		}
		this.time = this.direction == Tween.REVERSE ? this.duration : 0;
		this.play();
		dispatchMotionEvent.call(this, Tween.MOTION_START);
		dispatchMotionEvent.call(this, Tween.MOTION_CHANGE);
	}
	
	Tween.prototype.stop = function() {
		this.__isPlaying = false;
		if (this.__intervalId) {
			window.clearInterval(this.__intervalId);
		}
		dispatchMotionEvent.call(this, Tween.MOTION_STOP);
	}
	
	Tween.prototype.rewind = function() {
		//this.time = this.direction == Tween.REVERSE ? this.duration : 0;
		this.time = 0;
	}
	
	Tween.prototype.fforward = function() {
		this.time = this.duration;
	}
	
	Tween.prototype.forward = function() {
		this.direction = Tween.FORWARD;
		this.play();
	}
	
	Tween.prototype.reverse = function() {
		this.direction = Tween.REVERSE;
		this.play();
	}
	
	Tween.prototype.yoyo = function() {
		var b = this.begin;
		var f = this.finish;
		this.begin = f;
		this.finish = b;
		this.play();
	}
	
	Tween.prototype.destroy = function() {
		__super.destroy.call(this);
	}
	
	Tween.prototype.update = function() {
	}
	
	Tween.prototype.getPosition = function() {
		var change = this.finish - this.begin;
		if (typeof(this.easeFunction) == "function") {
			return this.easeFunction(this.time, this.begin, change, this.duration);
		} else {
			return change * this.time / this.duration + this.begin;
		}
	}
	
	Tween.prototype.isPlaying = function() {
		return this.__isPlaying;
	}

	return Tween;
})();
/**
 * Robert Penner easing equations
 * http://www.robertpenner.com/easing/
 */
(function(){
	
	var Class = benignware.core.Class;
	
	function Regular() {
	}
	
	Class.registerClass("benignware.transitions.easing.Regular", Regular);
	
	Regular.easeIn = function(t, b, c, d) {
		return c * (t /= d) * t + b;
	}
	
	Regular.easeOut = function(t, b, c, d) {
		return -c * (t /= d) * (t - 2) + b;
	}
	
	Regular.easeInOut = function(t, b, c, d) {
		if ((t /= d / 2) < 1)
			return c / 2 * t * t + b;

		return -c / 2 * ((--t) * (t - 2) - 1) + b;
	}
	
	return Regular;
})();
/**
 * Robert Penner easing equations
 * http://www.robertpenner.com/easing/
 */
(function(){
	
	var Class = benignware.core.Class;
	
	function Back() {
	}
	
	Class.registerClass("benignware.transitions.easing.Back", Back);
	
	Back.easeIn = function(t, b, c, d, s) {
		if (typeof(s) != "number")
			s = 1.70158;
		
		return c * (t /= d) * t * ((s + 1) * t - s) + b;
	}
	
	Back.easeOut = function(t, b, c, d, s) {
		if (typeof(s) != "number")
			s = 1.70158;
		
		return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
	}
	
	Back.easeInOut = function(t, b, c, d, s) {
		if (typeof(s) != "number")
			s = 1.70158; 
		
		if ((t /= d / 2) < 1)
			return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
		
		return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
	}
	
	return Back;
})();
/**
 * Robert Penner actionscript easing equations
 * http://www.robertpenner.com/easing/
 */
(function(){
	
	var Class = benignware.core.Class;
	
	function Elastic() {
	}
	
	Class.registerClass("benignware.transitions.easing.Elastic", Elastic);
	
	Elastic.easeIn = function(t, b, c, d, a, p) {
		if (t == 0)
			return b;
		
		if ((t /= d) == 1)
			return b + c;
		
		if (typeof(p) != "number")
			p = d * 0.3;
		
		var s;
		if (typeof(a) != "number" || a < Math.abs(c)) {
			a = c;
			s = p / 4;
		} else {
			s = p / (2 * Math.PI) * Math.asin(c / a);
		}

		return -(a * Math.pow(2, 10 * (t -= 1)) *
				 Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
	}
	
	Elastic.easeOut = function(t, b, c, d, a, p) {
		if (t == 0)
			return b;
			
		if ((t /= d) == 1)
			return b + c;
		
		if (typeof(p) != "number")
			p = d * 0.3;

		var s;
		if (typeof(a) != "number" || a < Math.abs(c))
		{
			a = c;
			s = p / 4;
		}
		else
		{
			s = p / (2 * Math.PI) * Math.asin(c / a);
		}

		return a * Math.pow(2, -10 * t) *
			   Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
	}
	
	Elastic.easeInOut = function(t, b, c, d, a, p) {
		if (t == 0)
			return b;
			
		if ((t /= d / 2) == 2)
			return b + c;
			
		if (typeof(p) != "number")
			p = d * (0.3 * 1.5);

		var s;
		if (typeof(a) != "number" || a < Math.abs(c))
		{
			a = c;
			s = p / 4;
		}
		else
		{
			s = p / (2 * Math.PI) * Math.asin(c / a);
		}

		if (t < 1)
		{
			return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) *
				   Math.sin((t * d - s) * (2 * Math.PI) /p)) + b;
		}
		
		return a * Math.pow(2, -10 * (t -= 1)) *
			   Math.sin((t * d - s) * (2 * Math.PI) / p ) * 0.5 + c + b;
	}
	
	return Elastic;
})();
/**
 * Robert Penner easing equations
 * http://www.robertpenner.com/easing/
 */
(function(){
	
	var Class = benignware.core.Class;
	
	function Strong() {
	}
	
	Class.registerClass("benignware.transitions.easing.Strong", Strong);
	
	Strong.easeIn = function(t, b, c, d) {
		return c * (t /= d) * t * t * t * t + b;
	}
	
	Strong.easeOut = function(t, b, c, d) {
		return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
	}
	
	Strong.easeInOut = function(t, b, c, d) {
		if ((t /= d / 2) < 1)
			return c / 2 * t * t * t * t * t + b;

		return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
	}
	
	return Strong;
})();
/**
 * Transition Class
 * @class benignware.transitions.Transition
 */
(function(){
	// import classes
	var Class = benignware.core.Class;
	var Event = Class.require("benignware.events.Event");
	var Delegate = Class.require("benignware.utils.Delegate");
	var EventDispatcher = Class.require("benignware.events.EventDispatcher");
	var Tween = Class.require("benignware.transitions.Tween");
	//
	var Regular = Class.require("benignware.transitions.easing.Regular");
	// define super
	var __super;
	/**
	 * Constructs a new form-element.
	 * @constructor
	 */
	function Transition(element, duration, scene, easeFunction) {
		__super.call(this);
		this.time = 0;
		this.scene = scene ? scene : Transition.IN; 
		this.easeFunction = easeFunction ? easeFunction : Regular.easeOut;
		this.element = element;
		this.duration = typeof(duration) != "undefined" ? duration : 1;
		var tween = new Tween();
		tween.begin = 0;
		tween.finish = 1;
		tween.duration = this.duration;
		// events
		tween.addEventListener(Tween.MOTION_START, Delegate.create(this, function(event) {
			this.dispatchEvent(new Event(Transition.TRANSITION_START, false, false));
			this.dispatchEvent(new Event(Transition.MOTION_START, false, false));
		}), false);
		tween.addEventListener(Tween.MOTION_STOP, Delegate.create(this, function(event) {
			this.dispatchEvent(new Event(Transition.MOTION_STOP, false, false));
		}), false);
		tween.addEventListener(Tween.MOTION_CHANGE, Delegate.create(this, function(event) {
			if (this.getElement()) {
				this.update();
			}
			this.dispatchEvent(new Event(Transition.MOTION_CHANGE, false, false));
		}), false);
		tween.addEventListener(Tween.MOTION_FINISH, Delegate.create(this, function(event) {
			this.dispatchEvent(new Event(Transition.MOTION_FINISH, false, false));
			this.dispatchEvent(new Event(Transition.TRANSITION_END, false, false));
		}), false);
		
		this.__tween = tween;
	}
	
	Class.registerClass("benignware.transitions.Transition", Transition);

	Transition = Class(EventDispatcher, Transition);
	__super = Class.getSuper(Transition);
	
	// constants
	Transition.IN = 1;
	Transition.OUT = -1;
	
	Transition.FORWARD = 1;
	Transition.REVERSE = -1;
	
	// events
	Transition.MOTION_START = "motionStart";
	Transition.MOTION_CHANGE = "motionChange";
	Transition.MOTION_STOP = "motionStop";
	Transition.MOTION_FINISH = "motionFinish";
	
	Transition.TRANSITION_START = "transitionStart";
	Transition.TRANSITION_END = "transitionEnd";
	
	
	Transition.prototype.element = null;
	
	Transition.prototype.params = {};
	
	Transition.prototype.duration = 1;
	
	Transition.prototype.easeInFunction = null;
	Transition.prototype.easeOutFunction = null;
	
	Transition.prototype.scene = Transition.IN;
	Transition.prototype.direction = Transition.FORWARD;
	
	Transition.prototype.setElement = function(element) {
		this.element = element;
	}
	
	Transition.prototype.getElement = function() {
		return this.element;
	}
	
	Transition.prototype.start = function() {
		initTween.call(this);
		if (this.element) {
			this.updateTarget();
		}
		this.__tween.start();
	}
	
	function initTween() {
		this.__tween.duration = this.duration;
		this.__tween.direction = this.direction;
		var easeFunction = null;
		if (this.scene == Transition.IN) {
			easeFunction = this.easeInFunction ? this.easeInFunction : this.easeFunction;
		} else {
			easeFunction = this.easeOutFunction ? this.easeOutFunction : this.easeFunction;
		}
		this.__tween.easeFunction = easeFunction;
	}
	
	Transition.prototype.stop = function() {
		initTween.call(this);
		this.__tween.stop();
	}
	
	
	Transition.prototype.play = function() {
		initTween.call(this);
		this.__tween.play();
	}
	
	
	Transition.prototype.forward = function() {
		initTween.call(this);
		this.__tween.forward();
	}
	
	Transition.prototype.fforward = function() {
		initTween.call(this);
		this.__tween.fforward();
		this.update();
	}
	
	Transition.prototype.reverse = function() {
		initTween.call(this);
		this.__tween.reverse();
	}
	
	Transition.prototype.yoyo = function() {
		initTween.call(this);
		this.__tween.yoyo();
	}
	
	Transition.prototype.updateTarget = function() {
		this.update();
	}
	
	Transition.prototype.isPlaying = function() {
		return this.__tween.isPlaying();
	}
	
	Transition.prototype.update = function() {
		var p = this.__tween.getPosition();
//		console.log("p");
	}
	
	Transition.prototype.destroy = function() {
		__super.destroy.call(this);
	}

	return Transition;
})();
/**
 * Transition Class
 * @class benignware.Fades.Fade
 */
(function(){
	// import classes
	var Class = benignware.core.Class;
	var Event = Class.require("benignware.events.Event");
	var EventDispatcher = Class.require("benignware.events.EventDispatcher");
	var Transition = Class.require("benignware.transitions.Transition");
	// define super
	var __super;
	/**
	 * Constructs a new form-element.
	 * @constructor
	 */
	function Fade(element, duration, scene, easeFunction) {
		this.duration = 1;
		__super.apply(this, arguments);
	}
	
	Class.registerClass("benignware.transitions.Fade", Fade);

	Fade = Class(Transition, Fade);
	__super = Class.getSuper(Fade);

	Fade.prototype.update = function() {
		
		this.__tween.duration = this.duration;
		var p = this.__tween.getPosition();
		var value;
		switch(this.scene) {
			case Transition.OUT:
				value = 1 - p;
				break;
			default:
				value = p;
		}
		if (this.element) {
			console.log("ELEM UPDATE TRANS: ", p);
			this.element.style.opacity = value;
			this.element.style.filter = "alpha(opacity=" + (value * 100) + ")";
		}
	}

	return Fade;
})();
/**
 * Transition Class
 * @class benignware.Flys.Fly
 */
(function(){
	// import classes
	var Class = benignware.core.Class;
	var Delegate = Class.require("benignware.utils.Delegate");
	var Element = Class.require("benignware.core.Element");
	var Event = Class.require("benignware.events.Event");
	var EventDispatcher = Class.require("benignware.events.EventDispatcher");
	//
	var Transition = Class.require("benignware.transitions.Transition");
	var Fade = Class.require("benignware.transitions.Fade");
	var Back = Class.require("benignware.transitions.easing.Back");
	var Regular = Class.require("benignware.transitions.easing.Regular");
	// define super
	var __super;
	/**
	 * Constructs a new form-element.
	 * @constructor
	 */
	function Fly(element) {
		__super.apply(this, arguments);
		this.easeFunction = Regular.easeInOut;
		this.fade = false;
	}
	
	Class.registerClass("benignware.transitions.Fly", Fly);

	Fly = Class(Fade, Fly);
	
	__super = Class.getSuper(Fly);
	
	Fly.WINDOW = "window";
	
	Fly.prototype.easeFunction = null;
	Fly.prototype.container = null;
	Fly.prototype.__pps = 0;
	Fly.prototype.__maxDuration = 0;
	
	Fly.prototype.start = function() {
		__super.start.call(this);	
	}
	
	Fly.prototype.setPixelPerSecond = function(pps) {
		this.__pps = parseFloat(pps);
	}
	
	Fly.prototype.getPixelPerSecond = function() {
		return this.__pps;
	}
	
	Fly.prototype.setMaxDuration = function(duration) {
		this.__maxDuration = parseFloat(duration);
	}
	
	Fly.prototype.getMaxDuration = function() {
		return this.__maxDuration;
	}
	
	Fly.prototype.setContainer = function(container) {
		if (typeof(container) == "string") {
			if (container == Fly.WINDOW) {
				container = window;
			} else {
				container = document.getElementById(container);
			}
		}
		this.container = container;
	}
	
	Fly.prototype.getContainer = function() {
		return this.container;
	}
	
	Fly.prototype.updateTarget = function() {
		var element = this.getElement();
		if (element) {
			var pos;
			var w, h;
			
			switch (this.container) {
				case window:
					var winSize = Element.getWindowSize();
					w = winSize.width;
					h = winSize.height;
					pos = {x: element.offsetLeft, y: element.offsetTop}
					break;
				case null:
				case undefined:
					w = element.offsetParent ? element.offsetParent.offsetWidth : 0;
					h = element.offsetParent ? element.offsetParent.offsetHeight : 0;
					pos = {x: element.offsetLeft, y: element.offsetTop}
					break;
				default:
//					pos = Element.getPosition(this.container, element.offsetParent);
					pos = {x: element.offsetLeft, y: element.offsetTop}
					w = this.container.offsetWidth;
					h = this.container.offsetHeight;
			}
			
			this.__destRelCenterX = (pos.x + element.offsetWidth / 2) / w;
			this.__destRelCenterY = (pos.y + element.offsetHeight / 2) / h;
			this.update();
		}
	}

	Fly.prototype.update = function() {
		var element = this.getElement();

		if (!element) {
			return;
		}

		element.style.position = "absolute";	
		
		
		var w, h, destX, destY;
		var pos;
		
		switch (this.container) {
			case window: 
				var winSize = Element.getWindowSize();
				pos = Element.getPosition(element.ownerDocument.body, element.offsetParent);
				w = winSize.width;
				h = winSize.height;
				break;
			case null:
				pos = {x: 0, y: 0}
				w = element.offsetParent ? element.offsetParent.offsetWidth : 0;
				h = element.offsetParent ? element.offsetParent.offsetHeight : 0;
				break;
			default:
				pos = Element.getPosition(this.container, element.offsetParent);
				w = this.container.offsetWidth;
				h = this.container.offsetHeight;
		}

		//
		destX = Math.floor((this.__destRelCenterX * w) - element.offsetWidth / 2);
		destY = Math.floor((this.__destRelCenterY * h) - element.offsetHeight / 2);

		var x;
		
		var d = this.duration
		if (this.__pps) {
			d = this.duration * w / this.__pps;
		}
		if (this.__maxDuration) {
			d = d > this.__maxDuration ? this.__maxDuration : d;
		}
		
		this.__tween.duration = d;
		var p = this.__tween.getPosition();
		
		switch (this.scene) {
			case Transition.OUT:
				x = destX + (w - destX + pos.x) * p;
				break;
			default: 
				var x1 = pos.x - element.offsetWidth;
				x = x1 + (destX - x1) * p;
				break;
		}
		
		element.style.left = x + "px";
		
		if (this.fade) {
			__super.update.call(this);
		}
	}

	return Fly;
})();
/**
 * Transition Class
 * @class benignware.transition.Fade
 */
(function(){
	// import classes
	var Class = benignware.core.Class;
	var Element = Class.require("benignware.core.Element");
	var Event = Class.require("benignware.events.Event");
	var EventDispatcher = Class.require("benignware.events.EventDispatcher");
	var Transition = Class.require("benignware.transitions.Transition");
	// define super
	var __super;
	/**
	 * Constructs a new form-element.
	 * @constructor
	 */
	function Zoom(element, duration, scene, easeFunction) {
		__super.apply(this, arguments);
	}
	
	Zoom.prototype.__minScale = 1;
	Zoom.prototype.__maxScale = 2;
	Zoom.prototype.__originX = 0.5;
	Zoom.prototype.__originY = 0.5;
	
	Class.registerClass("benignware.transitions.Zoom", Zoom);

	Zoom = Class.extend(Transition, Zoom);
	
	__super = Class.getSuper(Zoom);

	Zoom.prototype.setMinScale = function(minScale) {
		this.__minScale = minScale;
	}
	
	Zoom.prototype.getMinScale = function() {
		return this.__minScale;
	}
	
	Zoom.prototype.setMaxScale = function(maxScale) {
		this.__maxScale = maxScale;
	}
	
	Zoom.prototype.getMaxScale = function() {
		return this.__maxScale;
	}
	
	Zoom.prototype.setOriginX = function(originX) {
		this.__originX = originX;
	}
	
	Zoom.prototype.getOriginX = function() {
		return this.__originX;
	}
	
	Zoom.prototype.setOriginY = function(originY) {
		this.__originY = originY;
	}
	
	Zoom.prototype.getOriginY = function() {
		return this.__originY;
	}
	
	Zoom.prototype.update = function() {
		var elem = this.getElement()
		this.__tween.duration = this.duration;
		var p = this.__tween.getPosition();
		switch(this.scene) {
			case Transition.OUT:
				p = 1 - p;
				break;
		}
		if (elem) {
			
			var originX = this.getOriginX();
			var originY = this.getOriginY();
			var minScale = this.getMinScale();
			var maxScale = this.getMaxScale();
			var scale = minScale + (maxScale - minScale) * p;
			
			// w3c
			elem.style.transform = "scale(" + scale + ")";
			elem.style.transformOrigin = originX * 100 + "% " + originY * 100 + "%";
			
			// moz
			elem.style.MozTransform = "scale(" + scale + ")";
			elem.style.MozTransformOrigin = originX * 100 + "% " + originY * 100 + "%";
			
			
			// webkit
			elem.style.WebkitTransform = "scale(" + scale + ")";
			elem.style.WebkitTransformOrigin = originX * 100 + "% " + originY * 100 + "%";
			
			// opera
			elem.style.OTransform = "scale(" + scale + ")";
			elem.style.OTransformOrigin = originX * 100 + "% " + originY * 100 + "%";
			
			// ie
			
			if (false/*@cc_on || @_jscript_version <= 9 @*/) {
				
				// ie zoom does not work on inline elements
				var display = Element.getComputedStyle(elem, 'display');
				if (display == "inline") {
					elem.style.display = "inline-block";
				}
				// zoom
				elem.style.marginTop = "0px";
				elem.style.marginBottom = "0px";
				
				var ew = elem.offsetWidth;
				var eh = elem.offsetHeight;
				elem.style.zoom = scale;

				var x = -ew * originX * p;
				var y = -eh * originY * p;
				//
				var mr = - (ew * originX * (1 - scale));
				var ml = - (ew * (1 - originX) * (1 - scale));
				
				var mt = (eh * originY * (1 - scale));
				var mb = (eh * (1 - originY) * (1 - scale));
				//
				elem.style.marginTop = mt + "px";
				elem.style.marginBottom = mb + "px";
				elem.style.marginLeft = ml + "px";
				elem.style.marginRight = mr + "px";
				//
			}
		}
	}

	return Zoom;
})();

